信号(1)

一 信号是什么

  信号就是程序中断的一种方式,属于软件中断。
  中断就是终止当前的代码,转而执行其他的代码。中断有软件中断和硬件中断。

  Unix/Linux系统中,可以使用信号中断当前代码,转而执行其他的代码。常见的信号:
  ctrl+c、ctrl+\、段错误、总线错误、浮点数例外
  int/0 浮点数例外(出错,程序被信号中断),double/0 无穷大 (不出错)

  信号本质上就是一个整数(非负数),Unix常用信号1-48,Linux常用信号1-64。每个信号都有一个宏名称,便于记忆,宏名称都以SIG开头。比如:
  SIGINT就是信号2,ctrl+c 就是信号2

  信号是不连续的,有些信号是不存在的。不同的操作系统,对应信号的值有可能不同,因此在开发中,使用信号的宏名字有更好的通用性。
  信号的产生是无规律的,不知道什么时候会来,因此对于信号的处理采用异步处理。

kill 命令就是针对信号,可以发送信号:
  kill -信号 进程ID - 给某个进程发信号
  kill -l 显示所有的信号

  信号分为可靠信号和不可靠信号两类
  不可靠信号,1-31都是不可靠信号。这种信号不支持排队,有可能丢失。非实时信号。
  可靠信号,34-64都是可靠信号。这种信号支持排队,不会丢失,是 实时信号。


二 信号的处理

Unix/Linux对于信号的处理,有三种方式:
1. 默认处理 - 80%的默认处理都是中断进程。
2. 忽略信号 - 可以忽略信号,不做处理。
3. 自定义处理 - 程序员写 信号处理的代码。
注:
  信号9 不能被忽略,也不能被自定义处理。
  普通用户只能给自己的进程发信号,但root可以给所用用户发信号。

1 signal()函数

  信号的处理方式可以使用signal()/sigaction()注册。
  函数指针 signal(int 信号,函数指针)
  signal 可以设定信号的处理方式,第一个参数是哪个信号,第二个参数是信号的处理方式,包括:
  SIG_IGN - 忽略该信号
  SIG_DFL - 默认处理信号
  一个程序员自定义的函数名 - 自定义处理信号
  错误返回 SIG_ERR

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void fa(int signo){
    printf("发送了信号%d\n",signo);
    //signal(2,SIG_DFL);
}

int main(){
    printf("pid=%d\n",getpid());
    signal(2,fa);//信号2的处理方式 调用fa()
    if(signal(3,SIG_IGN)==SIG_ERR)
    perror("signal"),exit(-1);
    signal(9,fa);//9的处理方式无法更改
    while(1);
}//练习:修改代码,实现第一次信号2 打印
//从第二次开始,恢复到默认处理

2 子进程的信号处理

  fork()创建的子进程完全沿袭父进程对信号的处理方式。父进程忽略子进程也忽略,父进程自定义子进程也自定义。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

void fa(int signo){
  printf("捕获到了信号%d\n",signo);
}

int main(){
    signal(2,fa); 
    signal(3,SIG_IGN);
    pid_t pid = fork();
    if(pid == 0){
        printf("子进程%d开始运行\n",getpid());
        while(1);
    }
    printf("父进程退出\n");
}

  vfork()+exec 创建的子进程,父进程忽略,子进程也会忽略,但父进程是自定义,子进程会改为默认处理。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

void fa(int signo){
    printf("捕获到了信号%d\n",signo);
}

int main(){
    signal(2,fa); 
    signal(3,SIG_IGN);
    pid_t pid = vfork();
    if(pid == 0){
        printf("子进程%d开始运行\n",getpid());
        execl("./proc","proc",NULL);
    }
    printf("父进程退出\n");
}

proc.c
int main(){
  while(1);
}

三 信号的发送

发信号的方式:
1 键盘发送 (少部分信号)
  ctrl+c -> 2 SIGINT
  ctrl+\ -> 3 SIGQUIT
  ctrl+z -> 20 SIGSTP 暂停进程
2 出错 (少部分信号)
  段错误 -> 11 SIGSEGV
  总线错误 -> 7 SIGBUS
  整数除0 -> 8 SIGFPE
3 命令kill (全部信号)
  kill -信号 进程ID
  信号0 用来测试是否有权限发信号
4 信号发送函数 (全部信号)
  raise() kill() alarm() sigqueue() …

1 raise()函数

  raise()函数 只能给自己所在的进程发信号。

  sleep()和usleep()都可以休眠程序,区别:
sleep()被信号打断会返回剩余秒数,usleep()返回-1。sleep()休眠的单位是秒,usleep()是微秒。

#include <stdio.h>
#include <signal.h>

void fa(int signo){
    printf("捕获了信号%d\n",signo);
}

int main(){
    signal(SIGINT,fa);
    int res = usleep(5000000);//sleep(5);
    printf("res=%d\n",res);
    raise(3);
    while(1);
    //pause();//暂停函数,有信号到来时解除暂停
}

2 kill() 函数

int kill(pid_t pid,int sig)
  pid 的值可以是以下4种情况:
  大于0 发给进程ID=pid的某个进程
  = 0 发给和发送进程同组的所有进程
  = -1 发给所有有发送权限的进程
  < -1 发给进程组ID=-pid的进程

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>

void fa(int signo){
  printf("捕获了信号%d\n",signo);
}

int main(){
    pid_t pid = fork();
    if(pid == 0){
      signal(SIGINT,fa);
      while(1);
    }
  printf("父进程给子进程%d发信号2\n",pid);
  kill(pid,SIGINT);
}

killall 进程名称
  可以杀死多个同名进程

3 alarm()函数

  alarm(int sec)是闹钟函数,sec秒之后产生一个SIGALRM信号。
  如果多次调用alarm(),后面的闹钟会替换前面的。
  如果sec = 0 ,取消所有的闹钟
  返回值:
  如果alarm()时,以前设置的闹钟还没有执行,会返回之前的闹钟剩余的秒数;如果以前设置的闹钟执行了,或者没有设置,返回0.

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void fa(int signo){
  printf("捕获了信号%d\n",signo);
  alarm(1);
}

int main(){
    signal(SIGALRM,fa);
    alarm(5);//闹钟函数,5秒以后发信号SIGALRM
    printf("pid=%d\n",getpid());
    sleep(2); 
    int res = alarm(10);
    printf("res=%d\n",res);
    //sleep(2); 
    res = alarm(0);//取消
    //printf("res=%d\n",res);
    while(1);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值