prog2
不退出CTRL+D
。为什么然后prog1
退出CTRL+D
?更奇怪的是,信号处理程序不执行任何操作,尽管它以某种方式影响最终结果......
下面两个程序的区别仅在于prog1.c
sigaction()
使用了in,以及prog2.c
signal()
使用了in:
@@ -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);
Run Code Online (Sandbox Code Playgroud)
每个程序都只是打开一个环回 tty 并将其自身分成两个进程,其中一个进程从 tty 读取响应,另一个进程将数据写入 tty 设备。然后将从环回tty虚拟设备接收到的数据输出到控制终端。
编译prog1
并prog2
使用-lutil
选项。启动每个程序并输入hello<CTRL+D>
. 这会产生以下输出:
$ ./prog1
hello$
$ ./prog2
hello
Run Code Online (Sandbox Code Playgroud)
顺便说一句,应该设置哪些标志sigaction()
来复制 的行为signal()
?
以下是这些程序:
程序1.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;
}
Run Code Online (Sandbox Code Playgroud)
程序2.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;
}
Run Code Online (Sandbox Code Playgroud)
小智 5
行为上的差异很可能是因为signal
行为就像SA_RESTART
在 上设置了标志一样sigaction
。来自signal(2)手册页:
BSD 语义相当于使用以下标志调用 sigaction(2):
Run Code Online (Sandbox Code Playgroud)sa.sa_flags = SA_RESTART;
Linux上的情况如下:
内核的 signal() 系统调用提供 System V 语义。
默认情况下,在 glibc 2 及更高版本中,signal() 包装函数不会调用内核系统调用。相反,它使用提供 BSD 语义的标志来调用 sigaction(2)...
使用该SA_RESTART
标志时,某些系统调用会自动重新启动。当不使用它时,调用将返回错误,并将 errno 设置为EINTR
。
因此在“从环回读取”过程中会prog1
发生以下情况:
read
read
被阻止的系统调用返回-1while
条件退出循环,进程退出。在 中prog2
,该SA_RESTART
行为意味着在 (2) 中运行信号处理程序后,read
重新启动调用。
要使prog1
行为类似于prog2
,请设置SA_RESTART
:
sa.sa_flags = SA_RESTART;
Run Code Online (Sandbox Code Playgroud)
有关行为的更多详细信息,请参阅signal(7)手册页的“信号处理程序中断系统调用和库函数”部分。SA_RESTART