UNIX环境高级编程——可靠信号与不可靠信号
在早期的UNIX中信号是不可靠的,不可靠在这里指的是:信号可能丢失,一个信号发生了,但进程却可能一直不知道这一点。
现在Linux 在SIGRTMIN实时信号之前的都叫不可靠信号,这里的不可靠主要是不支持信号队列,就是当多个信号发生在进程中的时候(收到信号的速度超过进程处理的速度的时候),这些没来的及处理的信号就会被丢掉,仅仅留下一个信号。
可靠信号是多个信号发送到进程的时候(收到信号的速度超过进程处理信号的速度的时候),这些没来的及处理的信号就会排入进程的队列。等进程有机会来处理的时候,依次再处理,信号不丢失。
通过一个实例来说明 不可靠信号(SIGUSR1(10)) 、可靠信号SIGRTMIN(ubuntu上是34)丢失的情况:
#include <stdlib.h>
#include <iostream>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
using namespace std;
int g_unreliable = 0;
int g_reliable = 0;
void signal_dispath(int signo)
{
if(signo == SIGUSR1)
{
cout<<"receving signal SIGUSR1"<<endl;
g_unreliable++;
}
else if(signo == SIGRTMIN)
{
cout<<"receving signal SIGRTMIN"<<endl;
g_reliable++;
}
}
void output_myself()
{
cout<<"^^^^^^^^^^^^^^^^^^^^^^^^^^^"<<endl;
cout<<"^^^^^^^ Hello World ^^^^^^^"<<endl;
cout<<"^^^^^^^ I'm LeoK ^^^^^^^"<<endl;
cout<<"^^^^^^^^^^^^^^^^^^^^^^^^^^^"<<endl;
}
int main(int argc, char** argv)
{
if(argc != 1)
{
cout<<"this program need not paramter"<<endl;
return 0;
}
/* 每个程序的信息需要打印*/
output_myself();
if(signal(SIGUSR1, signal_dispath) == SIG_ERR)
{
perror("register SIGUSR1 signal failed");
return -1;
}
if(signal(SIGRTMIN, signal_dispath) == SIG_ERR)
{
perror("register SIGRTMIN signal failed");
return -1;
} sigset_t set;
sigset_t oset;
/* 清除set中的信号,把这个set置为空 */
sigemptyset(&set);
/* 往set中添加SIGUSR1信号 */
sigaddset(&set, SIGUSR1);
/* 往set中添加SIGRTMIN信号 */
sigaddset(&set, SIGRTMIN);
if(sigprocmask(SIG_BLOCK, &set, &oset) == -1)
{
perror("set process signal to be set failed");
return -1;
}
sleep(10);
if(sigpending(&set) == -1)
{
perror("sigpending get signal mask failed");
return -1;
}
/* 判断信号是不是在信号屏蔽字中 */
/* SIGUSR1 */
int ret = -1;
if((ret = sigismember(&set, SIGUSR1)) == -1)
{
perror("sigismember test SIGUSR1 failed");
return -1;
}
else if(ret == 0)
{
cout<<"SIGUSR1 signal exist not signal mask"<<endl;
}
else
{
cout<<"SIGUSR1 signal exist signal mask"<<endl;
} sleep(2);
/* SIGRTMIN */
if((ret = sigismember(&set, SIGRTMIN)) == -1)
{
perror("sigismember test SIGRTMIN failed");
return -1;
}
else if(ret == 0)
{
cout<<"SIGRTMIN signal exist not signal mask"<<endl;
}
else
{
cout<<"SIGRTMIN signal exist signal mask"<<endl;
}
/* 休眠30s */
sleep(30); /*解除对SIGRTMIN和SIGUSR1的屏蔽字*/
if(sigprocmask(SIG_SETMASK, &oset, NULL) == -1)
{
perror("set oldset failed");
return -1;
}
cout<<"SIGUSR1: "<<g_unreliable<<endl;
cout<<"SIGRTMIN: "<<g_reliable<<endl;
output_myself();
return 0;
}
该程序的解释是
首先把信号SIGUSR1和SIGRTMIN这两个信号通过sigaddset这个函数加入是使用的set(类型sigset_t)这个信号集合中,通过sigprocmask把进程的信号屏蔽字设置为set这个集合,这个时候让进程睡眠(在这个时候对于SIGUSR1、SIGRTMIN这两个信号是阻塞的,来达到进程处理的速度低于信号发送的速度),在睡眠的时候,通过脚本如下:
usr.sh
#/bin/bash
i=0;
for((i=0;i<20;i++));
do kill -10 `pidof a.out`;done
min.sh
#/bin/bash
i=0;
for((i=0;i<20;i++));
do kill -34 `pidof a.out`;done
发送信号SIGUSR1 和SIGRTMIN给进程a.out(test.cpp编译后的可执行程序)
最后的输出如下:
huangcheng@ubuntu:~$ ./a.out
^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^ Hello World ^^^^^^^
^^^^^^^ I'm LeoK ^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^
SIGUSR1 signal exist signal mask
SIGRTMIN signal exist signal mask
receving signal SIGRTMIN
receving signal SIGRTMIN
receving signal SIGRTMIN
receving signal SIGRTMIN
receving signal SIGRTMIN
receving signal SIGRTMIN
receving signal SIGRTMIN
receving signal SIGRTMIN
receving signal SIGRTMIN
receving signal SIGRTMIN
receving signal SIGRTMIN
receving signal SIGRTMIN
receving signal SIGRTMIN
receving signal SIGRTMIN
receving signal SIGRTMIN
receving signal SIGRTMIN
receving signal SIGRTMIN
receving signal SIGRTMIN
receving signal SIGRTMIN
receving signal SIGRTMIN
receving signal SIGUSR1
SIGUSR1: 1
SIGRTMIN: 20
^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^ Hello World ^^^^^^^
^^^^^^^ I'm LeoK ^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^
另一个例子:
先是recv程序:
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0) void handler(int); int main(int argc, char *argv[])
{
struct sigaction act;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0; sigset_t s;
sigemptyset(&s);
sigaddset(&s, SIGINT);
sigaddset(&s, SIGRTMIN);
sigprocmask(SIG_BLOCK, &s, NULL);
if (sigaction(SIGINT, &act, NULL) < 0)
ERR_EXIT("sigaction error"); if (sigaction(SIGRTMIN, &act, NULL) < 0)
ERR_EXIT("sigaction error"); if (sigaction(SIGUSR1, &act, NULL) < 0)
ERR_EXIT("sigaction error");
for (;;)
pause();
return 0;
} void handler(int sig)
{
if (sig == SIGINT || sig == SIGRTMIN)
printf("recv a sig=%d\n", sig);
else if (sig == SIGUSR1)
{
sigset_t s;
sigemptyset(&s);
sigaddset(&s, SIGINT);
sigaddset(&s, SIGRTMIN);
sigprocmask(SIG_UNBLOCK, &s, NULL);
}
}
在主函数中将SIGINT和SIGRTMIN信号加入信号屏蔽字,只有当接收到SIGUSR1信号时才对前面两个信号unblock。需要注意的是:如果在信号处理函数中对某个信号进行解除阻塞时,则只是将pending位清0,让此信号递达一次(同个实时信号产生多次进行排队都会抵达),但不会将block位清0,即再次产生此信号时还是会被阻塞,处于未决状态。
接着是send程序:
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<signal.h> #define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while(0) int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "Usage %s pid\n", argv[0]);
exit(EXIT_FAILURE);
} pid_t pid = atoi(argv[1]); //字符串转换为整数
union sigval val;
val.sival_int = 100;
sigqueue(pid, SIGINT, val); // 不可靠信号不会排队,即会丢失
sigqueue(pid, SIGINT, val);
sigqueue(pid, SIGINT, val);
sigqueue(pid, SIGRTMIN, val); //实时信号会排队,即不会丢失
sigqueue(pid, SIGRTMIN, val);
sigqueue(pid, SIGRTMIN, val);
sleep(3);
kill(pid, SIGUSR1); return 0; }
先是运行recv程序:
huangcheng@ubuntu:~$ ./sigrtime_recv2
接着ps出recv进程的pid,运行send程序:
huangcheng@ubuntu:~$ ./sigrtime_send 3251
在send程序中连续各发送了SIGINT和SIGRTMIN信号3次,接着睡眠3s后使用kill函数发送SIGUSR1信号给recv进程,此时recv进程会输出如下:
recv a sig=34
recv a sig=34
recv a sig=34
recv a sig=2
即实时信号支持排队,3个信号都接收到了,而不可靠信号不支持排队,只保留一个信号。
UNIX环境高级编程——可靠信号与不可靠信号的更多相关文章
- (八) 一起学 Unix 环境高级编程 (APUE) 之 信号
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (十三) [终篇] 一起学 Unix 环境高级编程 (APUE) 之 网络 IPC:套接字
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- Unix 环境高级编程 (APUE) 之 网络 IPC:套接字
一起学 Unix 环境高级编程 (APUE) 之 网络 IPC:套接字 . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级 ...
- 《UNIX环境高级编程(第3版)》
<UNIX环境高级编程(第3版)> 基本信息 原书名:Advanced Programming in the UNIX Environment (3rd Edition) (Addison ...
- (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (四) 一起学 Unix 环境高级编程(APUE) 之 系统数据文件和信息
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
随机推荐
- python2.7入门---操作mysql数据库增删改查
Python 标准数据库接口为 Python DB-API,Python DB-API为开发人员提供了数据库应用编程接口.Python 数据库接口支持非常多的数据库,你可以选择适合你项目的数据库: G ...
- VS生成项目时,有些文件无法复制到输出目录的解决办法
有时候,我们在生成项目时,发现有些文件如:.jpg的图片文件,无法复制到输出目录中,此时会非常纠结,反复的清理项目,重新生成,依旧不能解决此问题.后来我打开.csproj的项目工程文件时,经过对比发现 ...
- Redis出现多线程调用时抛出 [B cannot be cast to java.lang.Long] 异常
原因分析: 多个线程同时调用了同一个jedis对象,导致内存数据被多个线程竞争,产生数据混乱 (或者大家都用通一个redis获取同一个实例,登录同一个账号使用缓存时报错) 解决方案:每个线程都new出 ...
- Mac下配置远程Linux 服务器SSH密钥认证自动登录
1. 在本地机器创建公钥 打开万能的终端,执行如下命令,无视一切输出,一路欢快地回车即可. ssh-keygen -t rsa -C 'your email@domain.com' -t 指定密钥类型 ...
- SQL SERVER Management Studio
1. 实验目的 熟悉SQL SERVER Management Studio的部分操作 数据SQL SERVER简化版和完整版数据库设计 2. 实验内容 2.1. 熟悉简化版SQL ...
- Go 语言数组
Go 语言提供了数组类型的数据结构. 数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整形.字符串或者自定义类型. 相对于去声明number0, number ...
- oo第二阶段总结
第五次作业--多线程电梯 一.设计策略 本次作业是我们第一次接触多线程,给程序添加多线程功能后最大的挑战是实现共享数据的安全.避免冲突,由于这次作业是第一次尝试多线程方法,因此采用了将所有方法都加上s ...
- SQL Server AlwaysON从入门到进阶(2)——存储
本文属于SQL Server AlwaysON从入门到进阶系列文章 前言: 本节讲解关于SQL Server 存储方面的内容,相对于其他小节而言这节比较短.本节会提供一些关于使用群集或者非群集系统过程 ...
- mysql进阶(二十八)MySQL GRANT REVOKE用法
mysql进阶(二十八)MySQL GRANT REVOKE用法 MySQL的权限系统围绕着两个概念: 认证->确定用户是否允许连接数据库服务器: 授权->确定用户是否拥有足够的权限执 ...
- shell test和find命令实例解析
shell test和find命令实例解析 下面以\build\core\product.mk相关部分来学习 define _find-android-products-files $(shell t ...