在 BusyBox 的 init
实现中,run
函数负责解析并执行 inittab
中的条目(如 console::once:/path/to/script.sh
)。以下是它的关键实现位置和逻辑分析:
1. run
函数定义位置
- 源代码文件:
BusyBox 的init
相关代码位于:
busybox/init/init.c
(具体路径可能因版本略有不同)。 - 函数定义:
搜索关键字static void run(struct init_action *a)
,通常在较新版本的 BusyBox 中可找到类似实现。
2. run
函数的执行流程
当 BusyBox init
处理到 inittab
中的某个条目(如 console::once:/etc/init.sh
)时:
解析条目:
将条目拆解为<terminal>:<action>:<process>
,存入struct init_action
。struct init_action {
char terminal[TERMINAL_NAME_SIZE]; // e.g., "console"
char action_type; // e.g., 'o' (once), 'r' (respawn)
char command[INIT_COMMAND_SIZE]; // e.g., "/etc/init.sh"
pid_t pid; // Child process PID
};
调用
run()
:
根据动作类型(如once
,respawn
,askfirst
),决定如何运行命令:static void run(struct init_action *a) {
pid_t pid = fork();
if (pid == 0) { // Child process
setsid(); // Start a new session
if (a->terminal[0]) {
open_terminal(a->terminal); // Open /dev/console or other TTY
}
exec_cmd(a->command); // Execute the script (e.g., via /bin/sh)
_exit(EXIT_FAILURE); // If exec fails
} else { // Parent process (PID=1)
a->pid = pid;
}
}
绑定终端 (
/dev/console
):
如果条目指定了终端(如console::...
),子进程会通过以下步骤关联控制台:- 调用
open_terminal("/dev/console")
→ 打开设备文件。 - 使用
dup2()
将/dev/console
绑定到 stdin/stdout/stderr。
- 调用
执行脚本:
最终通过/bin/sh -c /etc/init.sh
(或直接 execve())运行脚本。
3. /dev/console
vs Shell
- 关键区别:
如果脚本未显式重定向输出,其 stdout/stderr 自动继承父进程的文件描述符,即指向/dev/console
. - 验证方法:
在脚本中添加调试命令查看文件描述符:
#!/bin/sh
ls -l /proc/$$/fd # $$ = current PID, check fd 0(stdin),1(stdout),2(stderr)
echo "Hello" > /dev/tty # Direct write to current terminal
4. FAQ
(1) Q: Why doesn’t my script output appear on console?
- A: Check:
- Kernel是否启用了控制台输出(检查内核参数如
console=ttyS0,115200n8
)。 /dev/concole是否存在且可写
:ls -l /dev/concole && echo test > /dev/concole
- Script是否有权限执行 (
chmod +x /etc/initsh.sh
)
- Kernel是否启用了控制台输出(检查内核参数如
(2) Q: How to debug inittab execution?
- A: Enable BusyBox debug logging by recompiling with:
make EXTRA_CFLAGS="-DDEBUG_INIT"
Or add logging in your script:
“`sh
#!/bin/sh
exec > /tmp/debug.logs.txt
exec
内容由零声教学AI助手提供,问题来源于学员提问