2010-07-22 13 views
6

मैं उबंटू के तहत नस्ल का उपयोग कर रहा हूं। वैसे मुझे उपयोगकर्ता के कीबोर्ड से एकल इनपुट कैरेक्टर प्राप्त करने की आवश्यकता है (जैसे कि जब कोई प्रोग्राम आपको y/n के लिए पूछता है?) ताकि कुंजी दबाए और एंटर दबाए बिना मुझे दर्ज किए गए चरित्र को पढ़ने की आवश्यकता हो। मैंने इसे बहुत गुमराह किया लेकिन जो कुछ मैंने पाया वह किसी भी तरह से इस लाइन से संबंधित था (int 21h) जिसके परिणामस्वरूप "सेगमेंटेशन फॉल्ट" होता है। कृपया मुझे यह समझने में सहायता करें कि एकल चरित्र कैसे प्राप्त करें या इस सेगमेंटेशन गलती को कैसे खत्म किया जाए।उबंटू के तहत नासम (असेंबली) का उपयोग करके कीबोर्ड से एकल वर्ण इनपुट कैसे पढ़ूं?

उत्तर

11

यह असेंबली से किया जा सकता है, लेकिन यह आसान नहीं है। आप int 21h का उपयोग नहीं कर सकते हैं, यह एक डॉस सिस्टम कॉल है और यह लिनक्स के तहत उपलब्ध नहीं है।

यूनिक्स जैसे ऑपरेटिंग सिस्टम (जैसे लिनक्स) के तहत टर्मिनल से पात्र प्राप्त करने के लिए, आप STDIN (फ़ाइल संख्या 0) से पढ़ते हैं। आम तौर पर, जब तक उपयोगकर्ता प्रेस दर्ज नहीं करता है तब तक पढ़ा गया सिस्टम कॉल अवरुद्ध हो जाएगा। इसे कैनोलिक मोड कहा जाता है। उपयोगकर्ता को एंटर दबाए रखने के इंतजार किए बिना एक सिंगल कैरेक्टर को पढ़ने के लिए, आपको पहले कैननिकल मोड को अक्षम करना होगा। बेशक, यदि आप बाद में लाइन इनपुट चाहते हैं, और आपके प्रोग्राम से बाहर निकलने से पहले आपको इसे फिर से सक्षम करना होगा।

लिनक्स पर प्रामाणिक मोड अक्षम करने के लिए, आप STDIN को एक IOCTL (आईओ नियंत्रण) भेजने के लिए, ioctl syscall का उपयोग कर। मुझे लगता है कि आप जानते हैं कि कैसे लिनक्स सिस्टम को असेंबलर से कॉल करना है।

ioctl syscall में तीन पैरामीटर हैं। पहली बार आदेश (एसटीडीआईएन) को भेजने के लिए फ़ाइल है, दूसरा आईओसीटीएल नंबर है, और तीसरा आम तौर पर डेटा संरचना के लिए एक सूचक है। सफलता पर ioctl 0, या असफल पर एक नकारात्मक त्रुटि कोड देता है।

पहले IOCTL आप की जरूरत TCGETS (संख्या 0x5401) जो एक termios संरचना में वर्तमान टर्मिनल पैरामीटर हो जाता है। तीसरा पैरामीटर टर्मियो संरचना के लिए एक सूचक है। कर्नेल स्रोत से, termios संरचना के रूप में परिभाषित किया गया है:

struct termios { 
    tcflag_t c_iflag;    /* input mode flags */ 
    tcflag_t c_oflag;    /* output mode flags */ 
    tcflag_t c_cflag;    /* control mode flags */ 
    tcflag_t c_lflag;    /* local mode flags */ 
    cc_t c_line;     /* line discipline */ 
    cc_t c_cc[NCCS];    /* control characters */ 
}; 

जहां tcflag_t 32 बिट्स है, cc_t एक बाइट लंबा है, और एनसीसीएस वर्तमान में देखें तो आप आसानी से कैसे परिभाषित कर सकते हैं के लिए एनएएसएम मैनुअल 19. के रूप में परिभाषित किया गया है और इस तरह की संरचनाओं के लिए आरक्षित जगह।

तो एक बार जब आपको वर्तमान टर्मियो मिल जाए, तो आपको कैनोलिक ध्वज को साफ़ करने की आवश्यकता है। यह ध्वज c_lflag फ़ील्ड में है, मास्क ICANON (0x00000002) के साथ। इसे साफ़ करने के लिए, c_lflag गणना करें (और ICANON नहीं)। और परिणाम को वापस c_lflag फ़ील्ड में संग्रहीत करें।

अब आपको टर्मियो संरचना में अपने परिवर्तनों के कर्नेल को सूचित करने की आवश्यकता है। टीसीएसईटीएस (संख्या 0x5402) ioctl का उपयोग करें, तीसरे पैरामीटर के साथ आपके टर्मियोस संरचना का पता सेट करें।

यदि सब ठीक हो जाए, तो टर्मिनल अब गैर-कैनोलिक मोड में है। आप कैनोलिक फ्लैग को सेट करके कैननिकल मोड को पुनर्स्थापित कर सकते हैं (आईसीएएनओएन के साथ ऑरिंग c_lflag द्वारा) और टीसीएसईटीएस ioctl को दोबारा कॉल कर सकते हैं।

