Linux信号量、进程信号

 

目录

前言

消息队列简述

信号量简述

进程信号

signal函数

sigaction函数

sigprocmask函数


前言

    首先了解一下system-V标准,该标准与消息队列、共享内存和信号量有关。这篇不说共享内存,我在之前的文章里说过了,也不重点说消息队列因为这个不常用,只简单提一下,这回主要写linux的信号量和进程信号,以及相关函数操作。

消息队列简述

    使用消息队列来实现数据传输。

    本质:是内核中的优先级队列。

    实现:多个进程通过访问同一个消息队列,以添加数据节点和获取数据节点来实现通信(这个方式是全通工)

    函数:int msgget(key_t key,int msgflg);//创建或打开消息队列

        key:是个键值, 多个进程可通key值来访问同一个消息队列.函数的返回值id 与key 有关。

        msgflag:权限标志位

     函数返回值:成功返回消息队列标识符,失败返回-1。

    消息队列生命周期随内核,自带同步与互斥,其传输是一种数据块传输。

信号量简述

    作用:实现进程间的同步与互斥

    本质:一个计数器+pcb等待队列

    P操作:对计时器减一,判断计数器是否大于等于0,如果是则返回,如果失败则阻塞。

    V操作:对计数器加一,判断计数器如果小于等于0,则唤醒一个等待的进程

    同步的实现:

        通过计数器对共享资源进行计数,在获取资源之前进行P操作,产生一个资源后进行V操作。

    互斥的实现:

        初始化计数器为1,表示资源只有一个,访问前进行P操作,访问完后进行V操作。

进程信号

    进程信号不等于信号量。

    概念:软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。在软件层次上是对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。信号机制除了基本通知功能外,还可以传递附加信息。

    使用kill -l指令可查询所有信号

      其中1~31是非可靠非实时信号

      34~64是可靠实时信号(在信号堆积时会优先处理实时信号)

 

    进程信号的产生:

        硬件产生:比如ctrl+c(SIGINT),ctrl+z(SIGTSTP),ctrl+\(SIGQUIT)等。

        软件产生:kill-sig pid命令,kill(),raise()等

    注册:让进程中做标记,让进程知道自己收到了某个信号。

               操作:在进程pcb中有个未决信号集pending,信号的注册就是在未决信号集中标记信      号以及添加信号的信息节点。  

               非可靠信号:若信号已经注册,则不作任何操作
               可靠信号:无论信号是否注册,都会进行注册

    注销:在信号被处理前,消除信号存在的痕迹(主要防止信号被重复定义)

               操作:在pcb中删除信号信息节点,重置位图

               非可靠信号:删除节点信息,直接位图重置
               可靠信号:删除信息节点之后,确定没有相同节点才会重置位图

    如果一个进程不能被kill杀死,那么有以下几种原因:

        1、这是个僵尸进程

        2、这个进程是停止态(对信号不处理)

        3、信号被阻塞或被自定义处理

signal函数

    信号的处理,使用signal函数

    sighandler_t signal(int signum,sighandler_t handler);

        signum:信号值  handler:信号要新指定的处理方式

        handler:SIG_DEL-默认; SIG_IGN-忽略;自定义

    返回值:成功返回信号原来的处理方式;失败返回-1(SIG_ERR)   

    示例:使用signal函数自定义SIGINT信号的处理方式

static int tem=0;

void func(){ //有信号时调用函数
 if(1==tem++){ //当第二次接收到信号时
   printf("exit success!\n");
   exit(0);    //第二次接收信号也就是按下第二个ctrl+c时退出
 }
 else{ //第一次接收信号时
   printf("hellow\n");
 }
}

int main(){
 signal(SIGINT,func); //func函数用于处理SIGINT信号(ctrl+c)

 while(1){
   sleep(2);
   printf("hello thankyou thankyou very much\n"); //无SIGINT信号时一直循环
 }

 return 0;
}

 

sigaction函数

    int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

        signum:要捕获的信号

        *act:接收到信号之后对信号进行处理的结构体

        *oldact:接收到信号之后,保存原来对此信号处理的各种方式与信号(可用来做备份)。如果不需要备份,此处可以填NULL
 

    示例:使用sigaction函数自定义SIGINT信号的处理方式

//在使用sigaction函数捕捉信号SIGINT,在执行信号捕捉后的处理函数是,屏蔽信号SIGQUIT
//在执行程序后 ctrl+c 会出现 2 signal is catched(2就是2号信号就是SIGINT)
//然后ctrl+\ 进程不会退出,ctrl+z将其停止

void sigcb(int signo){     
 printf("\n%d signal is catched\n",signo);
 while(1);                
}                          

int main(){                
 int ret;                 
 struct sigaction act;  //定义act这个结构体  
 act.sa_handler =sigcb;   //使用sigcb这个函数来处理
 sigemptyset(&act.sa_mask);//将act.sa_mask清0
 sigaddset(&act.sa_mask,SIGQUIT); //添加信号SIGQUIT到act.sa_mask集合
                                 //表示在处理信号时不希望受到其他信号影响
                                 //所以后面按ctrl+\(SIGQUIT)就不会退出
 act.sa_flags=0; //设为0是默认属性

 ret=sigaction(SIGINT,&act,NULL); //捕捉SIGINT信号
 if(ret<0){               
   printf("error\n");                                                            
   exit(1);
 }

 while(1);//一直循环等待捕捉信号

 return 0;
}

 

sigprocmask函数

    信号的阻塞:阻塞一个信号表示收到这个信号后暂时不处理,直到解除阻塞之后进行处理

    int sigprocmask(int how,sigset_t *set,sigset_t *oldset);

        how:要对信号阻塞集合进行的操作类型:以下几种:

                SIG_BLOCK:将set集合中的信号添加到阻塞集合中
                SIG_UNBLOCK:从阻塞集合中移出set集合中的信号
                SIG_SETMAS:将阻塞集合中的信号设置为阻塞集合的信号      

        oldset:用于保存修改前阻塞集合的信息,不使用置空即可

    返回值:成功0,失败-1

    示例:使用sigprocmask函数阻塞2号信号和40号信号,要求:阻塞2号信号和40号信号, 分别给进程发送5次2号信号和5次40号信号,观察结果

   

void sigcb(int signo){
  printf("goda signal:%d\n",signo);
}

int main(){
  signal(SIGINT,sigcb);
  signal(SIGRTMIN+6,sigcb);  //修改2号和40号信号的处理方式为sigcb函数
  sigset_t set,old;
  sigemptyset(&set);  //清空集合
  sigemptyset(&old);
  sigfillset(&set);   //将所有信号添加到set中
  sigprocmask(SIG_BLOCK,&set,&old);  //阻塞set中的信号

  int i=5;  
  while(i--){  //给5次2号信号
    kill(getpid(),2);                                                   
  }
  int j=5;
  while(j--){  //给5次40号信号
    kill(getpid(),40);
  }

  printf("Please input 'enter' to continue\n");
  getchar();

  //sigprocmask(SIG_SETMASK,&old,NULL); //按下回车后运行到此则解除阻塞
  sigprocmask(SIG_UNBLOCK,&set,NULL);

  while(1){  //这里执行完程序后不退出,可按ctrl+\退出
    sleep(1);
  }

  return 0;
}

    由上可见2号信号只打印了一次,40号信号打印了5次,因为2号信号是不可靠不实时信号,未决集合中存在,只会注册一次,产生再多也只会处理一次。而40号信号是实时信号,产生几次,注册几次。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值