繁体   English   中英

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

[英]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.

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