Printf从函数库到OS跟踪流程

首先需要弄清楚:API-用户编程借口与 系统调用的区别,这里有一篇非常好的文章:

http://www.tek-life.org/2010/10/12/linux%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8%EF%BC%BBz%EF%BC%BD/

另外,printf具体内部原理请见:

http://www.tek-life.org/2010/10/12/printf%E5%92%8C%E6%A0%87%E5%87%86%E8%BE%93%E5%87%BA%EF%BC%BBz%EF%BC%BD/

我仅仅是进行了code review


arch/x86/boot/printf.c

int printf(const char *fmt, ...)

{

char printf_buf[1024];

va_list args;

int printed;



va_start(args, fmt);

printed = vsprintf(printf_buf, fmt, args);

va_end(args);



puts(printf_buf);



return printed;

}

void __attribute__((section(".inittext"))) puts(const char *str)

{

int n = 0;

while (*str) {

putchar(*str++);

n++;

}

}



/*

* These functions are in .inittext so they can be used to signal

* error during initialization.

*/



void __attribute__((section(".inittext"))) putchar(int ch)

{

unsigned char c = ch;



if (c == '\n')

putchar('\r'); /* \n -> \r\n */



/* int $0x10 is known to have bugs involving touching registers

it shouldn't. Be extra conservative... */

asm volatile("pushal; pushw %%ds; int $0x10; popw %%ds; popal"

: : "b" (0x0007), "c" (0x0001), "a" (0x0e00|ch));

}

我擦,跟踪错了!这是在实模式!调用10号中断显示。注释上写的很清楚,他们被安排在inittext段,当且进当在初始化的时候会调用。Inittext会被擦除的。

10号调用的具体参数:






BIOS 呼叫 INT 10HAH=0EH
在文字模式 或 绘图模式下显示一字元,游标则右移一格
(AL) <=
字元 ascii
(BH) <=
页码
(BL) <=
前景颜色码 (绘图模式时)
:所谓 TTY 就是类似打字机输出方式,每显示一字元,游标则右移一格,
当移到最后一行时,游标则至跳下一列的最左边开始,当移到最后一列
时,萤幕则上卷一列

看一下C库函数中的具体实现:

采用ulibc

libc/stdio/printf.c:

int printf(const char * __restrict format, ...){

va_list arg;

int rv;



va_start(arg, format);

rv = vfprintf(stdout, format, arg);

va_end(arg);



return rv;

}

libc/stdio/old_vfprintf.c

int vfprintf(FILE * __restrict op, register const char * __restrict fmt,

va_list ap)

