glibc中的printf如何輸出到串口
glibc版本:2.3.6
本文引用地址:http://m.butianyuan.cn/article/201611/319993.htmCPU平臺(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):
- #undefprintf
- /*WriteformattedoutputtostdoutfromtheformatstringFORMAT.*/
- /*VARARGS1*/
- int
- printf(constchar*format,...)
- {
- va_listarg;
- intdone;
- va_start(arg,format);
- done=vfprintf(stdout,format,arg);//主要是這個(gè)函數(shù)
- va_end(arg);
- returndone;
- }
- #undef_IO_printf
- /*Thisisforlibg++.*/
- 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):
- externintvfprintf(FILE*__restrict__s,__constchar*__restrict__format,
_G_va_list__arg);
- /*Standardstreams.*/
- externstruct_IO_FILE*stdin;/*Standardinputstream.*/
- externstruct_IO_FILE*stdout;/*Standardoutputstream.*/
- externstruct_IO_FILE*stderr;/*Standarderroroutputstream.*/
- /*C89/C99saytheyremacros.Makethemhappy.*/
- #definestdinstdin
- #definestdoutstdout
- #definestderrstderr
- _IO_FILE*stdin=(FILE*)&_IO_2_1_stdin_;
- _IO_FILE*stdout=(FILE*)&_IO_2_1_stdout_;
- _IO_FILE*stderr=(FILE*)&_IO_2_1_stderr_;
- struct_IO_FILE{
- int_flags;/*High-orderwordis_IO_MAGIC;restisflags.*/
- #define_IO_file_flags_flags
- /*ThefollowingpointerscorrespondtotheC++streambufprotocol.*/
- /*Note:Tkusesthe_IO_read_ptrand_IO_read_endfieldsdirectly.*/
- char*_IO_read_ptr;/*Currentreadpointer*/
- char*_IO_read_end;/*Endofgetarea.*/
- char*_IO_read_base;/*Startofputback+getarea.*/
- char*_IO_write_base;/*Startofputarea.*/
- char*_IO_write_ptr;/*Currentputpointer.*/
- char*_IO_write_end;/*Endofputarea.*/
- char*_IO_buf_base;/*Startofreservearea.*/
- char*_IO_buf_end;/*Endofreservearea.*/
- /*Thefollowingfieldsareusedtosupportbackingupandundo.*/
- char*_IO_save_base;/*Pointertostartofnon-currentgetarea.*/
- char*_IO_backup_base;/*Pointertofirstvalidcharacterofbackuparea*/
- char*_IO_save_end;/*Pointertoendofnon-currentgetarea.*/
- struct_IO_marker*_markers;
- struct_IO_FILE*_chain;
- int_fileno;//這個(gè)就是linux內(nèi)核中文件描述符fd
- #if0
- int_blksize;
- #else
- int_flags2;
- #endif
- _IO_off_t_old_offset;/*Thisusedtobe_offsetbutitstoosmall.*/
- #define__HAVE_COLUMN/*temporary*/
- /*1+columnnumberofpbase();0isunknown.*/
- unsignedshort_cur_column;
- signedchar_vtable_offset;
- char_shortbuf[1];
- /*char*_save_gptr;char*_save_egptr;*/
- _IO_lock_t*_lock;
- #ifdef_IO_USE_OLD_IO_FILE
- };
- struct_IO_FILE_plus
- {
- _IO_FILEfile;
- conststruct_IO_jump_t*vtable;//IO函數(shù)跳轉(zhuǎn)表
- };
下面我們看看_IO_2_1_stdout_的定義(glibc-2.3.6/libio/stdfiles.c),順便給出_IO_2_1_stdin_和_IO_2_1_stderr_的定義:
- #defineDEF_STDFILE(NAME,FD,CHAIN,FLAGS)
- struct_IO_FILE_plusNAME
- ={FILEBUF_LITERAL(CHAIN,FLAGS,FD,NULL),
- &_IO_file_jumps};
- DEF_STDFILE(_IO_2_1_stdin_,0,0,_IO_NO_WRITES);
- DEF_STDFILE(_IO_2_1_stdout_,1,&_IO_2_1_stdin_,_IO_NO_READS);
- 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):
- #defineFILEBUF_LITERAL(CHAIN,FLAGS,FD,WDP)
- {_IO_MAGIC+_IO_LINKED+_IO_IS_FILEBUF+FLAGS,
- 0,0,0,0,0,0,0,0,0,0,0,0,(_IO_FILE*)CHAIN,FD,
- 0,_IO_pos_BAD,0,0,{0},0,_IO_pos_BAD,
- 0}
- conststruct_IO_jump_t_IO_file_jumps=
- {
- JUMP_INIT_DUMMY,
- JUMP_INIT(finish,INTUSE(_IO_file_finish)),
- JUMP_INIT(overflow,INTUSE(_IO_file_overflow)),
- JUMP_INIT(underflow,INTUSE(_IO_file_underflow)),
- JUMP_INIT(uflow,INTUSE(_IO_default_uflow)),
- JUMP_INIT(pbackfail,INTUSE(_IO_default_pbackfail)),
- JUMP_INIT(xsputn,INTUSE(_IO_file_xsputn)),
- JUMP_INIT(xsgetn,INTUSE(_IO_file_xsgetn)),
- JUMP_INIT(seekoff,_IO_new_file_seekoff),
- JUMP_INIT(seekpos,_IO_default_seekpos),
- JUMP_INIT(setbuf,_IO_new_file_setbuf),
- JUMP_INIT(sync,_IO_new_file_sync),
- JUMP_INIT(doallocate,INTUSE(_IO_file_doallocate)),
- JUMP_INIT(read,INTUSE(_IO_file_read)),
- JUMP_INIT(write,_IO_new_file_write),
- JUMP_INIT(seek,INTUSE(_IO_file_seek)),
- JUMP_INIT(close,INTUSE(_IO_file_close)),
- JUMP_INIT(stat,INTUSE(_IO_file_stat)),
- JUMP_INIT(showmanyc,_IO_default_showmanyc),
- JUMP_INIT(imbue,_IO_default_imbue)
- };
通過(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)核輸入信息。
評(píng)論