Linux-C 进程通信之信号
一、简述
记--进程之间使用信号进行通信。异步信号包含: 非实时信号 、实时信号。
实时信号一定会响应,非实时信息号不一定会响应(可能会被忽略,或丢失)
信号一般有以下设定:
1,捕捉 (收到某个信号,做指定的动作,而不是做默认的)
2,忽略 (收到某个信号,不做什么动作)
3,阻塞 (收到某个信号,先做完当前事情,然后在响应信号)
4,按照默认动作
注:SIGKILL,SIGSTOP不能被捕捉
关于信号的详细说明请参考man手册:man 7 signal。
二、signal()与kill()
signal()函数
功能
简单的信号处理函数(信号捕捉函数)
头文件
#include <signal.h>
原型
typedef void (*sighandler_t)(int); //信号处理函数的 指针
sighandler_t signal(int signum, sighandler_t handler);
参数
signum:要捕捉的信号
handler:处理方式
1、SIG_IGN:忽略这个信号
2、SIG_DFL:按照默认动作执行这个信号
3、如果是一个函数,则是捕捉这个信号,收到这个信号的时候去执行这个函数。
返回值
成功:返回信号处理程序的先前值
出错:返回SIG_ERR,并设置errno
kill()函数
功能
向一个进程发送信号
头文件
#include <sys/types.h>
#include <signal.h>
原型
int kill(pid_t pid, int sig);
参数
pid:进程ID (process id)
sig:要发送的信号
返回值
成功:返回0
失败:返回-1,并设置error。
三、sigprocmask()信号的阻塞
信号的阻塞:在阻塞过程中,信号不会丢失,会被挂起,等解开阻塞的时候再响应。
sigprocmask()函数
功能
阻塞信号,延迟响应 。(检查并更改阻塞的信号)
头文件
#include <signal.h>
原型
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
参数
how:对信号的操作(应用于信号集合中的所有函数)
SIG_BLOCK:添加信号集合里面的信号进行阻塞(原本的设置上添加设置)
SIG_UNBLOCK:解除信号集合里面的信号的阻塞。
SIG_SETMASK: 直接阻塞信号集合里面的信号,原本的设置直接被覆盖。
set:要设置的信号集合
int sigemptyset(sigset_t *set);//清空信号集合
int sigfillset(sigset_t *set);//将所有信号登记进集合里面
int sigaddset(sigset_t *set, int signum);//往集合里面添加signum信号
int sigdelset(sigset_t *set, int signum);// 往集合里面删除signum信号
int sigismember(const sigset_t *set, int signum);//测试信号集合里面有无signum信号
oldset:用来保存旧的信号集合设置
如果这里是一个具体的指针,则将原本的设置保存在这里
如果是NULL则不保存。
返回值
成功:返回0
失败:返回-1,并设置error
备注
在阻塞状态时,非实时信号可能会丢失
四、带数据的信号的发送与处理:sigaction()与sigqueue()
sigaction()函数
功能
检查并改变信号动作(捕捉信号,指定处理动作)
头文件
#include <signal.h>
原型
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
参数
signum:捕捉的信号
act:信号动作结构体,用来登记对这个信号进行处理的动作。
struct sigaction
{
void (*sa_handler)(int); //不带参数的信号处理函数,默认执行这个不带参数的信号处理函数
void (*sa_sigaction)(int, siginfo_t *, void *);//带参的信号处理函数,如果想要使能这个信号处理函数,
//需要设置一下sa_flags为SA_SIGINFO
sigset_t sa_mask; //信号阻塞设置
//1,在信号处理函数执行的过程当中阻塞掉指定的信号,
//指定的信号过来将会被挂起,等函数结束后再执行
//2,sigset_t信号集合类型,参照sigprocmask中sigset的使用方式。
int sa_flags; //信号的操作标识,例如设置使用的是带参信号处理函数,还是不带参数的
void (*sa_restorer)(void); //被遗弃的设置
};
oldact:用来保存原来的设置,如果是NULL则不保存。
返回值
成功:返回0
失败:返回-1,并设置error
备注
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 */
uid_t si_uid; /* Real user ID of sending process */
int si_status; /* Exit value or signal */
clock_t si_utime; /* User time consumed */
clock_t si_stime; /* System time consumed */
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_lower; /* Lower bound when address violation
occurred (since Linux 3.19) */
void *si_upper; /* Upper bound when address violation
occurred (since Linux 3.19) */
int si_pkey; /* Protection key on PTE that caused
fault (since Linux 4.6) */
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) */
};
sigqueue函数
功能
将信号和数据发送到指定进程
头文件
#include <signal.h>
原型
int sigqueue(pid_t pid, int sig, const union sigval value);
参数
pid:进程ID
sig:要发送的信号
value:附加数据
union sigval
{
int sival_int;//只附加一个int数据
void *sival_ptr;//附加更多数据(附加一个地址)
};
//注:这是共用体、两者取其一
返回值
成功:返回0
失败:返回-1,并设置error
备注
跟sigaction结合使用
五、补充
1、查看进程相关信息
ps:查看进程状态 (一个瞬间的进程状态,静态)
top:“实时查看” ,按q退出 (实时动态显示)
pstree 树状查看 (可以看出所属子进程)
2、对某个进程发送信号
kill -s 指定发送一个信号 进程的pid
killall:指定一个应用程序名字去发送信号
kill -l 查看系统有什么信号
其中SIGKILL,SIGSTOP只能按照系统默认动作执行,不能捕捉,阻塞及忽略
前面31个是非实时信号:
1,每一个非实时信号对应一个信号名
2,每一个非实时信号基本上都会对应一个系统事件
3,信号有可能会丢失
后面的31个是实时信号:
1,信号不会丢失
2,优先级比非实时信号要高
2、信号发送:
1,自己发给自己: raise()函数
2,发送信号给其他进程:
kill -s 信号 进程号
killall -s 信号 进程名字
3、信号的继承
1,信号初始化的设置是会被子进程继承的
2,信号的阻塞设置是会被子进程继承的
3,被挂起的信号是不会被子进程继承的
4、信号处理函数里面应该注意的地方:
1,不应该在信号处理函数里面操作全局变量(如果要操作,记得加锁)
2,应该注意一下一些函数调用是否安全,可以查看man 7 signal
5、测试所有信号的响应优先级
实时信号34-64:数字大的优先响应;
原文链接:https://blog.csdn.net/nanfeibuyi/article/details/81945408