简体   繁体   English

如何使信号中断 sem_wait() 但不终止进程?

[英]How to make a signal interrupt sem_wait() but not terminate the process?

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.我正在使用sem_wait()作为 IPC 应用程序的一部分,但我希望等待可以被 ctrl-C 中断,并在发生这种情况时继续程序。 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." sem_wait手册页说“ sem_wait() function 可以通过传递信号来中断。” So I am hoping to reach the line marked ### with result == EINTR when I press ctrl-C or otherwise send this process a signal.因此,当我按 ctrl-C 或以其他方式向该过程发送信号时,我希望到达标记为###result == EINTR的行。 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) .我尝试定义void handler(int){}并将其注册到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.这当然可以防止终止,但它不会神奇地使sem_wait()可中断——大概在注册期间或在处理程序本身中我应该做一些不同的事情。

I'm on Ubuntu 20.04 LTS and macOS 10.13.6 if this makes any difference.如果这有什么不同,我在 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;
}

Using the signal() function is the simplest way to handle the signal you want.使用signal() function 是处理所需信号的最简单方法。

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.在您的情况下, signum参数是SIGINT ( ^C ), handle参数是 function 您需要根据以下语法定义。

/* Type of a signal handler.  */
typedef void (*__sighandler_t) (int);

Declare all variables you want to access in the handler function as global.将要在处理程序 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 ) {
    // (...)
}

Although the signal() function may work, it is recommended to use the sigaction() function for compatibility purposes.尽管signal() function 可能有效,但出于兼容性目的,建议使用sigaction() function。

The behavior of signal() varies across UNIX versions, and has also varied historically across different versions of Linux. signal() 的行为因 UNIX 版本而异,并且在历史上因 Linux 的不同版本而异。 Avoid its use: use sigaction(2) instead.避免使用它:使用 sigaction(2) 代替。

int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);

Using sigaction() in the context of your program should be something like this.在程序的上下文中使用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 ) {
    // (...)
}

Calls only return EINTR if you have registered a signal handler for the signal in question.如果您已为相关信号注册了信号处理程序,则调用仅返回EINTR

Demo:演示:

$ 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

Yeah, this is Perl, but this is not a language-specific matter.是的,这是 Perl,但这不是特定于语言的问题。 You'll get the same results in C.您将在 C 中获得相同的结果。

Yeah, this is read , but this is a call-specific matter.是的,这是read ,但这是特定于呼叫的问题。 You'll get the same results for sem_wait .您将获得与sem_wait相同的结果。

This was just easier to write.这只是更容易写。

( $SIG{INT} =... results in a call to sigaction , and sysread is a thin wrapper around read . Note that $ARGV[0] is akin to argv[1] .) $SIG{INT} =...导致调用sigaction ,而sysread是对read的精简包装。请注意$ARGV[0]类似于argv[1] 。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM