Linux——信号

信号概念
是进程 间通讯的一种有限制的方式。它是一种异步的通知机制,用来提醒进程一个事件已经发生。当一个信号发送给一个进程,操作系统中断了进程正常的控制流程,此时,任何非原子操作都将被中断。如果进程定义了信号的处理函数,那么它将被执行,否则就执行默认的处理函数。

我们可以通过命令kill -l来查看所有的信号


哈哈哈哈乍一看会以为是64个信号,但是仔细看没有31和32信号,所以其实只有62个信号啦

产生信号的方式
  1. 用户在终端下按下某些键的时候,就会给正在运行的进程发送一个信号,例如 当我们按下ctrl+c的时候,就会产生一个SIGINT信号,然后按下一个ctrl+z会产生一个SIHTSTP,ctrl+\会产生SIGQUIT信号
  2. 硬件异常产生信号:当我们运行一个除以0的程序的时候,CPU的运算器会产生异常,内核将这个异常解释为SIGFPE信号发送给进程,再比如当我们访问非法内存时,MMU会产生异常,内核将此异常解释为一个SIGSEGV信号发送给进程
  3. 用系统函数kill 可以给一个进程发送信号


调用系统函数给进程发送信号
kill(给一个指定的进程发送信号) -信号的标号或者信号 进程的pid,raise(给当前进程发送信号),就不需要指定pid了直接发送信号
例:kill -9 24344

由软件条件产生信号
我们了解一下SIGALRM信号,alarm函数就可以产生SIGALRM信号,通过alarm设置一个闹钟就是告诉内核多长时间之后会给进程发送SIGALRM信号,该信号的默认动作是终止进程
然后我们可以写一个代码来测试一下
#include<stdio.h>
#include<unistd.h>

int main()
{
  size_t  count = 0;
  //让程序执行三秒之后发送一个SIGALRM信号,让程序返回
  alarm(3);
  while(1)
  {
    count += 1;
    printf("%lu ",count);
  }
  return 0;
}
我们会发现程序运行3秒后直接退出,就是进程在3秒后收到了SIGALRM信号使进程退出

阻塞信号
信号的其他相关概念
  1. 实际执行信号的处理动作叫做信号递达
  2. 信号从产生到递达状态称为信号未决
  3. 进程可以选择阻塞哪些信号
  4. 被阻塞的信号会一直处于未决状态,直到进程解决对信号的阻塞状态,信号才会执行递达的动作



void MyHandler(int sig)
{
  printf("sig = %d",sig);
}

void PrintSigset(sigset_t *set)
{
  int i = 1;
  for(; i<=31 ;i++)
  {
    if(sigismember(set,i))
    {
      printf("1");
    }
    else{
      printf("0");
    }
  }
  printf("\n");

}
int main()
{
  //捕捉SIGINT信号
  signal(SIGINT,MyHandler);
  //吧SIGINT信号屏蔽掉
  sigset_t set;
  sigset_t oset;
  sigemptyset(&set);
  sigaddset(&set,SIGINT);
  sigprocmask(SIG_BLOCK,&set,&oset);

  //循环读取,未决信号集

  while(1)
  {
    sigset_t pending_set;
    sigpending(&pending_set);
    PrintSigset(&set);
    sleep(1);
  }
  return 0;
}
我们在这定义了一个未决信号集,然后循环的读取此位图,当我们收到了一个SIGINT信号之后在循环读取我们就会发现第二位的位图变成了1

因为当我们一旦将2号信号屏蔽了之后我们的阻塞信号集的第二位的位图就会被置1,所以一旦我们在给此进程发送2号信号的话那么此进程的未决信号集的第二位的位图就也会变成1


在这里我们重点的了解一下这个信号的捕捉!
我们画个图了解一下
举个栗子, 我们知道当我们解引用一个NULL指针的时候会发生段错误,那其实是,操作系统在执行代码的时候,硬件设备MMU发现了错误后进行入内核,进行处理给进程发送一个11号信号,告诉进程发生了段错误,进程执行相应的处理函数一般是默认退出,所以程序直接崩溃退出


