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号调用的具体参数:
看一下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是怎么实现呢?通过系统调用,不过,这个write的API在哪里实现呢?
在内核中,我还没有找到,不过,从Linux的API列表中,看到了。具体方法是:
#: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;
}
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 10H,AH=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是怎么实现呢?通过系统调用,不过,这个write的API在哪里实现呢?
在内核中,我还没有找到,不过,从Linux的API列表中,看到了。具体方法是:
#: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;
}
评论
发表评论