Linux 0.11调试问题:调用fprintk时fd参数值异常
问题:Linux 0.11进程退出时fprintk参数异常排查
我近期在研究调试Linux 0.11源码,需求是实现进程退出时打印进程消息。我在/kernel/printk.c中添加了fprintk函数,并在/kernel/exit.c的do_exit()函数内调用该函数:定义int fd=3,调用前通过printk打印exit fd: 3(输出正常),但fprintk内部打印的fd却是异常的奇怪值,请求协助排查原因。
fprintk函数代码
// static char logbuf[1024]; int fprintk(int fd) { printk("--------------------------------------\n"); printk("fprintk called with fd: %d\n", fd); // Debugging information // va_list args; // int count; // va_start(args, fmt); // count = vsprintf(logbuf, fmt, args); // va_end(args); // printk("fprintk: formatted string: %s\n", logbuf); // Debugging information // if (fd == 3) { // count = sys_write(fd, logbuf, count); // printk("fprintk: written %d bytes to fd %d\n", count, fd); // Debugging information // } printk("--------------------------------------\n"); // return count; return 0; }
do_exit函数相关代码
int do_exit(long code) { int i; int fd = 3; free_page_tables(get_base(current->ldt[1]),get_limit(0x0f)); free_page_tables(get_base(current->ldt[2]),get_limit(0x17)); for (i=0 ; i<NR_TASKS ; i++) if (task[i] && task[i]->father == current->pid) { task[i]->father = 1; if (task[i]->state == TASK_ZOMBIE) /* assumption task[1] is always init */ (void) send_sig(SIGCHLD, task[1], 1); } for (i=0 ; i<NR_OPEN ; i++) if (current->filp[i]) sys_close(i); iput(current->pwd); current->pwd=NULL; iput(current->root); current->root=NULL; iput(current->executable); current->executable=NULL; if (current->leader && current->tty >= 0) tty_table[current->tty].pgrp = 0; if (last_task_used_math == current) last_task_used_math = NULL; if (current->leader) kill_session(); current->state = TASK_ZOMBIE; current->exit_code = code; // before father wakes up, children exits printk("exit fd: %d\n", fd); // fprintk(fd, "%ld\t%c\t%ld\n", current->pid, 'E', jiffies); fprintk(fd); tell_father(current->father); schedule(); return (-1); /* just to suppress warnings */ }
排查思路与解决方案
核心原因:函数隐式声明导致参数解析错误
Linux 0.11采用纯C环境,若调用函数前未声明其原型,编译器会默认按隐式声明处理(即int fprintk(),视为无参数或可变参数函数)。而你的fprintk定义是int fprintk(int fd),声明与定义不匹配会导致编译器按错误的调用约定解析栈上的参数,最终读取到错误的fd值。
验证与修复步骤
添加函数原型声明:
- 在
/kernel/printk.c的头部添加函数声明:int fprintk(int fd); - 在
/kernel/exit.c的开头通过#include "printk.h"(若printk.h存在),或直接添加extern int fprintk(int fd);
- 在
检查历史代码残留影响:
你注释掉了带格式化参数的fprintk(fd, fmt, ...)版本,若之前的隐式声明是按可变参数生成的,即使修改了函数定义,编译器仍可能残留旧的调用逻辑。确保所有调用处的函数签名与定义完全一致。重新编译内核:
执行内核编译流程,验证fprintk内部打印的fd值是否恢复正常。
补充说明
Linux 0.11的x86架构采用cdecl调用约定(参数从右往左压栈,调用者清理栈),函数声明与定义不匹配会直接导致栈参数解析错位——这也是你调用前printk能正确输出fd=3,但函数内部读取到异常值的根本原因:调用前栈上的fd变量本身没问题,但函数按错误的方式读取了栈位置。
内容的提问来源于stack exchange,提问作者陈润泽
相关产品推荐
相关产品推荐

