[英]How to make a signal interrupt sem_wait() but not terminate the process?
我正在使用sem_wait()
作为 IPC 应用程序的一部分,但我希望等待可以被 ctrl-C 中断,并在发生这种情况时继续程序。 但是,目前,任何信号都会终止我的程序。
sem_wait
手册页说“ sem_wait()
function 可以通过传递信号来中断。” 因此,当我按 ctrl-C 或以其他方式向该过程发送信号时,我希望到达标记为###
且result == EINTR
的行。 但就目前而言,我不这样做:程序只是以通常的信号相关消息终止。
我大概必须以某种方式更改信号处理程序,但是如何? 我尝试定义void handler(int){}
并将其注册到signal(SIGINT, handler)
。 这当然可以防止终止,但它不会神奇地使sem_wait()
可中断——大概在注册期间或在处理程序本身中我应该做一些不同的事情。
如果这有什么不同,我在 Ubuntu 20.04 LTS 和 macOS 10.13.6 上。
/* 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;
}
使用signal()
function 是处理所需信号的最简单方法。
sighandler_t signal(int signum, sighandler_t handler);
在您的情况下, signum
参数是SIGINT
( ^C
), handle
参数是 function 您需要根据以下语法定义。
/* Type of a signal handler. */
typedef void (*__sighandler_t) (int);
将要在处理程序 function 中访问的所有变量声明为全局变量。
#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 ) {
// (...)
}
尽管signal()
function 可能有效,但出于兼容性目的,建议使用sigaction()
function。
signal() 的行为因 UNIX 版本而异,并且在历史上因 Linux 的不同版本而异。 避免使用它:使用 sigaction(2) 代替。
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
在程序的上下文中使用sigaction()
应该是这样的。
#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 ) {
// (...)
}
如果您已为相关信号注册了信号处理程序,则调用仅返回EINTR
。
演示:
$ perl -M5.010 -e'
$SIG{INT} = sub { } if $ARGV[0];
sysread(STDIN, $buf, 1) or warn $!;
say "ok";
' 0
^C
$ echo $?
130 # Killed by SIGINT
$ perl -M5.010 -e'
$SIG{INT} = sub { } if $ARGV[0];
sysread(STDIN, $buf, 1) or warn $!;
say "ok";
' 1
^CInterrupted system call at -e line 3.
ok
是的,这是 Perl,但这不是特定于语言的问题。 您将在 C 中获得相同的结果。
是的,这是read
,但这是特定于呼叫的问题。 您将获得与sem_wait
相同的结果。
这只是更容易写。
( $SIG{INT} =...
导致调用sigaction
,而sysread
是对read
的精简包装。请注意$ARGV[0]
类似于argv[1]
。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.