部分man:
NAME
sigaction, rt_sigaction - examine and change a signal action
SYNOPSIS
#include
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
sigaction(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE
siginfo_t: _POSIX_C_SOURCE >= 199309L
DESCRIPTION
The sigaction() system call is used to change the action taken by a process on receipt of a specific signal. (See signal(7) for an overview of signals.)
signum specifies the signal and can be any valid signal except SIGKILL and SIGSTOP.
If act is non-NULL, the new action for signal signum is installed from act. If oldact is non-NULL, the previous action is saved in oldact.
The sigaction structure is defined as something like:
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
On some architectures a union is involved: do not assign to both sa_handler and sa_sigaction.
The sa_restorer field is not intended for application use. (POSIX does not specify a sa_restorer field.) Some further details of purpose of this field can be
found in sigreturn(2).
sa_handler specifies the action to be associated with signum and may be SIG_DFL for the default action, SIG_IGN to ignore this signal, or a pointer to a signal han‐
dling function. This function receives the signal number as its only argument.
If SA_SIGINFO is specified in sa_flags, then sa_sigaction (instead of sa_handler) specifies the signal-handling function for signum. This function receives the
signal number as its first argument, a pointer to a siginfo_t as its second argument and a pointer to a ucontext_t (cast to void *) as its third argument. (Com‐
monly, the handler function doesn't make any use of the third argument. See getcontext(3) for further information about ucontext_t.)
sa_mask specifies a mask of signals which should be blocked (i.e., added to the signal mask of the thread in which the signal handler is invoked) during execution
of the signal handler. In addition, the signal which triggered the handler will be blocked, unless the SA_NODEFER flag is used.
sa_flags specifies a set of flags which modify the behavior of the signal. It is formed by the bitwise OR of zero or more of the following:
SA_NOCLDSTOP
If signum is SIGCHLD, do not receive notification when child processes stop (i.e., when they receive one of SIGSTOP, SIGTSTP, SIGTTIN, or SIGTTOU) or
resume (i.e., they receive SIGCONT) (see wait(2)). This flag is meaningful only when establishing a handler for SIGCHLD.
SA_NOCLDWAIT (since Linux 2.6)
If signum is SIGCHLD, do not transform children into zombies when they terminate. See also waitpid(2). This flag is meaningful only when establishing a
handler for SIGCHLD, or when setting that signal's disposition to SIG_DFL.
If the SA_NOCLDWAIT flag is set when establishing a handler for SIGCHLD, POSIX.1 leaves it unspecified whether a SIGCHLD signal is generated when a child
process terminates. On Linux, a SIGCHLD signal is generated in this case; on some other implementations, it is not.
SA_NODEFER
Do not prevent the signal from being received from within its own signal handler. This flag is meaningful only when establishing a signal handler.
SA_NOMASK is an obsolete, nonstandard synonym for this flag.
SA_ONSTACK
Call the signal handler on an alternate signal stack provided by sigaltstack(2). If an alternate stack is not available, the default stack will be used.
This flag is meaningful only when establishing a signal handler.
SA_RESETHAND
Restore the signal action to the default upon entry to the signal handler. This flag is meaningful only when establishing a signal handler. SA_ONESHOT
is an obsolete, nonstandard synonym for this flag.
SA_RESTART
Provide behavior compatible with BSD signal semantics by making certain system calls restartable across signals. This flag is meaningful only when
establishing a signal handler. See signal(7) for a discussion of system call restarting.
SA_RESTORER
Not intended for application use. This flag is used by C libraries to indicate that the sa_restorer field contains the address of a "signal trampoline".
See sigreturn(2) for more details.
SA_SIGINFO (since Linux 2.2)
The signal handler takes three arguments, not one. In this case, sa_sigaction should be set instead of sa_handler. This flag is meaningful only when
establishing a signal handler.
简单总结:
参数 :
该函数的第一个参数为信号的值,可以为除sigkill及sigstop外的任何一 个特定有效的信号(为这两个信号定义自己的处理函数,将导致信号安装错误)
第二个参数是指向结构sigaction的一个实例的指针,在结构 sigaction的实例中,指定了对特定信号的处理,可以为空,进程会以缺省方式对信号处理
第三个参数oldact指向的对象用来保存原来对相应信号的处理,可以传入结构sigaction的指针来获取之前对信号处理情况,如果不需要保存可指定oldact为null。
如果把第二、第三个参数都设为NULL,那么该函数可用于检查信号的有效性。
第二个参数最为重要,其中包含了对指定信号的处理、信号所传递的信息、信号处理函数执行过程中应屏蔽掉哪些函数等等。
返回值:函数成功返回0,失败返回-1
在sigaction函数的第二个第三个参数,为指向sigaction这个结构体类型的指针
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
1. sa_handler指定信号关联函数
sa_handler指定信号关联函数,赋值为一个函数指针(函数名)即用户指定的信号处理函数。除此之外,还可以赋值为常数SIG_IGN表示忽略信号,赋值为常数SIG_DFL表示执行系统默认动作(采用缺省的处理方式)。
赋值为一个函数指针表示用自定义函数捕捉信号,或者说向内核注册了一个信号处理函数,该函数返回值为void,可以带一个int参数,通过参数可以得知当前信号的编号,这样就可以用同一个函数处理多种信号。显然,这也是一个回调函数,不是被main函数调用,而是被系统所调用。
2. sa_sigaction指定信号关联函数
和sa_handler的差别在于:
由sa_handler指定的处理函数只有一个参数,即信号值,所以信号不能传递除信号值之外的任何信息;
(例如上一篇的void signal_fun(int num))
由_sa_sigaction是指定的信号处理函数带有三个参数,是为实时信号而设的(当然同样支持非实时信号),它指定一个3参数信号处理函数。第一个参数为信号值,第二个参数是指向siginfo_t结构的指针,结构中包含信号携带的数据值,第三个参数没有使用(posix没有规范使用该参数的标准)
例如void signal_handle_new(int num,siginfo_t *info,void *d)
sa_handler主要用于不可靠信号(实时信号当然也可以,只是不能带信息),sa_sigaction用于实时信号可以带信息(siginfo_t),两者不能同时出现。
3. sa_mask存放需要手动屏蔽的信号
sa_mask指定在信号处理程序执行过程中,哪些信号应当被阻塞。缺省情况下当前信号本身被阻塞,防止信号的嵌套发送,除非指定SA_NODEFER或者SA_NOMASK标志位,处理程序执行完后,被阻塞的信号开始执行。
注:请注意sa_mask指定的信号阻塞的前提条件,是在由sigaction()安装信号的处理函数执行过程中由sa_mask指定的信号才被阻塞。
(屏蔽相关内容将在下一篇中带来)
4.sa_flags指定一组修改信号行为的标志
flag在man文档中有详细列出,常用的有:
SA_SIGINFO:
当sig_act.sa_flags = SA_SIGINFO;
时需要指定sa_sigaction。a_sigaction函数的第一个参数与sa_handler一样表示当前信号的编号,第二个参数是一个siginfo_t 结构体,第三个参数一般不用。当使用sa_handler时sa_flags设置为0即可。
If SA_SIGINFO is specified in sa_flags, then sa_sigaction (instead of sa_handler) specifies the signal-handling function for signum.
This function receives the signal number as its first argument, a pointer to a siginfo_t as its second argument and a pointer to a ucontext_t (cast to void *)as its third argument. (Commonly, the handler function doesn’t make any use of the third argument. See getcontext(3) for further information aboutucontext_t.)
……
SA_SIGINFO (since Linux 2.2)
The signal handler takes three arguments, not one. In this case, sa_sigaction should be set instead of sa_handler. This flag is meaningful only when establishing a signal handler.
注:很多文献在阐述该标志位时都认为,如果设置了该标志位,就必须定义三参数信号处理函数。实际不是这样的,验证方法很简单:自己实现一个单一参数信号处理函数,并在程序中设置该标志位,可以察看程序的运行结果。实际上,可以把该标志位看成信号是否传递参数的开关,如果设置该位,则传递参数;否则,不传递参数。
SA_NODEFER:
当SA_NODEFER设置时在信号处理函数执行期间不会屏蔽当前信号;
SA_NODEFER
Do not prevent the signal from being received from within its own signal handler. This flag is meaningful only when establishing a signal handler.
SA_NOMASK is an obsolete, nonstandard synonym for this flag.
5.sa_restorer
已过时,POSIX不支持它,不应再被使用。
在man sigaction 中对siginfo_t 结构体也作了说明
The siginfo_t argument to sa_sigaction is a struct with the following fields:
siginfo_t {
int si_signo; /* Signal number 信号编号 */
int si_errno; /* An errno value 如果为非零值则错误代码与之关联 */
int si_code; /* Signal code 说明进程如何接收信号以及从何处收到*/
int si_trapno; /* Trap number that caused
hardware-generated signal
(unused on most architectures) */
pid_t si_pid; /* Sending process ID适用于SIGCHLD,代表被终止进程的PID */
uid_t si_uid; /* Real user ID of sending process适用于SIGCHLD,代表被终止进程所拥有进程的UID */
int si_status; /* Exit value or signal 适用于SIGCHLD,代表被终止进程的状态 */
clock_t si_utime; /* User time consumed 适用于SIGCHLD,代表被终止进程所消耗的用户时间 */
clock_t si_stime; /* System time consumed 适用于SIGCHLD,代表被终止进程所消耗系统的时间 */
==================
sigval_t si_value; /* Signal value */
==================
int si_int; /* POSIX.1b signal */
void *si_ptr; /* POSIX.1b signal */
int si_overrun; /* Timer overrun count;
POSIX.1b timers */
int si_timerid; /* Timer ID; POSIX.1b timers */
void *si_addr; /* Memory location which caused fault */
long si_band; /* Band event (was int in
glibc 2.3.2 and earlier) */
int si_fd; /* File descriptor */
short si_addr_lsb; /* Least significant bit of address
(since Linux 2.6.32) */
void *si_call_addr; /* Address of system call instruction
(since Linux 3.5) */
int si_syscall; /* Number of attempted system call
(since Linux 3.5) */
unsigned int si_arch; /* Architecture of attempted system call
(since Linux 3.5) */
}
请留意两个用于传输数据的变量:
sigval_t si_value; /* Signal value /
int si_int; / POSIX.1b signal */
实际上:
void signal_handle_new(int num,siginfo_t *info,void *d)
读取第二个参数中对应变量的值来获取信号所带参数,通过这种方法实现进程间通讯man:
NAME
sigqueue - queue a signal and data to a process
SYNOPSIS
#include
int sigqueue(pid_t pid, int sig, const union sigval value);
Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
sigqueue(): _POSIX_C_SOURCE >= 199309L
DESCRIPTION
sigqueue() sends the signal specified in sig to the process whose PID is given in pid. The permissions required to send a signal are the same as for kill(2). As
with kill(2), the null signal (0) can be used to check if a process with a given PID exists.
The value argument is used to specify an accompanying item of data (either an integer or a pointer value) to be sent with the signal, and has the following type:
union sigval {
int sival_int;
void *sival_ptr;
};
If the receiving process has installed a handler for this signal using the SA_SIGINFO flag to sigaction(2), then it can obtain this data via the si_value field of
the siginfo_t structure passed as the second argument to the handler. Furthermore, the si_code field of that structure will be set to SI_QUEUE.
RETURN VALUE
On success, sigqueue() returns 0, indicating that the signal was successfully queued to the receiving process. Otherwise, -1 is returned and errno is set to indi‐
cate the error.
ERRORS
EAGAIN The limit of signals which may be queued has been reached. (See signal(7) for further information.)
EINVAL sig was invalid.
EPERM The process does not have permission to send the signal to the receiving process. For the required permissions, see kill(2).
ESRCH No process has a PID matching pid.
参数:
sigqueue()比kill()传递了更多的附加信息,但sigqueue()只能向一个进程发送信号,而不能发送信号给一个进程组。
同样可以使用传送sig=0的方式,检查指定pid进程是否存在
The value argument is used to specify an accompanying item of data (either an integer or a pointer value) to be sent with the signal, and has the following type:
union sigval {
int sival_int;
void *sival_ptr;
};
If the receiving process has installed a handler for this signal using the SA_SIGINFO flag to sigaction(2), then it can obtain this data via the si_value field of
the siginfo_t structure passed as the second argument to the handler. Furthermore, the si_code field of that structure will be set to SI_QUEUE.
实际开发中我们将要通过信号传输的值赋值入sigval,即可在siginfo_t 中获得到
linux系统是无法预知信号接收的时机的,那么就有可能出现这样的情况:
linux在执行一个信号对应的自定义信号处理函数的时候,接收到了一个新的信号,那应该怎么处理呢?
分为两种情况:
#include
#include //unix stand lib
#include
#include
#include
#include
#include
#include
#include //file dir
#include //wait func
#include //ststem
#include
using namespace std;
//新的信号处理函数多了两个参数,其中signfo_t里面携带了我们想要的数据
void signal_handle_new(int num, siginfo_t *info, void *d)
{
cout << "get value=" << info->si_int << endl;
}
int main(int argc, char *argv[])
{
//安装信号先于发送信号
struct sigaction sig_act;//新建信号安装结构体
sig_act.sa_sigaction = signal_handle_new;//指定信号关联函数
sig_act.sa_flags = SA_SIGINFO;//声明信号是携带数据的
//将信号和安装信号结构体关联
sigaction(SIGUSR1, &sig_act,NULL);//信号num,新信号结构体指针,旧信号结构体指针
pid_t pid;
pid = fork();
if (pid == 0)
{
while (1)
{
sleep(1);//为了降低CPU使用率
}
}
else if (pid > 0)
{
sleep(1);//保证子进程先走
sigval value;//新建携带数据联合体value
value.sival_int = 1999;
cout << "send signal" << endl;
sigqueue(pid, SIGUSR1, value);//携带value值发送信号SIGUSR1给进程号为pid的进程
}
return 0;
}
解释详见代码中注释,运行结果如下:
证明信号处理函数获取到了信号发送携带的int值,接收成功
我们进一步定义两套信号和对应的信号处理函数,实验信号的顺序响应问题,即排队响应和打断响应问题
同种信号在信号处理函数执行时输入:
#include //wait func
#include //ststem
#include
using namespace std;
//新的信号处理函数多了两个参数,其中signfo_t里面携带了我们想要的数据
void signal_handle_new(int num, siginfo_t *info, void *d)
{
for (int i = 0; i < 5; i++)
{
cout << "signal_handle_new is running" <<i<< endl;
sleep(1);
}
}
void signal_handle_new2(int num, siginfo_t *info, void *d)
{
cout << "signal_handle_new2 is running" << endl;
}
int main(int argc, char *argv[])
{
//安装信号
struct sigaction sig_act, sig_act2;//新建信号安装结构体
sig_act.sa_sigaction = signal_handle_new;//指定信号关联函数
sig_act2.sa_sigaction = signal_handle_new2;//指定信号关联函数
//将信号和安装信号结构体关联
sigaction(SIGUSR1, &sig_act,NULL);//信号num,新信号结构体指针,旧信号结构体指针
sigaction(SIGUSR2, &sig_act2, NULL);//信号num,新信号结构体指针,旧信号结构体指针
while (1)
{
cout << "process run..." <<getpid()<< endl;
sleep(1);
}
return 0;
}
从打印数字顺序可以看出,如果linux执行一个信号处理函数的时候如果又收到又收到一个同种信号,则不会打断,是排队顺序执行的。
同样的代码,接下来测试不同种信号在信号处理函数执行时输入:
可以验证上面的结论:如果linux执行一个信号处理函数的时候如果又收到一个不同种信号,会去执行新的信号处理函数,执行完之后再回来执行。
另外需注意:
结构体部分内容参考:
https://www.cnblogs.com/mickole/p/3191804.html
https://blog.csdn.net/jnu_simba/article/details/8947410