新聞中心

        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.104case.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就是這么定義的?。?!

        首先看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è)打開(kāi)文件的數(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)閉
        主站蜘蛛池模板: 郴州市| 双江| 洪洞县| 庆城县| 盐津县| 崇州市| 兖州市| 岑溪市| 朝阳区| 柘城县| 泾源县| 历史| 将乐县| 从化市| 雅安市| 万州区| 万山特区| 乌苏市| 盈江县| 临西县| 化隆| 榕江县| 莱西市| 丹阳市| 凤山市| 安岳县| 武威市| 汝城县| 喀喇沁旗| 镇康县| 泗阳县| 木里| 铜鼓县| 砚山县| 清河县| 双鸭山市| 措美县| 岱山县| 平南县| 平罗县| 山西省|