# Chapter 005: System Call
# Status: Timeline 1973.2.28
# Location: Bell Labs, Murray Hill
>>> Timeline Jump Complete
>>> Current Location: Bell Labs, 1973
>>> Mission: Debug critical system call implementation
>>> Warning: System call interface affects all future Unix variants
>>> Status: Initializing environment...
这次我来到了贝尔实验室的另一个时间点。眼前的终端显示着Unix V4版本的源代码。空气中弥漫着咖啡和纸质打印机墨水的气味。
"syscall的实现有问题,"Ken Thompson的声音从背后传来,"用户程序偶尔会进入死循环。"
我看向屏幕。这是Unix系统最核心的部分之一:系统调用接口。如果这里出现问题,将影响到未来所有的Unix变种系统。
首先检查系统调用的入口代码:
void trap() {
int syscallno;
struct proc *p;
p = current;
syscallno = u.u_ar0[R0];
if (syscallno >= nsyscalls) {
u.u_error = EINVAL;
return;
}
u.u_ar0[R0] = (*syscalls[syscallno])();
}
看起来入口函数本身没有明显问题。继续深入检查系统调用表:
int (*syscalls[])() = {
&sys_null, /* 0 = indir */
&sys_exit, /* 1 = exit */
&sys_fork, /* 2 = fork */
&sys_read, /* 3 = read */
&sys_write, /* 4 = write */
&sys_open, /* 5 = open */
&sys_close, /* 6 = close */
&sys_wait, /* 7 = wait */
/* ... */
};
"让我看看wait系统调用的实现。"我说着,调出了相关代码:
int sys_wait() {
struct proc *p;
struct proc *child;
p = current;
// 检查是否有子进程
child = p->p_children;
while (child != NULL) {
if (child->p_stat == ZOMBIE) {
// 回收僵尸进程
collect_zombie(child);
return child->p_pid;
}
child = child->p_sibling;
}
// 没有找到可等待的子进程
if (p->p_children == NULL) {
u.u_error = ECHILD;
return -1;
}
// 这里!这是问题所在
// 进程在这里等待时没有正确设置状态
while (1) {
sleep(p); // 可能导致死锁
}
}
"找到了,"我说,"wait系统调用的实现有严重问题。它在等待子进程时可能导致死锁。"
我开始修改代码:
int sys_wait() {
struct proc *p;
struct proc *child;
p = current;
while (1) {
// 首先检查是否有僵尸子进程
child = p->p_children;
while (child != NULL) {
if (child->p_stat == ZOMBIE) {
int pid = child->p_pid;
collect_zombie(child);
return pid;
}
child = child->p_sibling;
}
// 检查是否还有子进程
if (p->p_children == NULL) {
u.u_error = ECHILD;
return -1;
}
// 正确设置进程状态并进入睡眠
p->p_stat = SLEEPING;
p->p_wchan = p; // 等待通道
swtch(); // 切换到其他进程
// 被唤醒后检查是否有信号待处理
if (p->p_sig && issig()) {
u.u_error = EINTR;
return -1;
}
}
}
"这个修改解决了几个问题,"我解释道,"首先,它正确处理了进程状态的转换。其次,它增加了对信号的处理。最后,它使用了更安全的进程切换机制。"
为了测试这个修改,我编写了一个测试程序:
int main() {
int pid;
int status;
pid = fork();
if (pid == 0) {
// 子进程
sleep(1);
exit(42);
} else {
// 父进程
status = wait(0);
printf("child exited with status %d\n", status);
}
return 0;
}
"让我们编译并运行它。"
>>> Compiling test program...
>>> Running test...
>>> child exited with status 42
>>> Test passed: Wait system call working correctly
Ken Thompson点点头:"看起来不错。让我们再测试一些极端情况。"
我们又测试了几种场景:
1. 多个子进程同时退出
2. 父进程在子进程之前退出
3. 在wait期间发送信号
4. 嵌套的fork-wait调用
所有测试都通过了。这个修复不仅解决了当前的问题,还为后来的Unix系统提供了一个更可靠的进程管理模型。
正当我准备展示更多改进时,熟悉的眩晕感又来了。
>>> Mission Completed
>>> Timeline Stable
>>> Bug Fixed: Wait System Call Implementation
>>> Historical Impact: Critical
>>> Note: Process Management Model Established
>>> Preparing for next jump...
在视线变得模糊之前,我看到Ken Thompson在他的笔记本上写着什么。这些修改将成为后来所有Unix系统的标准实现。
系统调用,操作系统与用户程序之间的桥梁。一个看似简单的接口,却承载着整个计算机世界的秩序。而我,有幸参与了这个接口的完善。
# End of Chapter 005
# Next Timeline Loading...
梦远书城已将原网页转码以便移动设备浏览
本站仅提供资源搜索服务,不存放任何实质内容。如有侵权内容请联系搜狗,源资源删除后本站的链接将自动失效。
推荐阅读