【发布时间】:2015-01-05 22:31:40
【问题描述】:
当程序的主线程被read() 系统调用阻塞时,我试图手动中断它。我在第二个线程中通过调用pthread_kill() 执行此操作,但是发生了分段错误。但是,如果我在第二个线程中调用read(),即不是主线程并从主线程调用pthread_kill(),那么一切都会按预期工作。
例如,以下代码导致分段错误,我在第二个线程中调用pthread_kill(),大约在它启动后 2 秒。它使用通过(在主线程中)调用pthread_self()获得的主线程的pthread_t:
示例 1
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <unistd.h>
#include <signal.h>
static int fd = 0;
unsigned char buf[255];
static pthread_t s;
void sigHandler(int sig){
printf("Signal handler called.\n");
}
void * closeFD(void *arg){
printf("Second thread started.\n");
sleep(2);
int r = pthread_kill(s, SIGUSR1);
}
int main(char *argv[], int argc){
struct termios newtio;
pthread_t t1;
unsigned char buf[255];
void *res;
struct sigaction int_handler = {.sa_handler=sigHandler};
sigaction(SIGUSR1,&int_handler,0);
s = pthread_self();
printf("Process id is: %d.\n", getpid());
fd = open("/dev/ttyS0", O_RDONLY | O_NOCTTY);
if (fd != -1){
bzero(&newtio, sizeof(newtio));
newtio.c_cflag = B2400 | CS7 | CLOCAL | CREAD ;
newtio.c_iflag = ICRNL;
newtio.c_oflag = 0;
newtio.c_lflag = ~ICANON;
newtio.c_cc[VMIN] = 14;
tcsetattr(fd,TCSANOW,&newtio);
pthread_create(&t1, NULL, closeFD, NULL);
printf("Reading ..\n");
read(fd,buf,255);
close(fd);
}
return 0;
}
以下代码是相同的,只是我将调用 read() 放在第二个线程(在 closeFD() 中)并按预期工作。第二个线程解除阻塞并终止,而主线程等待它退出然后自行退出。
示例 2:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <unistd.h>
#include <signal.h>
static int fd = 0;
unsigned char buf[255];
static pthread_t s;
void sigHandler(int sig){
printf("Signal handler called.\n");
}
void * closeFD(void *arg){
printf("Second thread started.\n");
read(fd,buf,255);
printf("Read interrupted.\n");
}
int main(char *argv[], int argc){
struct termios newtio;
pthread_t t1;
unsigned char buf[255];
void *res;
struct sigaction int_handler = {.sa_handler=sigHandler};
sigaction(SIGUSR1,&int_handler,0);
s = pthread_self();
printf("Process id is: %d.\n", getpid());
fd = open("/dev/ttyS0", O_RDONLY | O_NOCTTY);
if (fd != -1){
bzero(&newtio, sizeof(newtio));
newtio.c_cflag = B2400 | CS7 | CLOCAL | CREAD ;
newtio.c_iflag = ICRNL;
newtio.c_oflag = 0;
newtio.c_lflag = ~ICANON;
newtio.c_cc[VMIN] = 14;
tcsetattr(fd,TCSANOW,&newtio);
pthread_create(&t1, NULL, closeFD, NULL);
sleep(2);
int r = pthread_kill(t1, SIGUSR1);
pthread_join(t1, &res);
close(fd);
}
return 0;
}
到目前为止,我还没有找到具体的参考资料,说明从第二个(在同一进程中)终止主线程是非法操作,那么我做错了什么吗?
更新 #1
感谢所有回复的人,但是我应该澄清几点:
- 我知道在信号处理程序中使用
printf是不安全的,但这是一个示例,它不是分段错误的原因,尽管它是一个有效点。将printf()从信号处理程序中取出仍然会导致分段错误。示例 2 与信号处理程序中的printf()一起使用。 - 我知道发送 SIGUSR 不会终止程序。但是,通过使用
pthread_kill(pthread_t thread, int signal),它会向线程thread发送一个信号,它会解除阻塞(如果它确实被阻塞了)。这是我想要的操作,这是示例 2 中实际发生的情况,这是我理解 应该 在任一示例中发生的情况,但在示例 1 中不会发生。 - 在描述示例 1 时,当我指的是“线程”时,我使用了术语“方法”,其中提到了对
pthread_kill()的调用。
进一步,引自“使用 POSIX 线程编程”,David R. Butenhof,第 6.6.3 节 p217 'pthread_kill':
在一个进程中,一个线程可以向一个特定线程发送信号 (包括它自己)调用
pthread_kill。
话虽如此,以下示例还给出了分段错误:
示例 3
#include <stdio.h>
#include <string.h>
#include <string.h>
#include <signal.h>
static pthread_t s;
int value = 0;
void sigHandler(int sig){
value = 1;
}
int main(char *argv[], int argc){
struct sigaction int_handler = {.sa_handler=sigHandler};
sigaction(SIGUSR1,&int_handler,0);
s = pthread_self();
printf("The value of 'value' is %d.\n", value);
printf("Process id is: %d.\n", getpid());
int r = pthread_kill(s, SIGUSR1);
printf("The value of 'value' is %d.\n", value);
return 0;
}
如果不是对sigaction() 的调用被替换为对signal() 的(不可移植的)调用,这也会失败。考虑到第三个示例,非常很简单,我无法找到任何明确声明这是非法行为的文档。事实上,引用的参考表明它是允许的!
【问题讨论】:
-
AFAIK
printf不是异步信号安全的,因此您的两个程序都有未定义的行为。 (并不是说这会改变您观察到的行为。) -
我试图重现这个问题,但我无法访问我正在使用的系统上的 /dev/ttyS0。所以我尝试让主线程执行
sleep(10)而不是从文件中读取,它正确地运行信号处理程序而没有段错误。如果没有从 /dev/ttyS0 读取主线程,是否还会出现段错误? -
这一行,在线程中:'int r = pthread_kill(s, SIGUSR1);'将杀死当前线程,而不是主线程。建议使用“pthread_exit(null)”之类的东西来退出当前线程。 main() 可能正在做类似 pthread_join(s);等待线程完成
-
大家好,请参阅我的原始帖子,其中我添加了更多信息和另一个失败的示例。