可重入函数
一个函数被不同的控制流执行,有可能在一个控制流还没有结束的时候,另一个控制流就再次进入了该进程,这函数就是可冲入函数

如果一个函数满足如下条件之一就成为不可重入函数
  1. 调用了malloc/free,malloc用全局链表来管理堆的
  2. 调用了标准I/O库函数

volatile限定符
保证内存的可见性

先看下面的代码
#include<stdio.h>
#include<signal.h>

//保证内存的可见性
volatile int g_val = 1;
void MyHandler(int sig)
{
  (void) sig;
  g_val = 0;
}
int main()
{
  //信号处理函数
  signal(SIGINT,MyHandler);
  while(g_val);
  return 0;
}

当我们捕捉到SIGINT信号的时候,进程退出,而当我们将编译器的优化级别开到最大O3的时候,我们会发现即使我们收到了SIGINT信号进程也不会退出,那这是为什么呢?

其实当我们将编译器的优化级别设到最高的时候,编译器一次将g_val的值读入内存的时候,他就不会再去内存读取这个值,因为一次一次的内存访问效率特别低,而且如果程序是单一的执行流的话,这个值根本是不会在改变的,那么编译器没有必要再一次一次的去内存在读取这个值,而且还会提高程序运行的效率,那么,为了解决这个问题, c语言就有了一个限定符volatile,保证内存的可见性,那么即使制定了优化级别,也不会优化掉对此变量的读写

SIGCHLD信号
如果我们一下创建多个子进程,然后父进程wait子进程的时候,如果同时有多个子进程完成,那么父进程只会处理一个子进程其他的都会变成僵尸进程,那其实我们呢就可以用waitpid函数来一个一个非阻塞式的等待,然后当许多子进程同时完成时,调用waitpid函数一个一个的回收,然后继续等待下一次SIGCHLD信号的到达,然后继续调用信号处理函数处理子进程
void MyHandler(int sig)
{
  (void) sig;
  //int ret = wait(NULL);
  //循环处理当前一起结束的进程
  while(1)
  {
    int ret = waitpid(-1,NULL,WNOHANG);
    if(ret > 0)
    {
      printf("waitpid %d\n",ret);
      continue;
    }
    //还有其他的未结束的进程,直接返回,因为接下来的还有其他的进程会触发此处理函数
    else if(ret == 0)
    {
      break;
    }
    //子进程都结束了
    else{
      break;
    }
  }
}
int main()
{
  //忽略掉SINCHID信号,自动销毁子进程
  signal(SIGCHLD,MyHandler);
  int i = 0;
  for(;i<20;i++)
  {
    pid_t ret = fork();
    if(ret < 0)
    {
      perror("fork error\n");
      return 1;
    }
    //子进程打印pid
    if(ret == 0)
    {
      printf("child = %d\n",getpid());
      sleep(3);
      exit(0);
    }
    //父进程直接执行下一次循环
  }
  while(1)
  {
    printf("father work!\n");
    sleep(1);
  }
return 0;
}


但是其实这种方法还不是很优雅,我们可以用一种更优雅的方式, 我们可知直接把SIGCHLD信号忽略掉,然后进程就会自动的将这些子进程销毁掉
int main()
{
  //忽略掉SINCHID信号,自动销毁子进程
  signal(SIGCHLD,SIG_IGN);
  int i = 0;
  for(;i<20;i++)
  {
    pid_t ret = fork();
    if(ret < 0)
    {
      perror("fork error\n");
      return 1;
    }
    //子进程打印pid
    if(ret == 0)
    {
      printf("child = %d\n",getpid());
      sleep(3);
      exit(0);
    }
    //父进程直接执行下一次循环
  }
  while(1)
  {
    printf("father work!\n");
    sleep(1);
  }
}


猜你喜欢

转载自blog.csdn.net/qq_36767247/article/details/80560848