Day30、信号、可重入函数、闹铃、信号的阻塞

本文介绍了进程间通信的基础知识,包括vfork创建子进程、管道通信原理及应用,并深入探讨了信号的概念、信号处理机制、信号阻塞等高级主题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

昨天补充:

vfork :用vfork创建子进程,当子进程创建成功的时候,先执行子进程,子进程执行完毕再执行父进程。

管道:(有名管道和无名管道)

无名管道应用于具有亲缘关系的进程间通讯

pipe(2) 产生两个文件描述符

fd[0]指向管道的读端

fd[1]指向管道的写端

一个进程需要关闭读端,另一个进程需要关闭写端。

 

一、            信号

1、 信号的概念

进程之间可以相互发送信号,这使信号成为一种进程间通信的基本手段

中断:硬件中断、软件中断

软中断:  信号是提供异步时间处理机制的软件中断。

2、查看信号

kill –l

tarena@tarena-virtual-machine:~$kill -l

 1) SIGHUP   2) SIGINT    3) SIGQUIT 4) SIGILL    5) SIGTRAP

 6) SIGABRT 7) SIGBUS   8) SIGFPE   9) SIGKILL  10)SIGUSR1

11) SIGSEGV 12) SIGUSR2 13)SIGPIPE  14) SIGALRM       15) SIGTERM

16) SIGSTKFLT    17) SIGCHLD       18)SIGCONT       19) SIGSTOP 20) SIGTSTP

21) SIGTTIN  22) SIGTTOU 23)SIGURG  24) SIGXCPU       25) SIGXFSZ

26) SIGVTALRM   27) SIGPROF 28)SIGWINCH     29) SIGIO      30) SIGPWR

31) SIGSYS   34) SIGRTMIN      35)SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3

38) SIGRTMIN+4  39) SIGRTMIN+5  40)SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10      45) SIGRTMIN+11 46) SIGRTMIN+12       47)SIGRTMIN+13  48) SIGRTMIN+14    49) SIGRTMIN+15      50) SIGRTMAX-14       51)SIGRTMAX-13       52) SIGRTMAX-12  53) SIGRTMAX-11   54) SIGRTMAX-10      55)SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7 58) SIGRTMAX-6  59) SIGRTMAX-5  60)SIGRTMAX-4       61) SIGRTMAX-3  62) SIGRTMAX-2 63) SIGRTMAX-1  64) SIGRTMAX    

共有62个信号,信号名,信号编号

 

信号的产生、信号的处理、信号的阻塞、信号的递达

从信号的产生到信号的递达之前,这个期间称为信号未决

信号分为两种:

可靠信号:在进程正在处理信号的时候,如果有同一个信号编号的信号多次递达,对每一次的递达都做处理,不会造成信号丢失,这样的信号叫做可靠信号。34--64

不可靠信号:在进程正在处理信号的时候,如果有同一个信号编号的信号多次递达,只能处理一个,这样就会造成信号丢失,这样的信号叫做不可靠信号。1--31

3、 信号处理(捕获)

SIG_DEL 默认处理   终止进程

SIG_IGN 信号忽略

交给用户处理,希望用户编写信号处理函数

 

4、 如何向进程注册信号处理函数

Signal (2) 信号处理函数

#include<signal.h>

typedef void(*sighandler_t)(int);

sighandler_tsignal(int signum, sighandler_t handler);

void(*sighandler_t)(int);

参数:

signum:信号的编号

handler:信号处理函数的入口。

功能:给信号设置信号处理函数。SIG_DFL  SIG_IGN 自定义

举例:signal_i.c

  1 #include<stdio.h>

  2 #include<signal.h>

  3 int main(){

  4    //使用signal函数向系统注册编号为2的信号处理函数

  5    signal(2,SIG_IGN);

  6    while(1);

  7    return 0;

  8 }

tarena@tarena-virtual-machine:~/day30$./a.out

^C^C   (终止不了,)

^\退出 (核心已转储)    (采用CTRL+\进行终止)

可以用kill –信号编号 id 进行终止。

说明:编号为2的信号名称是SIGINT ,相当于CTRL+C,作用中断进程

signal(2,SIG_IGN);意思是对编号为2的SIGINT功能采取SIG_IGN(信号忽略)功能

 

 

  1 #include<stdio.h>

  2 #include<signal.h>

  3 //用户自定义的信号处理函数

  4 void handle(int signum){

  5     printf("receivesignal %d\n",signum);

  6 }

  7 int main(){

  8    //使用signal函数向系统注册编号为2的信号处理函数

  9    signal(2,handle);

 10    while(1);

 11    return 0;

 12 }

