glibc-printf
2014-03-11 19:03
176 查看
printf()函数定义在stdio-common/printf.c
vfprintf()声明在libio/stdio.h
printf函数是通过vfprintf将format输出到stdout文件中,stdout是(FILE *)类型。stdout的定义也在libio/stdio.h中。
= 1,每个进程的进程控制块都有一个打开文件的数组files。file结构就是根据fd在这个数组中查找到相应的结构。找到结构后,就会调用file->write()来向外输出。具体输出到哪里,就要看file结构对应的设备驱动是什么。
通过本文可以理解:文件描述符0、1和2和stdout、stdin和stderr对应,如果要修改linux内核中文件描述符相关代码,一定要注意文件描述符0、1和2的分配和回收,否则会导致终端没有输出信息,也无法和内核输入信息。
#include <libioP.h> #include <stdarg.h> #include <stdio.h> #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 ldbl_strong_alias (__printf, printf); /* This is for libg++. */ ldbl_strong_alias (__printf, _IO_printf);
#define ldbl_strong_alias(name, aliasname) strong_alias (name, aliasname)strong_alias,即取别名。网上有人提及这个strong alias好像是为了防止c库符号被其他库符号覆盖掉而使用的,如果__printf被覆盖了,还有printf和_IO_printf可以用。
vfprintf()声明在libio/stdio.h
/* Write formatted output to S from argument list ARG. This function is a possible cancellation point and therefore not marked with __THROW. */ extern int vfprintf (FILE *__restrict __s, __const char *__restrict __format, _G_va_list __arg);
typedef struct _IO_FILE FILE;
printf函数是通过vfprintf将format输出到stdout文件中,stdout是(FILE *)类型。stdout的定义也在libio/stdio.h中。
/* 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 stderrlibio/stdio.c
#undef stdin #undef stdout #undef stderr _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_FILE结构定义在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 };_IO_2_1_stdin_、_IO_2_1_stdout_和_IO_2_1_stderr_的定义在libio/stdfiles.clibio/stdfiles.c
#ifdef _IO_MTSAFE_IO # if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T # define DEF_STDFILE(NAME, FD, CHAIN, FLAGS) \ static _IO_lock_t _IO_stdfile_##FD##_lock = _IO_lock_initializer; \ static struct _IO_wide_data _IO_wide_data_##FD \ = { ._wide_vtable = &_IO_wfile_jumps }; \ struct _IO_FILE_plus NAME \ = {FILEBUF_LITERAL(CHAIN, FLAGS, FD, &_IO_wide_data_##FD), \ &_IO_file_jumps}; # else # define DEF_STDFILE(NAME, FD, CHAIN, FLAGS) \ static _IO_lock_t _IO_stdfile_##FD##_lock = _IO_lock_initializer; \ struct _IO_FILE_plus NAME \ = {FILEBUF_LITERAL(CHAIN, FLAGS, FD, NULL), \ &_IO_file_jumps}; # endif #else # if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T # define DEF_STDFILE(NAME, FD, CHAIN, FLAGS) \ static struct _IO_wide_data _IO_wide_data_##FD \ = { ._wide_vtable = &_IO_wfile_jumps }; \ struct _IO_FILE_plus NAME \ = {FILEBUF_LITERAL(CHAIN, FLAGS, FD, &_IO_wide_data_##FD), \ &_IO_file_jumps}; # else # define DEF_STDFILE(NAME, FD, CHAIN, FLAGS) \ struct _IO_FILE_plus NAME \ = {FILEBUF_LITERAL(CHAIN, FLAGS, FD, NULL), \ &_IO_file_jumps}; # endif #endif 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); struct _IO_FILE_plus *_IO_list_all = &_IO_2_1_stderr_; INTVARDEF(_IO_list_all)_IO_2_1_stdout_的FD = 0、_IO_2_1_stdin_的FD = 1、_IO_2_1_stderr_的FD = 2。FILEBUF_LITERAL用于初始化_IO_FILE,定义在libio/libioP.h
#ifdef _IO_MTSAFE_IO /* check following! */ # ifdef _IO_USE_OLD_IO_FILE # define FILEBUF_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 }, &_IO_stdfile_##FD##_lock } # else # if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T # define FILEBUF_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 }, &_IO_stdfile_##FD##_lock, _IO_pos_BAD,\ NULL, WDP, 0 } # else # define FILEBUF_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 }, &_IO_stdfile_##FD##_lock, _IO_pos_BAD,\ 0 } # endif # endif #else # ifdef _IO_USE_OLD_IO_FILE # define FILEBUF_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 } # else # if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T # define FILEBUF_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, \ NULL, WDP, 0 } # else # define FILEBUF_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 } # endif # endif #endif其中,FD赋值给了_fileno。我们回到vfprintf的分析,vfprintf的具体实现本文就不详细讲解,主要原理是格式化字符串,最后将字符串输出到文件中,也就是stdout中。至于如何输出,则和_IO_file_jumps关系密切,_IO_file_jumps的定义在libio/fileops.c
const struct _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) }; libc_hidden_data_def (_IO_file_jumps)至于怎么跳转到这些函数,以及如何跳转到linux内核,由于涉及到glibc的一些细节,这里简单介绍一下进入内核后的情况:进入linux内核后,调用write(),在write之前所有的代码都是C库的代码,可以说是和平台无关的。而涉及到具体输出,就要调用操作系统提供给的接口。调用write()后,通过系统调用进入内核空间,首先是sys_write(),这个函数代码位于fs/read_write.c中。一进入sys_write(),就要根据传进来的fd描述符找到相应的file结构。对于标准输出,fd
= 1,每个进程的进程控制块都有一个打开文件的数组files。file结构就是根据fd在这个数组中查找到相应的结构。找到结构后,就会调用file->write()来向外输出。具体输出到哪里,就要看file结构对应的设备驱动是什么。
通过本文可以理解:文件描述符0、1和2和stdout、stdin和stderr对应,如果要修改linux内核中文件描述符相关代码,一定要注意文件描述符0、1和2的分配和回收,否则会导致终端没有输出信息,也无法和内核输入信息。
相关文章推荐
- Visual C++ ADO数据库编程入门(下)
- Visual Studio Intellisense、Eclipse Content Assist与理念式编程
- SAP 金额在表中的存储及货币转换因子
- Java transient关键字使用小记
- Visual C++ ADO数据库编程入门(上)
- 黑马程序员___交通灯管理系统
- 计算几何——凸包
- linux磁盘管理
- Android——内存管理基础
- java自定义注解
- win7与android设备通过蓝牙串口的连接方法
- glew库
- 国产第一虚拟币元宝币:元宝币基金会
- struts2中result的使用
- Iptables & tc在公有云的应用(示例)
- [ACM] hdu 4706 Children's Day
- 用栈来实现数制转换
- hdu 2509
- [ACM] hdu 4706 Children's Day
- 数据库持久连接理解