Reputation: 824
As you see, This is a sample in APUE.
#include "apue.h"
static void sig_int(int sig);
int main(int argc, char **argv)
{
char buf[MAXLINE];
pid_t pid;
int status;
if (signal(SIGINT, sig_int) == SIG_ERR) //sig_int is a simple handler function
err_sys("signal error");
printf("%% ");
while (fgets(buf, MAXLINE, stdin) != NULL) {
//This is a loop to implement a simple shell
}
return 0;
}
This is the signal handler
void sig_int(int sig)
/*When I enter Ctrl+C, It'll say a got SIGSTOP, but it would terminate.*/
{
if (sig == SIGINT)
printf("got SIGSTOP\n");
}
When I enter Ctrl+C, It'll say got SIGSTOP, but it terminates right now.
Upvotes: 1
Views: 1563
Reputation: 229058
The short version is that the signal interrupts the current system call. You're doing fgets()
, which likely now blocks in a read()
system-call. The read()
call is interrupted, it returns -1 and sets errno
to EINTR
.
This causes fgets
to return NULL, your loop ends, and the program is finished.
glibc on linux implements two different concepts for signal()
. One where system calls are automatically restarted across signals, and one where they are not.
When a signal occurs and the process is blocked in a system call, the system call is interrupted("cancelled"). Execution resumes in the user space application, and the signal handler occurs. The interrupted system call will return an error, and set errno to EINTR
.
What happens next depends on whether system calls are restarted or not across signals.
If system calls are restartable, the runtime (glibc) simply retries the system call. For the read()
system call, this would be similar to read()
being implemented as:
ssize_t read(int fd, void *buf, size_t len)
{
ssize_t sz;
while ((sz = syscall_read(fd, buf, len)) == -1
&& errno == EINTR);
return sz;
}
If system calls are not automatically restarted, read()
would behave similar to:
ssize_t read(int fd, void *buf, size_t len)
{
ssize_t sz;
sz = syscall_read(fd, buf, len));
return sz;
}
In the latter case it would be up to your application to check whether read() failed because it was interrupted by a signal. And it is up you, to determine if read()
just failed temporarily due to a signal getting handled, and it's up you you to re-try the read()
call
By using sigaction()
instead of signal()
, you get control over
whether system calls are restared or not. The relevant flag you specify with sigaction()
is
SA_RESTART Provide behavior compatible with BSD signal semantics by making certain system calls restartable across signals. This flag is meaningful only when establishing a sig‐ nal handler. See signal(7) for a discussion of system call restarting.
If you use signal()
, it depends on what semantics you want. As seen in the description of SA_RESTART
, if it is BSD signal semantics, system calls are restarted. This is the default behavior in glibc.
Another difference is that BSD semantics leave the signal handler installed by signal()
installed after a signal is handled. SVR4 semantics uninstalls the signal handler, and your signal handler will have to re-install the handler if you want to catch more signals.
The "apue.h" however, defines the macro _XOPEN_SOURCE 600
before including <signal.h>
. This will cause signal() to have SVR4 semantics, where system calls are not restarted. Which will cause your fgets() call to "fail".
Due to all these differences in behavior, use sigaction() instead of signal. sigaction() lets you control what happens instead of having the semantics change based on a (possibly) hidden #define
as is the case with signal()
Upvotes: 6