【发布时间】:2024-01-14 21:24:01
【问题描述】:
我最近学会了如何做整个 fork/exec/wait 事情,最后写了一些看起来像这样精简的代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
pid_t x = fork();
if (x == 0) {
execlp("sqlite3", "sqlite3");
printf("exec failed\n");
} else {
wait(NULL);
printf("Hi\n");
}
}
这很有效,实际上最终打开了sqlite3 shell,但有一个问题。如果您在sqlite3 shell 之外Ctrl + C,那么它也会终止父进程并且printf("Hi\n") 行永远不会运行。
我认为这是因为 SIGINT 正在传播到父进程,在进一步研究之后,我读到 SIGINT 将终止同一组中的所有进程,这意味着由于父进程和子进程共享同一个组'都被终止了。
我试图通过像这样调用setpgid 来解决这个问题:
int main() {
pid_t x = fork();
if (x == 0) {
setpgid(getpid(), getpid()); // <-- Added in this line
execlp("sqlite3", "sqlite3");
printf("exec failed\n");
} else {
wait(NULL);
printf("Hi\n");
}
}
运行良好,但打破了sqlite3 提示。此外Ctrl + C 在这种情况下仍然杀死了父母。我决定尝试进一步缩小范围,然后发现一些奇怪的东西,并被难住了。只需在 fork 中调用 getpid() 就会使 execlp() 失败。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
pid_t x = fork();
if (x == 0) {
getpid(); // <--- just adding this in makes it fail
execlp("sqlite3", "sqlite3");
printf("exec failed\n");
} else {
wait(NULL);
printf("Hi\n");
}
}
不仅适用于sqlite3,还适用于execlp("ls", "ls") 之类的简单内容(尽管这可能与原始问题无关)。
我有点不知所措,我想知道是否有人能指出我正确的方向。我不太确定从这里做什么。
- 我使用的是 OSX 10.11,并且正在使用 clang 进行编译。
-
clang --version:Apple LLVM 版本 8.0.0 (clang-800.0.42.1)
【问题讨论】:
-
"如果你 Ctrl + C 退出 sqlite3 shell,那么它也会终止父进程" 因为 Ctrl+C 会导致 Bash 向它产生的进程发送 SIGINT (即您的 C 程序),而不是当时恰好从标准输入读取的任何进程。
-
@ColonelThirtyTwo 嗯,我明白了!所以我需要将
Ctrl + C发送到前台?另外,当我做execlp("bash", "bash")时,为什么不是这种情况?处理Ctrl + C就好了。 -
你已经是了。就 bash 而言,您的进程是前台进程,而
sqlite3只是碰巧使用相同标准输入管道的其他进程。您可以在 C 程序中处理 ctrl+c 并发送 sqlite3 SIGTERM,或使用 Ctrl+D 将 EOF 发送到 stdin,sqlite3 将接收并解释为 stdin 流的结尾。 -
首先修复最严重的错误:它应该是
execlp("bash", "bash", NULL);-- 需要NULL来终止execl()、execlp()和的参数列表execle()。然后,如果你想在父进程中处理 Ctrl+C,只需安装一个信号处理程序,它将SIGINT信号转发给子进程。 -
我想 bash 对终端做了一些特殊的设置来捕获和解释 ctrl+c 以便它可以将 SIGINT 发送给它的孩子。子 bash 进程会覆盖父进程的设置。
标签: c linux process sqlite fork