Linux內核版本:2.6.14
glibc版本:2.3.6
CPU平台:arm
printf的輸出不一定是串口,也可以是LCD,甚至是文件等,這裡僅以輸出到串口為例。本文分析了printf和文件描述符0、1和2以及stdout、stdin和stderr的關系,通過這篇文章可以知道文件描述符0、1和2為什麼對應著stdout、stdin和stderr,因為glibc就是這麼定義的!!!
首先看glibc中printf函數的定義(glibc-2.3.6/stdio-common/printf.c):
#undef printf
/* Write formatted output to stdout from the format string FORMAT. */
/* VARARGS1 */
int
printf (const char *format, ...)
{
va_list arg;
int done;
va_start (arg, format);
done = vfprintf (stdout, format, arg);//主要是這個函數
va_end (arg);
return done;
}
#undef _IO_printf
/* This is for libg++. */
strong_alias (printf, _IO_printf);
strong_alias,即取別名。網上有人提及這個strong alias好像是為了防止c庫符號被其他庫符號覆蓋掉而使用的,如果printf被覆蓋了,還有_IO_printf可以用。跟蹤vfprintf函數(),我們先給出該函數的聲明,如下(glibc-2.3.6/libio/stdio.h):
extern int vfprintf (FILE *__restrict __s, __const char *__restrict __format,
_G_va_list __arg);
printf函數是通過vfprintf將format輸出到stdout文件中,stdout是(FILE *)類型。stdout的定義如下(glibc-2.3.6/libio/stdio.h),順被也給出stdin和stderr的定義:
/* Standard streams. */
extern struct _IO_FILE *stdin; /* Standard input stream. */
extern struct _IO_FILE *stdout; /* Standard output stream. */
extern struct _IO_FILE *stderr; /* Standard error output stream. */
/* C89/C99 say they're macros. Make them happy. */
#define stdin stdin
#define stdout stdout
#define stderr stderr
繼續跟蹤stdout(glibc-2.3.6/libio/stdio.c):
_IO_FILE *stdin = (FILE *) &_IO_2_1_stdin_;
_IO_FILE *stdout = (FILE *) &_IO_2_1_stdout_;
_IO_FILE *stderr = (FILE *) &_IO_2_1_stderr_;
在繼續分析_IO_2_1_stdout_之前,我們先看一下_IO_FILE(FILE和_IO_FILE是一回事,#define FILE _IO_FILE)的定義(glibc-2.3.6/libio/libio.h):
struct _IO_FILE {
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
/* The following pointers correspond to the C++ streambuf protocol. */
/* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
char* _IO_read_ptr; /* Current read pointer */
char* _IO_read_end; /* End of get area. */
char* _IO_read_base; /* Start of putback+get area. */
char* _IO_write_base; /* Start of put area. */
char* _IO_write_ptr; /* Current put pointer. */
char* _IO_write_end; /* End of put area. */
char* _IO_buf_base; /* Start of reserve area. */
char* _IO_buf_end; /* End of reserve area. */
/* The following fields are used to support backing up and undo. */
char *_IO_save_base; /* Pointer to start of non-current get area. */
char *_IO_backup_base; /* Pointer to first valid character of backup area */
char *_IO_save_end; /* Pointer to end of non-current get area. */
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno;//這個就是linux內核中文件描述符fd
#if 0
int _blksize;
#else
int _flags2;
#endif
_IO_off_t _old_offset; /* This used to be _offset but it's too small. */
#define __HAVE_COLUMN /* temporary */
/* 1+column number of pbase(); 0 is unknown. */
unsigned short _cur_column;
signed char _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_FILE file;
const struct _IO_jump_t *vtable;//IO函數跳轉表
};
Glibc 的詳細介紹:請點這裡
Glibc 的下載地址:請點這裡