{

union {

#ifdef LLONG_MAX

long long ll;

#endif

#if LONG_MAX != INT_MAX

long l;

#endif

int i;

} intarg;

int i, cnt, dataargtype, len;

const void *argptr = argptr; /* ok to be initialized. */

register char *p;

const char *fmt0;

int preci, width;

#define upcase i

int radix, dpoint /*, upcase*/;

char tmp[65]; /* TODO - determine needed size from headers */

char flag[sizeof(spec)];

__STDIO_AUTO_THREADLOCK_VAR;



__STDIO_AUTO_THREADLOCK(op);



__STDIO_STREAM_VALIDATE(op);



cnt = 0;



if (__STDIO_STREAM_IS_NARROW_WRITING(op)

|| !__STDIO_STREAM_TRANS_TO_WRITE(op, __FLAG_NARROW)

) {



while (*fmt) {

if (*fmt == '%') {

fmt0 = fmt; /* save our position in case of bad format */

++fmt;

width = -1; /* min field width */

preci = -5; /* max string width or mininum digits */

radix = 10; /* number base */

dpoint = 0; /* found decimal point */



/* init flags */

for (p =(char *) spec ; *p ; p++) {

flag[p-spec] = '\0';

}

flag[FLAG_0_PAD] = ' ';



/* process optional flags */

for (p = (char *)spec ; *p ; ) {

if (*fmt == *p) {

flag[p-spec] = *fmt++;

p = (char *)spec; /* restart scan */

} else {

p++;

}

}



if (!flag[FLAG_PLUS]) {

flag[FLAG_PLUS] = flag[FLAG_SPACE];

}



/* process optional width and precision */

do {

if (*fmt == '.') {

++fmt;

dpoint = 1;

}

if (*fmt == '*') { /* parameter width value */

++fmt;

i = va_arg(ap, int);

} else {

for ( i = 0 ; (*fmt >= '0') && (*fmt <= '9') ; ++fmt ) {

i = (i * 10) + (*fmt - '0');

}

}



if (dpoint) { preci = i;

if (i<0) {

preci = -5;

}

} else {

width = i;

if (i<0) {

width = -i;

flag[FLAG_MINUS_LJUSTIFY] = 1;

}

}

} while ((*fmt == '.') && !dpoint );



/* process optional qualifier */

p = (char *) qual_chars;

do {

if (*fmt == *p) {

++fmt;

break;

}

} while (*++p);

if ((p - qual_chars < 2) && (*fmt == *p)) {

p += ((sizeof(qual_chars)-2) / 2);

++fmt;

}

dataargtype = ((int)(p[(sizeof(qual_chars)-2) / 2])) << 8;



#if WANT_GNU_ERRNO

if (*fmt == 'm') {

flag[FLAG_PLUS] = '\0';

flag[FLAG_0_PAD] = ' ';

p = __glibc_strerror_r(errno, tmp, sizeof(tmp));

goto print;

}

#endif



/* process format specifier */

for (p = (char *) u_spec ; *p ; p++) {

if (*fmt != *p) continue;

if (p-u_spec < 1) { /* print a % */

goto charout;

}

if (p-u_spec < 2) { /* store output count in int ptr */

_store_inttype(va_arg(ap, void *),

dataargtype,

(intmax_t) (cnt));

goto nextfmt;

}



if (p-u_spec < 10) {

if (*p == 'p') {

#if INTPTR_MAX == INT_MAX

dataargtype = 0;

#else

#error Fix dataargtype for pointers!

#endif

}



switch(dataargtype) {

case (PA_INT|PA_FLAG_LONG_LONG):

#ifdef LLONG_MAX

intarg.ll = va_arg(ap, long long);

argptr = &intarg.ll;

break;

#endif

case (PA_INT|PA_FLAG_LONG):

#if LONG_MAX != INT_MAX

intarg.l = va_arg(ap, long);

argptr = &intarg.l;

break;

#endif

default:

intarg.i = va_arg(ap, int);

argptr = &intarg.i;

break;

}

}

if (p-u_spec < 8) { /* unsigned conversion */

radix = u_radix[p-u_spec-2];

upcase = ((*p == 'x') ? __UIM_LOWER : __UIM_UPPER);

if (*p == 'p') {

upcase = __UIM_LOWER;

flag[FLAG_HASH] = 'p';

}

p = _uintmaxtostr(tmp + sizeof(tmp) - 1,

(uintmax_t)

_load_inttype(dataargtype, argptr, radix),

radix, upcase);



flag[FLAG_PLUS] = '\0'; /* meaningless for unsigned */

if (*p != '0') { /* non-zero */

if (flag[FLAG_HASH]) {

if (radix == 8) {

*--p = '0'; /* add leadding zero */

} else if (radix != 10) { /* either 2 or 16 */

flag[FLAG_PLUS] = '0';

*--p = 'b';

if (radix == 16) {

*p = 'x';

if (*fmt == 'X') {

*p = 'X';

}

}

}

}

} else if (flag[FLAG_HASH] == 'p') { /* null pointer */

p = "(nil)";

}

} else if (p-u_spec < 10) { /* signed conversion */

p = _uintmaxtostr(tmp + sizeof(tmp) - 1,

(uintmax_t)

_load_inttype(dataargtype, argptr, -radix),

-radix, upcase);



} else if (p-u_spec < 12) { /* character or string */

flag[FLAG_PLUS] = '\0';

flag[FLAG_0_PAD] = ' ';

if (*p == 'c') { /* character */

p = tmp;

*p = va_arg(ap, int);

/* This takes care of the "%c",0 case */

len = 1;

goto print_len_set;

} else { /* string */

p = va_arg(ap, char *);

if (!p) {

p = "(null)";

preci = 6;

} else {

if (preci < 0) {

preci = INT_MAX;

}

}

len = strnlen(p, preci);

goto print_len_set;

}

#if defined(__UCLIBC_HAS_FLOATS__) || WANT_FLOAT_ERROR

} else if (p-u_spec < 27) { /* floating point */

#endif /* defined(__UCLIBC_HAS_FLOATS__) || WANT_FLOAT_ERROR */

#if defined(__UCLIBC_HAS_FLOATS__)

struct printf_info info;

if (preci < 0) {

preci = 6;

}

info.width = width;

info.prec = preci;

info.spec = *fmt;

info.pad = flag[FLAG_0_PAD];

info._flags = 0;

if (flag[FLAG_PLUS] == '+') {

PRINT_INFO_SET_FLAG(&info,showsign);

} else if (flag[FLAG_PLUS] == ' ') {

PRINT_INFO_SET_FLAG(&info,space);

}

if (flag[FLAG_HASH]) {

PRINT_INFO_SET_FLAG(&info,alt);

}

if (flag[FLAG_MINUS_LJUSTIFY]) {

PRINT_INFO_SET_FLAG(&info,left);

}

#if 1

cnt += _fpmaxtostr(op,

(__fpmax_t)

((dataargtype == (8 << 8))

? va_arg(ap, long double)

: (long double) va_arg(ap, double)),

&info, _fp_out_narrow);

#else

cnt += _fpmaxtostr(op,

(__fpmax_t)

((lval > 1)

? va_arg(ap, long double)

: (long double) va_arg(ap, double)),

&info, _fp_out_narrow);

#endif

goto nextfmt;

#elif WANT_FLOAT_ERROR

(void) ((lval > 1) ? va_arg(ap, long double)

: va_arg(ap, double)); /* carry on */

p = (char *) dbl_err;

#endif /* defined(__UCLIBC_HAS_FLOATS__) */

}



#if WANT_GNU_ERRNO

print:

#endif

{ /* this used to be printfield */

/* cheaper than strlen call */

/* for ( len = 0 ; p[len] ; len++ ) { } */

len = strnlen(p, SIZE_MAX);

print_len_set:

if ((*p == '-')

#if WANT_GNU_ERRNO

&& (*fmt != 'm')

#endif

&& (*fmt != 's')) {

flag[FLAG_PLUS] = *p++;

--len;

}

if (flag[FLAG_PLUS]) {

++len;

++preci;

if (flag[FLAG_PLUS] == '0') { /* base 16 */

++preci; /* account for x or X */

}

}



if (preci >= 0) {

if ((*fmt == 's')

#if WANT_GNU_ERRNO

|| (*fmt == 'm')

#endif

) {

if (len > preci) {

len = preci;

} else {

preci = len;

}

}

preci -= len;

if (preci < 0) {

preci = 0;

}

width -= preci;

}



width -= len;

if (width < 0) {

width = 0;

}



if (preci < 0) {

preci = 0;

if (!flag[FLAG_MINUS_LJUSTIFY]

/* && flag[FLAG_PLUS] */

&& (flag[FLAG_0_PAD] == '0')) {

preci = width;

width = 0;

}

}



while (width + len + preci) {

unsigned char ch;

/* right padding || left padding */

if ((!len && !preci)

|| (width && !flag[FLAG_MINUS_LJUSTIFY])) {

ch = ' ';

--width;

} else if (flag[FLAG_PLUS]) {

ch = flag[FLAG_PLUS]; /* sign */

if (flag[FLAG_PLUS]=='0') { /* base 16 case */

flag[FLAG_PLUS] = *p++; /* get the x|X */

} else {

flag[FLAG_PLUS] = '\0';

}

--len;

} else if (preci) {

ch = '0';

--preci;

} else {

ch = *p++; /* main field */

--len;

}

++cnt;

PUTC(ch, op);

}

}

goto nextfmt;

}



fmt = fmt0; /* this was an illegal format */

}



charout:

++cnt;

PUTC(*fmt, op); /* normal char out */



nextfmt:

++fmt;

}



}



i = (__FERROR_UNLOCKED(op)) ? -1 : cnt;



__STDIO_STREAM_VALIDATE(op);



__STDIO_AUTO_THREADUNLOCK(op);



return i;

}

