当前位置:首页 > 公众号精选 > 嵌入式云IOT技术圈
[导读]上节我们分享了Linux进程间通信的其中两种方式:管道、消息队列,文章如下: Linux进程间通信(上)之管道、消息队列实践 这节我们就来分享一下Linux的另外两种进程间通信的方式:信号、信号量。 1、信号 我们使用过windows的都知道,当一个程序被卡死的时候不

上节我们分享了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进行操作的。也就是说信号量是协调进程对共享资源操作的,起到了类似互斥锁的作用,但却比锁拥有更强大的功能。

往期精彩

Linux进程通信(上)之管道、消息队列实践

C语言三剑客之《C陷阱与缺陷》一书精华提炼

C语言三剑客之《C专家编程》一书精华提炼

【为宏正名】99%人都不知道的"##"里用法

【Linux系统编程】可重入和不可重入函数

觉得本次分享的文章对您有帮助,随手点[在看]并转发分享,也是对我的支持。

免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