Linux进程间通信(中)之信号、信号量实践
上节我们分享了Linux进程间通信的其中两种方式:管道、消息队列,文章如下:
这节我们就来分享一下Linux的另外两种进程间通信的方式:信号、信号量。
1、信号
我们使用过windows的都知道,当一个程序被卡死的时候不管怎样都没反应,这样我们就可以打开任务管理器直接强制性的结束这个进程,这个方法的实现就是和Linux上通过生成信号和捕获信号来实现相似的,运行过程中进程捕获到这些信号做出相应的操作使最终被终止。
信号的主要来源是分为两部分,一部分是硬件来源,一部分是软件来源;进程在实际中可以用三种方式来响应一个信号:一是忽略信号,不对信号做任何操作,其中有两个信号是不能别忽略的分别是SIGKILL和SIGSTOP。二是捕捉信号,定义信号处理函数,当信号来到时做出响应的处理。三是执行缺省操作,Linux对每种信号都规定了默认操作。注意,进程对实时信号的缺省反应是立即终止。
发送信号的函数有很多,主要使用的有:kill()、raise()、abort()、alarm()
。
先来熟悉下kill函数,进程可以通过kill()函数向包括它本身在内的其它进程发送一个信号,如果程序没有发送这个信号的权限,对kill函数的调用将会失败,失败的原因通常是由于目标进程由另一个用户所拥有。
kill函数的原型为:
#include<sys/types.h>
#include<signal.h>
int kill(pid_t pid,int sig);
它的作用是把信号sig发送给进程号为pid的进程,成功时返回0。kill调用失败返回-1,调用失败通常有三大原因:
-
1、给定的信号无效 -
2、发送权限不够 -
3、目标进程不存在
还有一个非常重要的函数,信号处理signal函数。程序可以用signal函数来处理指定的信号,主要通过恢复和忽略默认行为来操作。signal函数原型如下:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
我们来看一个例程了解一下signal函数。signal.c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
//函数ouch对通过参数sig传递进来的信号作出响应。
void ouch(int sig)
{
printf("signal %d\n", sig);
//恢复终端中断信号SIGINT的默认行为
(void) signal(SIGINT, SIG_DFL);
}
int main()
{
//改变终端中断信号SIGINT的默认行为,使之执行ouch函数
(void) signal(SIGINT, ouch);
while(1)
{
printf("Hello World!\n");
sleep(1);
}
return 0;
}
运行结果:
可以看出当我按下ctrl+c的时候并不会退出,只有当再次按下ctrl+c的时候才会退出。造成的原因是因为SIGINT的默认行为被signal函数改变了,当进程接受到信号SIGINT时,它就去调用函数ouch去处理,注意ouch函数把信号SIGINT的处理方式改变成默认的方式,所以当你再按一次ctrl+c时,进程就像之前那样被终止了。
下面是几种常见的信号:
-
SIGHUP :从终端上发出的结束信号 -
SIGINT :来自键盘的中断信号 ( ctrl + c ) -
SIGKILL :该信号结束接收信号的进程 -
SIGTERM:kill 命令发出的信号 -
SIGCHLD:标识子进程停止或结束的信号 -
SIGSTOP:来自键盘 ( ctrl + z ) 或调试程序的停止执行信号。
信号发送主要函数有kill和raise。上面我们知道kill函数的用法也清楚kill函数是可以向自身发送信号和其它进程发送信号,raise与之不同的是只可以向本身发送信号。
通过raise函数向自身发送数据,使子进程暂停通过测试如下: raise.c
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
int main()
{
pid_t pid;
int ret;
if((pid=fork())<0)
{
printf("Fork error\n");
exit(1);
}
//子进程
if(pid==0)
{
//在子进程中使用raise()函数发出SIGSTOP信号,使子进程暂停
printf("I am child pid:%d.I am waiting for any signal\n",getpid());
raise(SIGSTOP);
printf("I am child pid:%d.I am killed by progress:%d\n",getpid(),getppid());
exit(0);
}
//父进程
else
{
sleep(2);
//在父进程中收集子进程发出的信号,并调用kill()函数进行相应的操作
if((waitpid(pid,NULL,WNOHANG))==0)
{
//若pid指向的子进程没有退出,则返回0,且父进程不阻塞,继续执行下边的语句
if((ret=kill(pid,SIGKILL))==0)
{
printf("I am parent pid:%d.I am kill %d\n",getpid(),pid);
}
}
//等待子进程退出,否则就一直阻塞
waitpid(pid,NULL,0);
exit(0);
}
}
当调用raise的时候子进程就会暂停:
信号是对终端机的一种模拟,也是一种异步通信方式。
2、信号量
主要作为进程间,以及同一进程不同线程之间的同步手段。信号量是用来解决进程之间的同步与互斥问题的一种进程之间的通信机制,包括一个称为信号量的变量和在该信号量下等待资源的进程等待队列,以及对信号量进行的两个原子操作。信号量对应于某一种资源,取一个非负的整形值。信号量的值是指当前可用的资源数量。
由于信号量只有两种操作,一种是等待信号,另一种是发送信号。即P和V,它们的行为如下:
-
P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行。 -
V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1。
Linux特别提供了一组信号量接口来对信号操作,它们不只是局限的针对二进制信号量,下面我们来对每个函数介绍,需要注意的是这些函数都是用来成对组的信号量值进行操作的。
2.1、semget函数
它的作用是创建一个新信号量或取得一个已有信号量。
int semget(key_t key, int nsems, int semflg);
第一个参数是key整数型,不相关的进程可以通过它访问一个信号量,它代表程序可能要使用的某个资源,程序对所有信号量的访问都是间接的,先通过调用semget函数并提供一个键,再由系统生成一个相应的信号标识符(semget函数的返回值),只有semget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。如果多个程序使用相同的key值,key将负责协调工作。
第二个参数是制定需要的信号数量,通常情况下为1。
第三个参数是一组标志位,当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。
semget函数成功返回一个相应信号标识符(非零),失败返回-1。
2.2、semop函数
它的作用是改变信号量的值。
int semop(int semid, struct sembuf *sops, unsigned nsops);
sops是一个指针,它指向这样一个数组:元素用来描述对semid代表的信号量集合中第几个信号进行怎么样的操作。nops规定该数组中操作的数量。
semop函数返回0表示成功,返回-1表示失败。
2.3、semctl函数
该函数用来直接控制信号量信息。
int semctl(int semid, int semnum, int cmd, …);
semget并不会初始化每个信号量的值,这个初始化必须通过SETVAL命令或SETALL命令调用semctl来完成。
例程:semctl.c
#include <stdio.h>
#include <linux/sem.h>
#define NUMS 10
int get_sem_val(int sid,int semnum)//取得当前信号量
{
return(semctl(sid,semnum,GETVAL,0));
}
int main(void)
{
int I ;
int sem_id;
int pid;
int ret;
struct sembuf sem_op;//信号集结构
union semun sem_val;//信号量数值
//建立信号量集,其中只有一个信号量
sem_id = semget(IPC_PRIVATE,1,IPC_CREAT|0600);
//IPC_PRIVATE私有,只有本用户使用,如果为正整数,则为公共的;1为信号集的数量;
if (sem_id==-1)
{
printf("create sem error!\n");
exit(1);
}
printf("create %d sem success!\n",sem_id);
//信号量初始化
sem_val.val=1;
//设置信号量,0为第一个信号量,1为第二个信号量,...以此类推;SETVAL表示设置
ret = semctl(sem_id,0,SETVAL,sem_val);
if (ret < 0){
printf("initlize sem error!\n");
exit(1);
}
//创建进程
pid = fork();
if (pid < 0)
{
printf("fork error!\n");
exit(1);
}
else if(pid == 0)
{
//一个子进程,使用者
for ( i=0;i<NUMS;i++)
{
sem_op.sem_num=0;
sem_op.sem_op=-1;
sem_op.sem_flg=0;
semop(sem_id,&sem_op,1);//操作信号量,每次-1
printf("%d 使用者: %d\n",i,get_sem_val(sem_id,0));
}
}
else
{
//父进程,制造者
for (i=0;i<NUMS;i++)
{
sem_op.sem_num=0;
sem_op.sem_op=1;
sem_op.sem_flg=0;
semop(sem_id,&sem_op,1);//操作信号量,每次+1
printf("%d 制造者: %d\n",i,get_sem_val(sem_id,0));
}
}
exit(0);
}
运行结果:
信号量的出现就是保证资源在一个时刻只能有一个进程(线程),所以例子当中只有制造者在制造(+1操作)过程中,使用者这个进程是无法随sem_id进行操作的。也就是说信号量是协调进程对共享资源操作的,起到了类似互斥锁的作用,但却比锁拥有更强大的功能。
往期精彩
觉得本次分享的文章对您有帮助,随手点[在看]
并转发分享,也是对我的支持。
本文分享自微信公众号 - 嵌入式云IOT技术圈(gh_d6ff851b4069)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
架构设计 | 基于消息中间件,图解柔性事务一致性
本文源码:GitHub·点这里 || GitEE·点这里 一、最大努力通知 TCC分段提交适用分布式架构中对一致性、实时性要求较高的业务场景,在实际业务中也存在实时性比较低的业务,例如常见的短信通知,客户端消息,运营体系更新等业务,这时候为了减轻核心流程的复杂度和压力,可以采取最大努力通知方式实现柔性事务的管理。 例如常见的第三方支付业务中,本地业务和支付端业务处理完成之后都会生成消息通知,基本流程如下: 本地业务预处理完成之后; 请求第三方支付服务; 支付操作成功对该账号发送消息; 支付服务回调本地业务; 本地业务生成系统通知消息; 上述流程的消息场景中有一些基础特点,在核心业务处理完成之后,发送消息通知,允许失败,在指定时间段内或者指定重试次数之后,允许消息丢失情况存在,即消息的不可靠性。 在实际的支付系统中,启动每日对账校验时会对当日的流水做校验,如果发现支付流水有未完成的流程,会有状态弥补,后续可以继续处理,这种手段在对账中很常用。 二、可靠消息 分布式事务基于可靠消息最终一致性的实现方案,既然是可靠消息,则要求MQ必须支持事务管理,这样才能保证业务前后一致性。 1、Rocke...
- 下一篇
使用深度学习模型创作动漫故事,比较LSTM和GPT2的文本生成方法
这个项目的动机是想看看在短短的几年时间里NLP领域的技术已经走了多远,特别是当它涉及到生成创造性内容的时候。通过生成动画概要,我探索了两种文本生成技术,首先是使用相对陈旧的LSTM,然后使用经过微调的GPT2。 在这篇文章中,您将看到AI创建这种废话开始的过程。。。 A young woman capable : a neuroi laborer of the human , where one are sent back home ? after defeating everything being their resolve the school who knows if all them make about their abilities . however of those called her past student tar barges together when their mysterious high artist are taken up as planned while to eat to fight ! 这件艺术 A young woman named Ha...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Windows10,CentOS7,CentOS8安装Nodejs环境
- 设置Eclipse缩进为4个空格,增强代码规范
- CentOS7设置SWAP分区,小内存服务器的救世主
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS8安装Docker,最新的服务器搭配容器使用
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Hadoop3单机部署,实现最简伪集群