libc/stdio/old_vfprintf.c

#define PUTC(C,F) putc_unlocked((C),(F))

include/stdio.h

define putc_unlocked(_ch, _fp) __PUTC_UNLOCKED(_ch, _fp)

libc/sysdeps/linux/common/bits/uClibc_stdio.h

#define __PUTC_UNLOCKED(__c, __stream) (__fputc_unlocked)((__c),(__stream))

libc/stdio/fputc.c

int __fputc_unlocked(int c, register FILE *stream)

{

__STDIO_STREAM_VALIDATE(stream);



/* First the fast path. We're good to go if putc macro enabled. */

if (__STDIO_STREAM_CAN_USE_BUFFER_ADD(stream)) {

__STDIO_STREAM_BUFFER_ADD(stream, ((unsigned char) c));

return (unsigned char) c;

}



/* Next quickest... writing and narrow oriented, but macro

* disabled and/or buffer is full. */

if (__STDIO_STREAM_IS_NARROW_WRITING(stream)

|| !__STDIO_STREAM_TRANS_TO_WRITE(stream, __FLAG_NARROW)

) {

if (__STDIO_STREAM_IS_FAKE_VSNPRINTF(stream)) {

return (unsigned char) c;

}



if (__STDIO_STREAM_BUFFER_SIZE(stream)) { /* Do we have a buffer? */

/* The buffer is full and/or the stream is line buffered. */

if (!__STDIO_STREAM_BUFFER_WAVAIL(stream) /* Buffer full? */

&& __STDIO_COMMIT_WRITE_BUFFER(stream) /* Commit failed! */

) {

goto BAD;

}

#ifdef __UCLIBC_MJN3_ONLY__

#warning CONSIDER: Should we fail if the commit fails but we now have room?

#endif



__STDIO_STREAM_BUFFER_ADD(stream, ((unsigned char) c));



if (__STDIO_STREAM_IS_LBF(stream)) {

if ((((unsigned char) c) == '\n')

&& __STDIO_COMMIT_WRITE_BUFFER(stream)) {

/* Commit failed! */

__STDIO_STREAM_BUFFER_UNADD(stream); /* Undo the write! */

goto BAD;

}

}

} else {

/* NOTE: Do not try to save space by moving uc to the top of

* the file, as that dramaticly increases runtime. */

unsigned char uc = (unsigned char) c;

if (! __stdio_WRITE(stream, &uc, 1)) { //如果没有缓冲区,那么就输出到标准输出中

goto BAD;

}

}

return (unsigned char) c;

}



BAD:

return EOF;

}

