prog2
未在CTRL+D
退出。为什么prog1
会在CTRL+D
退出?更奇怪的是,信号处理程序不会执行任何操作,尽管它会以某种方式影响最终结果......
以下两个程序仅与prog1.c
sigaction()
中使用的程序不同,并且使用了prog2.c
signal()
:
@@ -39,10 +39,7 @@
/* read from loopback tty */
if (cpid > 0) {
/* this is the strange part */
- struct sigaction sa;
- sa.sa_handler = child_handler;
- sa.sa_flags = 0;
- sigaction(SIGCHLD, &sa, NULL);
+ signal(SIGCHLD, child_handler);
struct termios tty;
tcgetattr(fd, &tty);
这些程序中的每一个都只是打开一个环回tty并将自身分成两个进程,其中一个进程从tty读取响应,另一个进程将数据写入tty设备。然后,从环回tty虚拟设备接收的数据输出到控制终端。
使用prog1
选项编译prog2
和-lutil
。启动每个程序并键入hello<CTRL+D>
。这导致以下输出:
$ ./prog1
hello$
$ ./prog2
hello
BTW,应在sigaction()
中设置哪些标记以复制signal()
的行为?
以下是计划:
prog1.c的
#include <termios.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <pty.h>
int fd;
void child_handler(int s)
{
(void) s;
}
int main(void)
{
char c;
/* open loopback tty device */
pid_t lpid;
lpid = forkpty(&fd, NULL, NULL, NULL);
if (lpid == -1) {
exit(1);
}
if (lpid == 0) {
char *args[] = { "cat", NULL };
execv("/bin/cat", args);
}
/* create parallel process */
pid_t cpid;
cpid = fork();
if (cpid == -1) {
close(fd);
exit(1);
}
/* read from loopback tty */
if (cpid > 0) {
/* this is the strange part */
struct sigaction sa;
sa.sa_handler = child_handler;
sa.sa_flags = 0;
sigaction(SIGCHLD, &sa, NULL);
struct termios tty;
tcgetattr(fd, &tty);
cfmakeraw(&tty);
tcsetattr(fd, TCSANOW, &tty);
while (read(fd, &c, 1) != -1)
write(STDOUT_FILENO, &c, 1);
}
/* write to loopback tty */
if (cpid == 0) {
struct termios stdtio_restore;
struct termios stdtio;
tcgetattr(STDIN_FILENO, &stdtio_restore);
tcgetattr(STDIN_FILENO, &stdtio);
cfmakeraw(&stdtio);
tcsetattr(STDIN_FILENO, TCSANOW, &stdtio);
while (1) {
read(STDIN_FILENO, &c, 1);
if (c == 0x04) break;
write(fd, &c, 1);
}
tcsetattr(0, TCSANOW, &stdtio_restore);
close(fd);
exit(0);
}
return 0;
}
prog2.c
#include <termios.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <pty.h>
int fd;
void child_handler(int s)
{
(void) s;
}
int main(void)
{
char c;
/* open loopback tty device */
pid_t lpid;
lpid = forkpty(&fd, NULL, NULL, NULL);
if (lpid == -1) {
exit(1);
}
if (lpid == 0) {
char *args[] = { "cat", NULL };
execv("/bin/cat", args);
}
/* create parallel process */
pid_t cpid;
cpid = fork();
if (cpid == -1) {
close(fd);
exit(1);
}
/* read from loopback tty */
if (cpid > 0) {
/* this is the strange part */
signal(SIGCHLD, child_handler);
struct termios tty;
tcgetattr(fd, &tty);
cfmakeraw(&tty);
tcsetattr(fd, TCSANOW, &tty);
while (read(fd, &c, 1) != -1)
write(STDOUT_FILENO, &c, 1);
}
/* write to loopback tty */
if (cpid == 0) {
struct termios stdtio_restore;
struct termios stdtio;
tcgetattr(STDIN_FILENO, &stdtio_restore);
tcgetattr(STDIN_FILENO, &stdtio);
cfmakeraw(&stdtio);
tcsetattr(STDIN_FILENO, TCSANOW, &stdtio);
while (1) {
read(STDIN_FILENO, &c, 1);
if (c == 0x04) break;
write(fd, &c, 1);
}
tcsetattr(0, TCSANOW, &stdtio_restore);
close(fd);
exit(0);
}
return 0;
}
答案 0 :(得分:4)
行为上的差异可能是因为signal
的行为就像在SA_RESTART
上设置sigaction
标志一样。从signal(2)手册页:
BSD语义等同于用sigaction(2)调用 以下标志:
sa.sa_flags = SA_RESTART;
Linux的情况如下:
内核的signal()系统调用提供了System V语义。
默认情况下,在glibc 2及更高版本中,signal()包装函数不会调用内核系统调用。相反,它打电话 sigaction(2)使用提供BSD语义的标志...
使用SA_RESTART
标志时,会自动重启某些系统调用。如果未使用,则调用将返回错误,并将错误号设置为EINTR
。
因此,在prog1
中的“从环回读取”过程中会发生以下情况:
read
read
系统调用它被阻止返回-1 while
条件退出,并退出流程。在prog2
中,SA_RESTART
行为意味着在(2)中运行信号处理程序后,read
调用将重新启动。
要使prog1
的行为与prog2
相同,请设置SA_RESTART
:
sa.sa_flags = SA_RESTART;
有关SA_RESTART
行为的更多详细信息,请参阅signal(7)手册页中的“信号处理程序中断系统调用和库函数”部分。