关于Linux信号的简单说明、使用、编码
- 1. 信号的产生情况
- 2.信号的处理方式
- 3. 与信号相关的系统调用
- 3.1 向其他进程发送信号(kill)
- 3.2 设置信号的处理方式(sigaction)
- 3.3 检测或者更改信号屏蔽字
- 3.4 检查是否有挂起信号
- 3.5 使用signal设置信号处理程序
- 4. 示例程序
很久没有发文章了,最近想自学一下linux的多进程通信,并做了一些简单的学习笔记,来分享我目前对
多进程微薄的理解。
1. 信号的产生情况
| 序号 | 产生情况 |
|---|
| 1 | 硬件产生(非法访问内存、除0操作) |
| 2 | 软件触发(定时器) |
| 3 | 用户终端产生(Ctrl+C) |
| 4 | 用户shell发送kill命令 |
| 5 | 同一所有者的进程之间传输信号,或者root用户向进程传输信号 |
2.信号的处理方式
| 序号 | 处理方式 |
|---|
| 1 | 忽略(有些信号不可以忽略,如SIGSTOP) |
| 2 | 默认操作 |
| 3 | 用户自定义操作 |
3. 与信号相关的系统调用
| 序号 | 系统调用方式 |
|---|
| 1 | 向其他进程发送信号 |
| 2 | 改变默认信号处理方式 |
| 3 | 阻塞信号掩模 |
| 4 | 检查是否有挂起信号(信号被阻塞) |
3.1 向其他进程发送信号(kill)
#include
#include
int kill(pid_t pid, int sig);
// pid>0,向pid进程发送信号
// pid=0,向同进程组的其他进程发信号
// pid=-1,向所有有权发送信号的进程发送
// pid<-1,向pid进程组发送信号
// 返回值:0表示成功,-1表示失败,失败时,可以在errno得到错误码
3.2 设置信号的处理方式(sigaction)
#include
int sigaction(int signum, const struct sigaction *act, const struct sigaction *old);
// signum表示要操作的信号(除了SIGKILL SIGSTOP)
// act为要执行的处理方式
// old为以前的处理方式
struct sigaction
{
void (*sa_handler)(int); // 默认的处理函数
void (*sa_sigaction)(int, siginfo_t *, void *); // 当sa_flag包含SA_SIGINFO时,信号处理使用的函数
sigset_t sa_mask; // 处理函数执行期间需要屏蔽的信号
int sa_flags; // 指定信号处理行为
void (*sa_restorer)(void); // 废弃
};
3.3 检测或者更改信号屏蔽字
#include
int sigpromask(int how, const sigset_t *set, const sigset_t *oldset);
// how: 有三种值,SIG_BLOCK加入信号到进程屏蔽;SIG_UNBLOCK从信号屏蔽中将信号删除;SIG_SETMASK将set值设置为新的进程屏蔽
// set:指向信号集的指针,为NULL时,该函数通过oldset检测当前的设置值
// 返回值:0成功;1失败
3.4 检查是否有挂起信号
// 我理解的挂起是“屏蔽”的意思,可能不太准确
#include
int sigpending(sigset_t *set);
// 返回值:0成功
3.5 使用signal设置信号处理程序
// signal信号是ANSI C标准的设置信号处理方式的函数,功能与3.2中的sigaction一样,但后者是POSIX标准的
#include
sighandler_t signal(int signum, sighandler_t hander);
// signum:进程号
// handler:信号处理程序,或者是宏SIG_ING(忽略),或者是宏SIG_DEL(系统默认)
4. 示例程序
// 接下来的实例程序展示了3.1~3.4的函数使用方法,我并没有是使用3.5,因为觉得没必要了
#include
#include
#include
#include
#include
#include
#include
int Operation2ChildPid2(); // 对第二个子进程的操作
int Operation3ChildPid3(); // 对第三个子进程的操作,信号掩模
void ChildPid2SignalAction(int i32SigNum);
int main()
{
pid_t pidtChild1 = 0; // 第一个子进程的进程号,用来测试kill命令
if (0 < (pidtChild1 = fork())) // 主进程
{
sleep(5);
int i32Ret = kill(pidtChild1, SIGSTOP); // 向子进程发送停止信号
if (i32Ret != 0)
{
std::cout << "向子进程1发送信号失败" << std::endl;
exit(EXIT_FAILURE);
}
else
{
std::cout << "向子进程1发送信号成功" << std::endl;
i32Ret = Operation2ChildPid2(); // 操作第2个子进程,第二个子进程注册USR1/2两个命令,观察向其发送USR1/2后是佛可以接受成功
if (EXIT_SUCCESS == i32Ret)
{
std::cout << "对子进程2的sigaction测试成功" << std::endl;
i32Ret = Operation3ChildPid3(); // 操作第3个子进程,测试信号掩模和sigpending
if (EXIT_SUCCESS == i32Ret)
{
std::cout << "对子进程3的sigaction测试成功" << std::endl;
}
else
{
std::cout << "对子进程3的sigaction测试失败" << std::endl;
exit(EXIT_FAILURE);
}
}
else
{
std::cout << "对子进程2的sigaction测试失败" << std::endl;
exit(EXIT_FAILURE);
}
}
}
else if (0 == pidtChild1) // 子进程
{
std::cout << "子进程开启" << std::endl;
for (int i = 0; i < 10; i++)
{
std::cout << "子进程1心跳" << i << std::endl;
sleep(1);
}
std::cout << "子进程执行成功" << std::endl;
}
else
{
std::cout << "fork child1 failed" << std::endl;
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
int Operation2ChildPid2()
{
pid_t pidtChild2 = 0;
if (0 < (pidtChild2 = fork()))
{
sleep(5);
int i32Ret = kill(pidtChild2, SIGUSR1);
if (i32Ret != 0)
{
std::cout << "向子进程2发送USR1信号失败" << std::endl;
return EXIT_FAILURE;
}
else
{
i32Ret = 1;
int i32Ret = kill(pidtChild2, SIGUSR2);
if (i32Ret != 0)
{
std::cout << "向子进程2发送USR2信号失败" << std::endl;
return EXIT_FAILURE;
}
else
{
;
}
}
sleep(7);
}
else if (0 == pidtChild2)
{
// 子进村注册信号处理函数
struct sigaction structSigaction;
structSigaction.sa_flags = SA_RESTART; // 让子进程在处理信号后,继续返回执行信号来时的程序
structSigaction.sa_handler = ChildPid2SignalAction;
sigaction(SIGUSR1, &structSigaction, NULL);
sigaction(SIGUSR2, &structSigaction, NULL);
for (int i = 0; i < 10; i++)
{
std::cout << "子进程2心跳" << i << std::endl;
sleep(1);
}
}
return EXIT_SUCCESS;
}
void ChildPid2SignalAction(int i32SigNum)
{
// 子进程2
if (SIGUSR1 == i32SigNum)
{
std::cout << "子进程2收到USR1信号" << std::endl;
}
else if (SIGUSR2 == i32SigNum)
{
std::cout << "子进程2收到USR2信号" << std::endl;
}
else
{
std::cout << "子进村2收到未知信号" << std::endl;
}
return;
}
int Operation3ChildPid3()
{
pid_t pidtChild3 = 0;
if (0 < (pidtChild3 = fork()))
{
sleep(1);
for (int i = 0; i < 20; i++)
{
// 每1秒就发送一个USR1信号
int i32Ret = kill(pidtChild3, SIGUSR1); // 3秒后向子进程3发送信号
if (EXIT_SUCCESS == i32Ret)
{
std::cout << "向子进程3发送命令成功" << std::endl;
}
else
{
std::cout << "向子进程3发送命令失败" << std::endl;
}
sleep(1);
}
sleep(20); // 等待子进程3结束
}
else if (0 == pidtChild3)
{
// 先注册一个信号的自定义处理
struct sigaction sigactionUSER1;
memset(&sigactionUSER1.sa_mask, 0, sizeof(sigactionUSER1.sa_mask)); // 清空信号处理期间需要屏蔽的信号
sigactionUSER1.sa_handler = ChildPid2SignalAction; // 就拿子进程2的处理函数来处理吧
sigactionUSER1.sa_flags = SA_RESTART; // 标记处理函数要使用sa_handler,并且处理完信号后,恢复子进程运行
sigaction(SIGUSR1, &sigactionUSER1, NULL); // 注册对SIGUSR1的处理函数
sigset_t sigsetNew, sigsetOld;
sigemptyset(&sigsetNew); // 用于设置的新sigset
sigemptyset(&sigsetOld); // 用于保存的sigset
sigaddset(&sigsetNew, SIGUSR1); // 需要屏蔽的信号
sigprocmask(SIG_BLOCK, &sigsetNew, &sigsetOld); // 屏蔽SIGUSR1
for (int i = 0; i < 20; i++)
{
std::cout << "子进程3心跳" << i << std::endl;
if (9 == i)
{
sigset_t sigsetSigpending; // 用于提取阻塞信号
sigpending(&sigsetSigpending);
if (sigismember(&sigsetSigpending, SIGUSR1))
{
std::cout << "检测到阻塞信号" << std::endl;
}
else
{
std::cout << "未检测到阻塞信号" << std::endl;
}
sigprocmask(SIG_SETMASK, &sigsetOld, NULL); // 恢复默认阻塞,也就是不阻塞SIGUSR1信号
}
sleep(1);
}
}
else
{
std::cout << "子进程3创建失败" << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}