昨天补充:
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