libc/stdio/_WRITE.c

size_t attribute_hidden __stdio_WRITE(register FILE *stream,

register const unsigned char *buf, size_t bufsize)

{

size_t todo;

ssize_t rv, stodo;



__STDIO_STREAM_VALIDATE(stream);

assert(stream->__filedes >= -1);

assert(__STDIO_STREAM_IS_WRITING(stream));

assert(!__STDIO_STREAM_BUFFER_WUSED(stream)); /* Buffer must be empty. */



todo = bufsize;



do {

if (todo == 0) { /* Done? */

__STDIO_STREAM_VALIDATE(stream);

return bufsize;

}

stodo = (todo <= SSIZE_MAX) ? todo : SSIZE_MAX;

if ((rv = __WRITE(stream, (char *) buf, stodo)) >= 0) { //写到标准输出中

#ifdef __UCLIBC_MJN3_ONLY__

#warning TODO: Make custom stream write return check optional.

#endif

#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__

assert(rv <= stodo);

if (rv > stodo) { /* Wrote more than stodo! */

/* abort(); */

}

#endif

todo -= rv;

buf += rv;

} else

#ifdef __UCLIBC_MJN3_ONLY__

#warning EINTR?

#endif

/* if (errno != EINTR) */

{

__STDIO_STREAM_SET_ERROR(stream);



#ifdef __STDIO_BUFFERS

if ((stodo = __STDIO_STREAM_BUFFER_SIZE(stream)) != 0) {

unsigned char *s;



if (stodo > todo) {

stodo = todo;

}



s = stream->__bufstart;



do {

if (((*s = *buf) == '\n')

&& __STDIO_STREAM_IS_LBF(stream)

) {

break;

}

++s;

++buf;

} while (--stodo);



stream->__bufpos = s;



todo -= (s - stream->__bufstart);

}

#endif /* __STDIO_BUFFERS */



__STDIO_STREAM_VALIDATE(stream);

return bufsize - todo;

}

} while (1);

}

