C - SIGINT处理程序不能与多线程一起工作,每个线程都有一个popen进程。
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C - SIGINT处理程序不能与多线程一起工作,每个线程都有一个popen进程。相关的知识,希望对你有一定的参考价值。
我有一个小的C程序,它的功能如下。
- 打开多个线程,每一个线程都会产生一个
ssh
流程中使用popen()
. - 对于每个线程,处理来自
FILE
回归popen()
. (想想看打开一个ssh
命令和尾随)。)
我想让它在用户按下 Ctrl+C
一次),所有开 ssh
进程(由线程产生)将被杀死,进程也会相应退出。
我试过一个类似于这里的顶层答案的解决方案。POSIX pthread编程
这个例子是可行的,但是,如果我把线程函数改成和我的一样(呼叫 popen()
), 这需要我打 Ctrl+C
多次. 我猜想这和以下事实有关: ssh
进程 popen()
并非无视 SIGINT
信号,允许它通过我定义的处理程序。这样做对吗?
这是我目前的代码。
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void handler(int signo) {
printf("I'm in the handler with SIGNAL %d!", signo);
exit(0);
}
void *thread(void *argument) {
// Hardcoding to "myuser@myhost.com" here for sake of example, but you
// could image the host is different per thread
FILE *file = popen("ssh myuser@myhost.com \"tail -f /path/to/some/log.log\"", "r");
if (file == null) {
printf("Error opening file\n");
exit(1);
}
char line[2048];
while (fgets(line, sizeof(line), fp) != NULL) {
printf("%s\n", line);
}
return NULL;
}
int main(void) {
// Block the SIGINT signal. The threads will inherit the signal mask.
// This will avoid them catching SIGINT instead of this thread.
sigset_t sigset, oldset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGINT);
pthread_sigmask(SIG_BLOCK, &sigset, &oldset);
// Spawn the two threads.
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, thread, &(unsigned int){1});
pthread_create(&thread2, NULL, thread, &(unsigned int){2});
// Install the signal handler for SIGINT.
struct sigaction s;
s.sa_handler = handler;
sigemptyset(&s.sa_mask);
s.sa_flags = 0;
sigaction(SIGINT, &s, NULL);
// Restore the old signal mask only for this thread.
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
// Wait for SIGINT to arrive.
pause();
// Cancel both threads.
pthread_cancel(thread1);
pthread_cancel(thread2);
// Join both threads.
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
// Done.
puts("Terminated.");
return EXIT_SUCCESS;
}
我对发生的事情的假设是否正确?如果不正确,有什么想法我做错了吗?
当你启动ssh程序时,你是在屏蔽SIGINT的情况下启动的,所以那些在管道末端的程序会忽略SIGINT。你可以通过将popen命令改为类似 "sleep 30; ls "的命令来测试这一点,启动你的程序,按ctrl-C键,然后输入 "ps s"。 你很可能会看到几行 "sh -c sleep 30; ls "和 "sleep 30 "这样的形式,以及他们的信号处置.如果你等到他们自然死亡,然后再次运行程序,但这次用 "Ctrl-\"打它。(SIGTERM),你会注意到它会立即清理。
你可以将popen行改为。
sigset_t nset;
pthread_sigmask(SIG_SETMASK, &oldset, &nset);
FILE *file = popen("ssh myuser@myhost.com \"tail -f /path/to/some/log.log\"", "r");
pthread_sigmask(SIG_SETMASK, &nset, NULL);
并把oldset的定义移到普通的位置。大多数情况下,你会得到你想要的效果,但是在这两个对pthread_sigmask()的调用之间有一个竞赛条件,一个SIGINT可能会在这个上下文中调用handler,而不是预期的那个。
要解决这个竞赛条件,你需要做的是写一个类似于popen()的函数,fork(2)然后在exec(2)调用shell之前,将子函数中的信号设置成你想要的样子。
这很不幸,但信号和线程真的不好混。
以上是关于C - SIGINT处理程序不能与多线程一起工作,每个线程都有一个popen进程。的主要内容,如果未能解决你的问题,请参考以下文章