tarena@tarena-virtual-machine:~/day30$./a.out

^Creceive signal2

^Creceive signal2

^\退出 (核心已转储)

对编号为2(功能是终止)的信号改变注册其功能,执行ctrl+c相当于执行handle这个函数。

 

继承:子进程继承父进程的信号

举例:

  1 #include<stdio.h>

  2 #include<signal.h>

  3 #include<sys/types.h>

  4 #include<unistd.h>

  5 //用户自定义的信号处理函数

  6 void handle(int signum){

  7    printf("process id %d",getgid());

  8    printf("receive signal %d\n",signum);

  9 }

 10 int main(){

 11    pid_t pid;

 12    //使用signal函数向系统注册编号为2的信号处理函数

 13    signal(2,handle);

 14    pid=fork();

 15    if(pid<0){

 16        perror("fork");

 17        return 1;

 18     }

 19     while(1);

 20    return 0;

 21 }

tarena@tarena-virtual-machine:~/day30$./a.out

^C process id3195

receive signal 2

process id 3194

receive signal 2

执行ctrl+c 执行一次程序

tarena@tarena-virtual-machine:~$ps -aux

tarena    3194 97.8 0.0   2004   284 pts/0    R+  11:17   0:49 ./a.out

tarena    3195 97.4 0.0   2004    60 pts/0   R+   11:17   0:49 ./a.out

tarena@tarena-virtual-machine:~$kill -9 3195

tarena@tarena-virtual-machine:~$kill -9 3194

已杀死

5、 信号处理流程

a)      进程正常运行的时候,收到信号

b)     切换到内核态

c)      进程从内核态切换回用户态之前,先检查是否有信号到达。如果有信号到达,调用用户态的信号处理函数,信号处理完毕,调用sigreturn切换回内核态。如果没有信号到达,切换回用户态继续进程的执行。

补充

1. 给进程发送信号

       kill –信号编号 id

 

  1 #include<stdio.h>

  2 int main(){

  3    while(1);

  4    return 0;

  5 }

tarena@tarena-virtual-machine:~$ps –aux

tarena    2937 98.9 0.0   2000   284 pts/0   R+   10:00   0:31 ./a.out

tarena@tarena-virtual-machine:~$kill -9 2937

已杀死

2. 编码规范

函数命名或者变量命名  单词与单词之间用下划线区分

 

6、 可重入函数

一个函数正在执行,此时来了一个信号,信号的处理函数恰好是这个函数,这个函数又被调用,这样的函数成为可重入函数。

可重入函数是只允许有局部变量的函数。(存在栈里,用栈帧管理,每次调用函数都释放)

在可重入函数里,不允许出现malloc、errno、environ

7、 使用软件给进程发送信号

kill (2)

#include<sys/types.h>

#include<signal.h>

int kill(pid_tpid, int sig);

功能:给指定的进程发送信号

参数:

pid :接受信号的进程

sig :信号编号

返回值:-1表示失败,erron被设置

0代表成功

 

 

raise (3)

功能:给调用raise的进程发送信号sig

参数:sig : 指定的信号编号

 

举例:signal.c

  1 #include<stdio.h>

  2 #include<signal.h>

  3 #include<sys/types.h>

  4 #include<unistd.h>

  5 //用户自定义的信号处理函数

  6 void handle(int signum){

  7    printf("process id %d\n",getpid());

  8    printf("receive signal %d\n",signum);

  9 }

 10 int main(){

 11    pid_t pid;

 12    //使用signal函数向系统注册编号为2的信号处理函数

 13    signal(2,handle);

 14    pid=fork();

 15    if(pid<0){

 16        perror("fork");

 17        return 1;

 18     }

 19    while(1);

 20    return 0;

 21 }

在另一个终端下:vi kill.c

  1 #include<stdio.h>

  2 #include<sys/types.h>

  3 #include<unistd.h>

  4 int main(void){

  5    int i;

  6    for(i=0;i<3;i++){

  7        //第一个参数是进程的pid,这个进程接受信号

  8        kill(3352,2); 

  9        sleep(5);  

 10     }

 11    return 0;

 12 }

一个进程给另一个进程发送信号

tarena@tarena-virtual-machine:~$./a.out

pid=3352

signum :2

 

自己给自己发信号

  1 #include<stdio.h>

  2 #include<sys/types.h>

  3 #include<unistd.h>

  4 #include<signal.h>

  5 void handle(int signum){

  6    printf("signum :%d\n",signum);

  7 }

  8 int main(void){

  9    signal(2,handle);

 10    printf("pid=%d\n",getpid());

 11    while(1){

 12        raise(2);

 13        sleep(5);

 14     }

 15    return 0;

 16 }

 

