I am using sem_wait()
as part of an IPC application, but I want the
wait to be interruptible by ctrl-C, and to continue the program if
this happens. Currently, however, any signal terminates my program.
The sem_wait
man page says "the sem_wait()
function is interruptible
by the delivery of a signal." So I am hoping to reach the line marked
###
with result == EINTR
when I press ctrl-C or otherwise send this
process a signal. But as it stands, I do not: the program just
terminates with the usual signum-dependent message.
I presumably have to change the signal handler somehow, but how? I tried defining void handler(int){}
and registering it with signal(SIGINT, handler)
. That prevents termination of course but it doesn’t magically make sem_wait()
interruptible—presumably there’s something different I should be doing during registration, or in the handler itself.
I'm on Ubuntu 20.04 LTS and macOS 10.13.6 if this makes any difference.
/* Welcome to `semtest.c`. Compile with::
*
* gcc semtest.c -o semtest # Darwin
* gcc semtest.c -o semtest -pthread # Linux
*
* Run with::
*
* ./semtest wait
*
* Terminate by either (a) sending a signal (ctrl-c or `kill`)
* or (b) running `./semtest post`
*/
#include <string.h>
#include <stdio.h>
#include <semaphore.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For S_* mode constants */
#include <unistd.h> /* For getpid() */
int main(int argc, const char * argv[])
{
sem_t * semaphore = NULL;
char cmd;
int result;
if(argc == 2 && !strcmp(argv[1], "wait")) cmd = argv[1][0];
else if(argc == 2 && !strcmp(argv[1], "post")) cmd = argv[1][0];
else return fprintf(stderr, "usage: %s wait\n or: %s post", argv[0], argv[0]);
# define SEMAPHORE_NAME "/test"
fprintf(stderr, "%s is running with PID %d\n", argv[0], getpid());
if(cmd == 'w')
{
sem_unlink(SEMAPHORE_NAME);
semaphore = sem_open(SEMAPHORE_NAME, O_CREAT, (S_IRUSR | S_IWUSR), 0);
if(semaphore == SEM_FAILED)
return fprintf(stderr, "failed to create/open semaphore \"%s\" - sem_open() failed with error %d\n", SEMAPHORE_NAME, errno);
fprintf(stderr, "waiting for semaphore \"%s\"...\n", SEMAPHORE_NAME);
errno = 0;
/* signal(SIGINT, something...? ); */
result = sem_wait(semaphore);
/* ### Want to reach this line with result==EINTR when signal arrives */
fprintf(stderr, "sem_wait() returned %d (errno is %d)\n", result, errno);
sem_close(semaphore);
}
if(cmd == 'p')
{
semaphore = sem_open(SEMAPHORE_NAME, O_CREAT, (S_IRUSR | S_IWUSR), 0);
if(semaphore == SEM_FAILED)
return fprintf(stderr,"failed to create/open semaphore \"%s\" - sem_open() failed with error %d\n", SEMAPHORE_NAME, errno);
fprintf(stderr, "posting semaphore \"%s\"\n", SEMAPHORE_NAME);
errno = 0;
result = sem_post(semaphore);
if(result) fprintf(stderr, "sem_post() failed with return code %d (errno is %d)\n", result, errno);
sem_close(semaphore);
}
return 0;
}
Using the signal()
function is the simplest way to handle the signal you want.
sighandler_t signal(int signum, sighandler_t handler);
In your case, the signum
parameter is SIGINT
(^C
) and the handle
parameter is a function you need to define according the following syntax.
/* Type of a signal handler. */
typedef void (*__sighandler_t) (int);
Declare all variables you want to access in the handler function as global.
#include <signal.h>
sem_t * semaphore = NULL;
void sigint_handler( int signal );
int main(int argc, const char * argv[]) {
signal(SIGINT, sigint_handler);
// (...)
return 0;
}
void sigint_handler( int signal ) {
// (...)
}
Although the signal()
function may work, it is recommended to use the sigaction()
function for compatibility purposes.
The behavior of signal() varies across UNIX versions, and has also varied historically across different versions of Linux. Avoid its use: use sigaction(2) instead.
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
Using sigaction()
in the context of your program should be something like this.
#include <signal.h>
sem_t * semaphore = NULL;
void sigint_handler( int signal );
int main(int argc, const char * argv[]) {
struct sigaction act = { 0 };
act.sa_handler = sigint_handler;
sigaction(SIGINT, &act, NULL);
// (...)
return 0;
}
void sigint_handler( int signal ) {
// (...)
}