我正在try CSAPP的微型贝壳实验室.但是,当我输入命令行时,我的代码就卡壳了.

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$ 

实验室链接:

我修改了Makefile,增加了-Og -g,并try 使用GDB和VSCode进行调试.

I found the program get stuck on the sigprocmask. As shown in the following picture, if I click "Step Over", it continues to run and never stops.
enter image description here

我复制了相关的代码并单独运行,它工作正常.

我已经在WSL和一台虚拟机上测试了这一点,两者都表现出相同的行为.

推荐答案

如果我点击"跳过",它会继续运行,永远不会停止.

我复制了那个.所以让我们来看看发生了什么.

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

现在我们来看看actually是怎么回事.sigprocmask解锁SIGCHLD,这导致immediate传递将要返回信号just before sigprocmask.这进而调用sigchld_handler,而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> 

以下是来自Linux man page的相关文本:

如果在Options中设置了WNOHANG来调用waitpid(),则它至少有一个由pid指定的子进程的状态不可用,并且状态对于任何由PID指定的进程都不可用,则返回0.否则,返回-1

C++相关问答推荐

C语言中字符数组声明中的标准

将 typewriter LF打印到Windows终端,而不是隐含的CR+LF

将指针作为参数传递给函数

C语言中的strstr问题

如何捕捉只有换行符或空格字符缓冲区的边缘大小写

轮询libusb_pollfd struct 列表的正确方式是什么?

如何使用C++在控制台中以彩色打印被阻止的客户端

Go和C中的数据 struct 对齐差异

错误:字符串在C中获得意外输出

如何在C宏定义中包含双引号?

c程序,让用户输入两类数字,并给出输出用户输入多少个数字

STM32 FATFS用户手册(Um1721)中的代码正确吗?

';malloc():损坏的顶部大小';分配超过20万整数后

通过GTK';传递回调参数;s g_signal_connect()导致C中出现意外值

GnuCobol 使用 double 类型的参数调用 C 函数

Struct 内的数组赋值

(GNU+Linux) 多个线程同时调用malloc()

可以从指针数组中的值初始化指针吗?

我们可以在不违反标准的情况下向标准函数声明添加属性吗?

将十六进制值或十进制值分配给 uint16_t 有什么区别?