我正在尝试 CSAPP 中的小型 shell 实验室。但是当我输入命令行时,我的代码卡住了。
steven@Steven:/mnt/f/大学/CSAPP/cmu15213/shlab-handout$ ./tsh
tsh> 123
tsh> 123: command not found
123
123
123
^\Terminating after receipt of SIGQUIT signal
steven@Steven:/mnt/f/大学/CSAPP/cmu15213/shlab-handout$
实验室链接:
我通过添加
-Og -g
修改了Makefile并尝试使用GDB和VSCode进行调试。
我发现程序卡在
sigprocmask
上。如下图所示,如果我点击“Step Over”,它会继续运行,永远不会停止。我复制了相关代码并单独运行,它运行正常。
tsh.c
:https://paste.ubuntu.com/p/g86JpxvJhv/我在 WSL 和虚拟机中对此进行了测试,并且两者都表现出相同的行为。
如果我单击“Step Over”,它会继续运行并且永远不会停止。
我转载了。那么让我们看看发生了什么。
gdb -q ./tsh
(gdb) break tsh.c:191
(gdb) b tsh.c:191
Breakpoint 1 at 0x40156a: file tsh.c, line 191.
(gdb) run
Starting program: /tmp/tsh
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
tsh> aaa
[Detaching after fork from child process 182]
aaa: command not found
Breakpoint 1, eval (cmdline=0x7fffffffd930 "aaa\n") at tsh.c:191
191 sigprocmask(SIG_SETMASK, &prev, NULL);
(gdb) n
此时一切都挂了,所以我们必须陷入
sigprocmask
,对吗?
事实上,我们不是。
^C
Program received signal SIGINT, Interrupt.
0x00007ffff7eb5c37 in __GI___wait4 (pid=-1, stat_loc=0x7fffffffcd5c, options=3, usage=0x0)
at ../sysdeps/unix/sysv/linux/wait4.c:30
30 return SYSCALL_CANCEL (wait4, pid, stat_loc, options, usage);
(gdb) bt
#0 0x00007ffff7eb5c37 in __GI___wait4 (pid=-1, stat_loc=0x7fffffffcd5c, options=3, usage=0x0)
at ../sysdeps/unix/sysv/linux/wait4.c:30
#1 0x0000000000401a02 in sigchld_handler (sig=17) at tsh.c:383
#2 <signal handler called>
#3 __GI___pthread_sigmask (how=2, newmask=<optimized out>, oldmask=0x0) at pthread_sigmask.c:43
#4 0x00007ffff7e18d8d in __GI___sigprocmask (how=<optimized out>, set=<optimized out>, oset=<optimized out>)
at ../sysdeps/unix/sysv/linux/sigprocmask.c:25
#5 0x0000000000401583 in eval (cmdline=0x7fffffffd930 "aaa\n") at tsh.c:191
#6 0x000000000040142d in main (argc=1, argv=0x7fffffffde68) at tsh.c:149
现在我们看看“实际上”发生了什么。 sigprocmask
解锁
SIGCHLD
,从而导致在 sigprocmask
即将返回之前立即 传送该信号。这反过来会调用 sigchld_handler
,反复
在永无休止的循环中调用
waitpid
。
为什么循环不终止?因为代码期望
waitpid
在没有子节点时返回 0
,但这是不正确的:在这种情况下
waitpid
返回
-1
。
以下修复使
tsh
按预期工作:
diff -u tsh.c.orig tsh.c
--- tsh.c.orig 2024-01-20 21:42:47.915401415 -0800
+++ tsh.c 2024-01-20 21:43:20.145996657 -0800
@@ -383,7 +383,7 @@
pid = waitpid(-1, &status, WNOHANG | WUNTRACED);
// 如果没有僵尸进程,则退出
- if (pid == 0)
+ if (pid == 0 || pid == -1)
return;
// 如果子进程终止导致waitpid从阻塞中恢复
./tsh
tsh> aaa
aaa: command not found
tsh> bbb
bbb: command not found
tsh>