alarm(2)

#include<unistd.h>

unsigned intalarm(unsigned int seconds);

参数:seconds 指定的时间

功能:在seconds指定的秒数之后,给自己的进程发送SIGALRM信号。如果进程对这个信号采用默认处理,则这个进程结束。

  举例:alarm.c

1#include<stdio.h>

  2 #include<unistd.h>

  3 int main(void){

  4    int i;

  5    //设置闹钟为1秒

  6    alarm(1);

  7     for(i=0;;i++){

  8        printf("%d\n",i);

  9     }

 10 }

65648

65649

闹钟   电脑一秒执行6万次

如果seconds指定为0,代表取消了alarm的设置

  1 #include<stdio.h>

  2 #include<unistd.h>

  3 int main(void){

  4    int i;

  5    //设置闹钟为1秒

  6    alarm(10);

  7    for(i=0;;i++){

  8        alarm(0);

  9        printf("%d\n",i);

 10     }

 11 }

无限循环

pause (2)

#include <unistd.h>

int pause(void);

功能:使被调用的进程挂起(sleep),直到有信号产生

返回值:-1代表错误

举例:pause.c

  1 #include<stdio.h>

  2 #include<unistd.h>

  3 #include<signal.h>

  4 //信号处理函数

  5 void handle(int signum){

  6

  7 }

  8 int main(){

  9    //向进程注册SIGALRM信号的处理函数

 10    signal(SIGALRM,handle);

 11    //设置闹钟的时间为2秒

 12    alarm(2);

 13    //挂起当前进程

 14    pause();

 15    //取消闹钟的设置

 16    alarm(0);

 17    return 0;

 18 }

 

 

自己封装一个函数,实现sleep(3)的功能

  1 #include<stdio.h>

  2 #include<unistd.h>

  3 #include<signal.h>

  4 //信号处理函数

  5 void handle(int signum){

  6    printf("...\n");

  7 }

  8 unsigned int mysleep(unsigned int seconds){

  9    unsigned int ret;

 10    ret=alarm(seconds);//3秒发一次SIGALRM信号,这个函数的作用就是发信号

 11    pause();

 12    alarm(0);

 13    return ret;

 14 }

 15 int main(){

 16    //向进程注册SIGALRM信号的处理函数,发送SIGALRM信号时,调用handle这个函数

 17    signal(SIGALRM,handle);

 18    while(1){

 19        mysleep(3);

 20     }

 21    return 0;

 22 }

3秒钟打印一次

 

sleep(3)  休眠几秒

#include<unistd.h>

Unsigned intsleep(unsigned int seconds);

usleep(3)  休眠几微妙

#include<unistd.h>

intusleep(useconds_t usec);

8、 信号的阻塞

Sigset_t   信号集  类型   以下函数都是对信号集合做处理

#include<signal.h>

intsigemptyset(sigset_t *set);

intsigfillset(sigset_t *set);

intsigaddset(sigset_t *set, int signum);

intsigdelset(sigset_t *set, int signum);

intsigismember(const sigset_t *set, int signum);

      

       sigset_t  p;//声明一个sigset_t集合类型的变量p

       sigemptyset(*p);//将p的内容清空

sigfillset(&p);//将p的内容全部置1

sigaddset();将信号signum添加到集合里

sigdelset();将信号signum从信号set的集合里清除

sigismember();判断信号signum是不是集合set的有效成员

 

设置信号的屏蔽 (阻塞)

Sigprocmask (2)

#include <signal.h>

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

功能:设置信号的阻塞

参数:

set:新的信号集

oldset:旧的信号集

how:SIG_BLOCK、SIG_UNBLOCK、SIG_SETMASK

cur:00010101

set:01000101

或操作: 01010101

 

 

       举例:promask.c   把2号信号(ctrl+c)给屏蔽, 即 使ctrl +c这个功能不能用

  1#include<stdio.h>

  2#include<signal.h>

  3int main(){

 4     sigset_t set;

 5     //清空set

 6     sigemptyset(&set);

 7     //将信号添加到集合set里

 8     sigaddset(&set,SIGINT);

 9     sigaddset(&set,SIGQUIT);

 10     //设置信号的屏蔽

 11    sigprocmask(SIG_SETMASK,&set,NULL);

 12    while(1);

 13    return 0;

 14}tarena@tarena-virtual-machine:~/day30$ ./a.out

^C^C

^\^\^\^\

^C

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值