Reputation: 1579
prog2
does not exit on CTRL+D
. Why prog1
exits on CTRL+D
then? More strange is that the signal handler does not perform any action, although it influences the end result somehow...
The following two programs differ only with that in prog1.c
sigaction()
is used, and in prog2.c
signal()
is used:
@@ -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);
Each of this programs simply opens a loopback tty and splits itself into two processes, one of which reads response from tty, and another writes data to tty device. The data received from the loopback tty virtual device is then output to the controlling terminal.
Compile prog1
and prog2
using -lutil
option. Start each program and type hello<CTRL+D>
. This results in the following output:
$ ./prog1
hello$
$ ./prog2
hello
BTW, which flags should be set in sigaction()
to duplicate the behavior of signal()
?
Here are the programs:
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;
}
Upvotes: 1
Views: 1739
Reputation: 336
The difference in behavior is likely because signal
behaves as if the SA_RESTART
flag was set on sigaction
. From the signal(2) manual page:
The BSD semantics are equivalent to calling sigaction(2) with the following flags:
sa.sa_flags = SA_RESTART;
The situation on Linux is as follows:
The kernel's signal() system call provides System V semantics.
By default, in glibc 2 and later, the signal() wrapper function does not invoke the kernel system call. Instead, it calls sigaction(2) using flags that supply BSD semantics...
When the SA_RESTART
flag is used some system calls are automatically restarted. When it is not used the call will return an error with errno set to EINTR
.
Thus in the "read from loopback" process in prog1
the following happens:
read
read
system call it was blocked in returns -1 while
condition and the process exits.In prog2
, the SA_RESTART
behavior means that after the signal handler is run in (2), the read
call is restarted.
To make prog1
behave like prog2
, set SA_RESTART
:
sa.sa_flags = SA_RESTART;
See the "Interruption of system calls and library functions by signal handlers" section of the signal(7) manual page for more detail on SA_RESTART
's behavior.
Upvotes: 5