से बाहर निकलने से पहले हमेशा कैननिकल मोड को पुनर्स्थापित करें जैसा कि मैंने कहा, यह आसान नहीं है।

0

आसान तरीका: एक पाठ-मोड कार्यक्रम के लिए, libncurses कीबोर्ड का उपयोग करने के लिए; एक ग्राफिकल प्रोग्राम के लिए, Gtk+ का उपयोग करें।

कठिन तरीका: टेक्स्ट-मोड प्रोग्राम मानते हुए, आपको कर्नेल को यह बताना होगा कि आप सिंगल-कैरेक्टर इनपुट चाहते हैं, और फिर आपको पूरी तरह से बहीखाता और डिकोडिंग करना होगा। यह वास्तव में जटिल है। अच्छे पुराने डॉस getch() दिनचर्या के बराबर नहीं है। आप प्रारंभ कर सकते हैं कि यह कैसे करें: Terminal I/O। ग्राफिकल कार्यक्रम और भी जटिल हैं; इसके लिए निम्नतम स्तर API Xlib है।

किसी भी तरह से, आप जो कुछ भी असेंबली में पागल कोडिंग करने जा रहे हैं; इसके बजाय सी का उपयोग करें।

+1

जबकि आपने जो भी कहा है, वह सी के लिए सही है, यदि ओपी असेंबली सीखने की कोशिश कर रहा है तो यह वास्तव में एक प्रासंगिक उत्तर नहीं है। –

+1

ऐसा इसलिए है क्योंकि ओपी * असेंबली भाषा * में प्रोग्रामिंग नहीं होनी चाहिए। असेंबली भाषा में कुछ भी हाथ कोड करने का एकमात्र अच्छा कारण यह है कि यह एक प्रदर्शन-महत्वपूर्ण कम्प्यूटेशनल सबराउटिन है, या एक ऑपरेटिंग सिस्टम कर्नेल के बहुत कम निम्न स्तर के टुकड़ों में से एक है जिसे * किसी भी अन्य तरीके से कोड नहीं किया जा सकता है। उपयोगकर्ता बातचीत योग्य नहीं है। ओपी क्या कर रहा है वह यूनिक्स के तहत भी एक अच्छा * सीखने का अभ्यास नहीं है। – zwol

+0

ऐसा कहकर, ओपी को असेंबली भाषा लिखने से रोकना नहीं है जो libncurses को कॉल करता है, हालांकि यह मुझे समय और गहनता की गड़बड़ी की तरह लगता है (लेकिन यह यूनिक्स टर्मिनल I की असेंबली भाषा के रूप में उतना ही बुरा नहीं होगा।/हाथ से)। – zwol

5

मैं इस हाल ही में करने के लिए की जरूरत है, और कैलम के excellent answer से प्रेरित, मैं निम्नलिखित लिखा है:

termios:  times 36 db 0 
stdin:   equ 0 
ICANON:   equ 1<<1 
ECHO:   equ 1<<3 

canonical_off: 
     call read_stdin_termios 

     ; clear canonical bit in local mode flags 
     push rax 
     mov eax, ICANON 
     not eax 
     and [termios+12], eax 
     pop rax 

     call write_stdin_termios 
     ret 

echo_off: 
     call read_stdin_termios 

     ; clear echo bit in local mode flags 
     push rax 
     mov eax, ECHO 
     not eax 
     and [termios+12], eax 
     pop rax 

     call write_stdin_termios 
     ret 

canonical_on: 
     call read_stdin_termios 

     ; set canonical bit in local mode flags 
     or dword [termios+12], ICANON 

     call write_stdin_termios 
     ret 

echo_on: 
     call read_stdin_termios 

     ; set echo bit in local mode flags 
     or dword [termios+12], ECHO 

     call write_stdin_termios 
     ret 

read_stdin_termios: 
     push rax 
     push rbx 
     push rcx 
     push rdx 

     mov eax, 36h 
     mov ebx, stdin 
     mov ecx, 5401h 
     mov edx, termios 
     int 80h 

     pop rdx 
     pop rcx 
     pop rbx 
     pop rax 
     ret 

write_stdin_termios: 
     push rax 
     push rbx 
     push rcx 
     push rdx 

     mov eax, 36h 
     mov ebx, stdin 
     mov ecx, 5402h 
     mov edx, termios 
     int 80h 

     pop rdx 
     pop rcx 
     pop rbx 
     pop rax 
     ret 

फिर आप कर सकते हैं:

call canonical_off 

आप टेक्स्ट की पंक्ति में पढ़ रहे हैं , आप शायद यह भी करना चाहते हैं:

call echo_off 

ताकि प्रत्येक चरित्र टाइप किए जाने के रूप में प्रतिबिंबित न हो।

ऐसा करने के बेहतर तरीके हो सकते हैं, लेकिन यह 64-बिट फेडोरा स्थापना पर मेरे लिए काम करता है।

अधिक जानकारी termios(3), या termbits.h source में मैन पेज में मिल सकती है।

संबंधित मुद्दे