libc/stdio/_stdio.h

#define __WRITE(STREAMPTR,BUF,SIZE) \

((((STREAMPTR)->__gcs.write) == NULL) ? -1 : \

(((STREAMPTR)->__gcs.write)((STREAMPTR)->__cookie,(BUF),(SIZE))))

这个write到底怎么实现呢?

看:

libc/stdio/_fopen.c

__STDIO_STREAM_RESET_GCS(stream);

libc/stdio/_stdio.h

#define __STDIO_STREAM_RESET_GCS(S) \

(S)->__cookie = &((S)->__filedes); \

(S)->__gcs.read = _cs_read; \

(S)->__gcs.write = _cs_write; \

(S)->__gcs.seek = _cs_seek; \

(S)->__gcs.close = _cs_close

libc/stdio/_cs_funcs.c

ssize_t attribute_hidden _cs_write(void *cookie, const char *buf, size_t bufsize)

{

return write(*((int *) cookie), (char *) buf, bufsize);

}

这个write是怎么实现呢?通过系统调用,不过,这个writeAPI在哪里实现呢?

在内核中,我还没有找到,不过,从LinuxAPI列表中,看到了。具体方法是:

#:man 2 syscalls

系统调用服务例程:

asmlinkage ssize_t sys_write(unsigned int fd, const char __user * buf, size_t count)

{

struct file *file;

ssize_t ret = -EBADF;

int fput_needed;



file = fget_light(fd, &fput_needed);

if (file) {

loff_t pos = file_pos_read(file);

ret = vfs_write(file, buf, count, &pos);

file_pos_write(file, pos);

fput_light(file, fput_needed);

}



return ret;

}

struct file *fget_light(unsigned int fd, int *fput_needed)

{

struct file *file;

struct files_struct *files = current->files;



*fput_needed = 0;

if (likely((atomic_read(&files->count) == 1))) {

file = fcheck_files(files, fd);

} else {

rcu_read_lock();

file = fcheck_files(files, fd);

if (file) {

if (atomic_inc_not_zero(&file->f_count))

*fput_needed = 1;

else

/* Didn't get the reference, someone's freed */

file = NULL;

}

rcu_read_unlock();

}



return file;

}

fs/read_write.c

ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)

{

ssize_t ret;



if (!(file->f_mode & FMODE_WRITE))

return -EBADF;

if (!file->f_op || (!file->f_op->write && !file->f_op->aio_write))

return -EINVAL;

if (unlikely(!access_ok(VERIFY_READ, buf, count)))

return -EFAULT;



ret = rw_verify_area(WRITE, file, pos, count);

if (ret >= 0) {

count = ret;

if (file->f_op->write)

ret = file->f_op->write(file, buf, count, pos);

else

ret = do_sync_write(file, buf, count, pos);

if (ret > 0) {

fsnotify_modify(file->f_path.dentry);

add_wchar(current, ret);

}

inc_syscw(current);

}



return ret;

}

评论

此博客中的热门博文

Linux/ARM Page Table Entry 属性设置分析

提交了30次才AC ---【附】POJ 2488解题报告

笔记