<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > glibc中的printf如何輸出到串口

          glibc中的printf如何輸出到串口

          作者: 時(shí)間:2016-11-22 來(lái)源:網(wǎng)絡(luò) 收藏
          內(nèi)核版本:2.6.14

          glibc版本:2.3.6

          本文引用地址:http://www.ex-cimer.com/article/201611/319993.htm

          CPU平臺(tái):arm

          printf輸出不一定是串口,也可以是LCD,甚至是文件等,這里僅以輸出到串口為例。本文分析了printf和文件描述符0、1和2以及stdout、stdin和stderr的關(guān)系,通過(guò)這篇文章可以知道文件描述符0、1和2為什么對(duì)應(yīng)著stdout、stdin和stderr,因?yàn)間libc就是這么定義的!?。?/p>

          首先看glibc中printf函數(shù)的定義(glibc-2.3.6/stdio-common/printf.c):

          [plain]view plaincopy
          print?
          1. #undefprintf
          2. /*WriteformattedoutputtostdoutfromtheformatstringFORMAT.*/
          3. /*VARARGS1*/
          4. int
          5. printf(constchar*format,...)
          6. {
          7. va_listarg;
          8. intdone;
          9. va_start(arg,format);
          10. done=vfprintf(stdout,format,arg);//主要是這個(gè)函數(shù)
          11. va_end(arg);
          12. returndone;
          13. }
          14. #undef_IO_printf
          15. /*Thisisforlibg++.*/
          16. strong_alias(printf,_IO_printf);

          strong_alias,即取別名。網(wǎng)上有人提及這個(gè)strong alias好像是為了防止c庫(kù)符號(hào)被其他庫(kù)符號(hào)覆蓋掉而使用的,如果printf被覆蓋了,還有_IO_printf可以用。跟蹤vfprintf函數(shù)(),我們先給出該函數(shù)的聲明,如下(glibc-2.3.6/libio/stdio.h):

          [plain]view plaincopy
          print?
          1. externintvfprintf(FILE*__restrict__s,__constchar*__restrict__format,
          2. _G_va_list__arg);
          printf函數(shù)是通過(guò)vfprintf將format輸出到stdout文件中,stdout是(FILE *)類型。stdout的定義如下(glibc-2.3.6/libio/stdio.h),順被也給出stdin和stderr的定義:

          [plain]view plaincopy
          print?
          1. /*Standardstreams.*/
          2. externstruct_IO_FILE*stdin;/*Standardinputstream.*/
          3. externstruct_IO_FILE*stdout;/*Standardoutputstream.*/
          4. externstruct_IO_FILE*stderr;/*Standarderroroutputstream.*/
          5. /*C89/C99saytheyremacros.Makethemhappy.*/
          6. #definestdinstdin
          7. #definestdoutstdout
          8. #definestderrstderr
          繼續(xù)跟蹤stdout(glibc-2.3.6/libio/stdio.c):

          [plain]view plaincopy
          print?
          1. _IO_FILE*stdin=(FILE*)&_IO_2_1_stdin_;
          2. _IO_FILE*stdout=(FILE*)&_IO_2_1_stdout_;
          3. _IO_FILE*stderr=(FILE*)&_IO_2_1_stderr_;
          在繼續(xù)分析_IO_2_1_stdout_之前,我們先看一下_IO_FILE(FILE和_IO_FILE是一回事,#define FILE _IO_FILE)的定義(glibc-2.3.6/libio/libio.h):

          [plain]view plaincopy
          print?
          1. struct_IO_FILE{
          2. int_flags;/*High-orderwordis_IO_MAGIC;restisflags.*/
          3. #define_IO_file_flags_flags
          4. /*ThefollowingpointerscorrespondtotheC++streambufprotocol.*/
          5. /*Note:Tkusesthe_IO_read_ptrand_IO_read_endfieldsdirectly.*/
          6. char*_IO_read_ptr;/*Currentreadpointer*/
          7. char*_IO_read_end;/*Endofgetarea.*/
          8. char*_IO_read_base;/*Startofputback+getarea.*/
          9. char*_IO_write_base;/*Startofputarea.*/
          10. char*_IO_write_ptr;/*Currentputpointer.*/
          11. char*_IO_write_end;/*Endofputarea.*/
          12. char*_IO_buf_base;/*Startofreservearea.*/
          13. char*_IO_buf_end;/*Endofreservearea.*/
          14. /*Thefollowingfieldsareusedtosupportbackingupandundo.*/
          15. char*_IO_save_base;/*Pointertostartofnon-currentgetarea.*/
          16. char*_IO_backup_base;/*Pointertofirstvalidcharacterofbackuparea*/
          17. char*_IO_save_end;/*Pointertoendofnon-currentgetarea.*/
          18. struct_IO_marker*_markers;
          19. struct_IO_FILE*_chain;
          20. int_fileno;//這個(gè)就是linux內(nèi)核中文件描述符fd
          21. #if0
          22. int_blksize;
          23. #else
          24. int_flags2;
          25. #endif
          26. _IO_off_t_old_offset;/*Thisusedtobe_offsetbutitstoosmall.*/
          27. #define__HAVE_COLUMN/*temporary*/
          28. /*1+columnnumberofpbase();0isunknown.*/
          29. unsignedshort_cur_column;
          30. signedchar_vtable_offset;
          31. char_shortbuf[1];
          32. /*char*_save_gptr;char*_save_egptr;*/
          33. _IO_lock_t*_lock;
          34. #ifdef_IO_USE_OLD_IO_FILE
          35. };
          36. struct_IO_FILE_plus
          37. {
          38. _IO_FILEfile;
          39. conststruct_IO_jump_t*vtable;//IO函數(shù)跳轉(zhuǎn)表
          40. };

          下面我們看看_IO_2_1_stdout_的定義(glibc-2.3.6/libio/stdfiles.c),順便給出_IO_2_1_stdin_和_IO_2_1_stderr_的定義:

          [plain]view plaincopy
          print?
          1. #defineDEF_STDFILE(NAME,FD,CHAIN,FLAGS)
          2. struct_IO_FILE_plusNAME
          3. ={FILEBUF_LITERAL(CHAIN,FLAGS,FD,NULL),
          4. &_IO_file_jumps};
          5. DEF_STDFILE(_IO_2_1_stdin_,0,0,_IO_NO_WRITES);
          6. DEF_STDFILE(_IO_2_1_stdout_,1,&_IO_2_1_stdin_,_IO_NO_READS);
          7. DEF_STDFILE(_IO_2_1_stderr_,2,&_IO_2_1_stdout_,_IO_NO_READS+_IO_UNBUFFERED);

          從這里我們可以看到,_IO_2_1_stdout_的FD = 0、_IO_2_1_stdin_的FD = 1、_IO_2_1_stderr_的FD = 2。FILEBUF_LITERAL用于初始化_IO_FILE,定義如下(glibc-2.3.6/libio/libioP.h):

          [plain]view plaincopy
          print?
          1. #defineFILEBUF_LITERAL(CHAIN,FLAGS,FD,WDP)
          2. {_IO_MAGIC+_IO_LINKED+_IO_IS_FILEBUF+FLAGS,
          3. 0,0,0,0,0,0,0,0,0,0,0,0,(_IO_FILE*)CHAIN,FD,
          4. 0,_IO_pos_BAD,0,0,{0},0,_IO_pos_BAD,
          5. 0}
          其中,F(xiàn)D賦值給了_fileno。我們回到vfprintf的分析,vfprintf的具體實(shí)現(xiàn)本文就不詳細(xì)講解,主要原理是格式化字符串,最后將字符串輸出到文件中,也就是stdout中。至于如何輸出,則和_IO_file_jumps關(guān)系密切,_IO_file_jumps的定義如下:

          [plain]view plaincopy
          print?
          1. conststruct_IO_jump_t_IO_file_jumps=
          2. {
          3. JUMP_INIT_DUMMY,
          4. JUMP_INIT(finish,INTUSE(_IO_file_finish)),
          5. JUMP_INIT(overflow,INTUSE(_IO_file_overflow)),
          6. JUMP_INIT(underflow,INTUSE(_IO_file_underflow)),
          7. JUMP_INIT(uflow,INTUSE(_IO_default_uflow)),
          8. JUMP_INIT(pbackfail,INTUSE(_IO_default_pbackfail)),
          9. JUMP_INIT(xsputn,INTUSE(_IO_file_xsputn)),
          10. JUMP_INIT(xsgetn,INTUSE(_IO_file_xsgetn)),
          11. JUMP_INIT(seekoff,_IO_new_file_seekoff),
          12. JUMP_INIT(seekpos,_IO_default_seekpos),
          13. JUMP_INIT(setbuf,_IO_new_file_setbuf),
          14. JUMP_INIT(sync,_IO_new_file_sync),
          15. JUMP_INIT(doallocate,INTUSE(_IO_file_doallocate)),
          16. JUMP_INIT(read,INTUSE(_IO_file_read)),
          17. JUMP_INIT(write,_IO_new_file_write),
          18. JUMP_INIT(seek,INTUSE(_IO_file_seek)),
          19. JUMP_INIT(close,INTUSE(_IO_file_close)),
          20. JUMP_INIT(stat,INTUSE(_IO_file_stat)),
          21. JUMP_INIT(showmanyc,_IO_default_showmanyc),
          22. JUMP_INIT(imbue,_IO_default_imbue)
          23. };
          至于怎么跳轉(zhuǎn)到這些函數(shù),以及如何跳轉(zhuǎn)到linux內(nèi)核,由于涉及到glibc的一些細(xì)節(jié),這里簡(jiǎn)單介紹一下進(jìn)入內(nèi)核后的情況:進(jìn)入linux內(nèi)核后,調(diào)用write(),在write之前所有的代碼都是C庫(kù)的代碼,可以說(shuō)是和平臺(tái)無(wú)關(guān)的。而涉及到具體輸出,就要調(diào)用操作系統(tǒng)提供給的接口。調(diào)用write()后,通過(guò)系統(tǒng)調(diào)用進(jìn)入內(nèi)核空間,首先是sys_write(),這個(gè)函數(shù)代碼位于fs/read_write.c中。一進(jìn)入sys_write(),就要根據(jù)傳進(jìn)來(lái)的fd描述符找到相應(yīng)的file結(jié)構(gòu)。對(duì)于標(biāo)準(zhǔn)輸出,fd= 1,每個(gè)進(jìn)程的進(jìn)程控制塊都有一個(gè)打開文件的數(shù)組files。file結(jié)構(gòu)就是根據(jù)fd在這個(gè)數(shù)組中查找到相應(yīng)的結(jié)構(gòu)。找到結(jié)構(gòu)后,就會(huì)調(diào)用file->write()來(lái)向外輸出。具體輸出到哪里,就要看file結(jié)構(gòu)對(duì)應(yīng)的設(shè)備驅(qū)動(dòng)是什么。

          通過(guò)本文可以理解:文件描述符0、1和2和stdout、stdin和stderr對(duì)應(yīng),如果要修改linux內(nèi)核中文件描述符相關(guān)代碼,一定要注意文件描述符0、1和2的分配和回收,否則會(huì)導(dǎo)致終端沒(méi)有輸出信息,也無(wú)法和內(nèi)核輸入信息。



          關(guān)鍵詞: glibcprintf輸出串

          評(píng)論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();