You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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值。

验证与修复步骤

  1. 添加函数原型声明:

    • 在/kernel/printk.c的头部添加函数声明:int fprintk(int fd);
    • 在/kernel/exit.c的开头通过#include "printk.h"(若printk.h存在),或直接添加extern int fprintk(int fd);
  2. 检查历史代码残留影响:
    你注释掉了带格式化参数的fprintk(fd, fmt, ...)版本,若之前的隐式声明是按可变参数生成的,即使修改了函数定义,编译器仍可能残留旧的调用逻辑。确保所有调用处的函数签名与定义完全一致。

  3. 重新编译内核:
    执行内核编译流程,验证fprintk内部打印的fd值是否恢复正常。

补充说明

Linux 0.11的x86架构采用cdecl调用约定(参数从右往左压栈,调用者清理栈),函数声明与定义不匹配会直接导致栈参数解析错位——这也是你调用前printk能正确输出fd=3,但函数内部读取到异常值的根本原因:调用前栈上的fd变量本身没问题,但函数按错误的方式读取了栈位置。

内容的提问来源于stack exchange,提问作者陈润泽

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 07:45:11