如何使信号中断 sem_wait() 但不终止进程?
How to make a signal interrupt sem_wait() but not terminate the process?
我正在使用 sem_wait()
作为 IPC 应用程序的一部分,但我想要
等待被 ctrl-C 中断,并在以下情况下继续程序
有时候是这样的。然而,目前,任何信号都会终止我的程序。
sem_wait
手册页说“sem_wait()
函数是可中断的
通过传递信号。”所以我希望到达标有
###
和 result == EINTR
当我按 ctrl-C 或以其他方式发送时
处理信号。但就目前而言,我没有:该程序只是
以通常的符号相关消息终止。
我大概必须以某种方式更改信号处理程序,但是如何更改?我尝试定义 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;
}
仅调用 return 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]
.)
使用signal()
函数是处理您想要的信号的最简单方法。
sighandler_t signal(int signum, sighandler_t handler);
在您的例子中,signum
参数是 SIGINT
(^C
),handle
参数是您需要根据以下语法定义的函数。
/* Type of a signal handler. */
typedef void (*__sighandler_t) (int);
将您要在处理函数中访问的所有变量声明为全局变量。
#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()
功能可能有效,但出于兼容性考虑,建议使用sigaction()
功能。
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);
在您的程序上下文中使用 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 ) {
// (...)
}
我正在使用 sem_wait()
作为 IPC 应用程序的一部分,但我想要
等待被 ctrl-C 中断,并在以下情况下继续程序
有时候是这样的。然而,目前,任何信号都会终止我的程序。
sem_wait
手册页说“sem_wait()
函数是可中断的
通过传递信号。”所以我希望到达标有
###
和 result == EINTR
当我按 ctrl-C 或以其他方式发送时
处理信号。但就目前而言,我没有:该程序只是
以通常的符号相关消息终止。
我大概必须以某种方式更改信号处理程序,但是如何更改?我尝试定义 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;
}
仅调用 return 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]
.)
使用signal()
函数是处理您想要的信号的最简单方法。
sighandler_t signal(int signum, sighandler_t handler);
在您的例子中,signum
参数是 SIGINT
(^C
),handle
参数是您需要根据以下语法定义的函数。
/* Type of a signal handler. */
typedef void (*__sighandler_t) (int);
将您要在处理函数中访问的所有变量声明为全局变量。
#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()
功能可能有效,但出于兼容性考虑,建议使用sigaction()
功能。
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);
在您的程序上下文中使用 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 ) {
// (...)
}