Linux技术-信号
目的
- 熟练使用kill命令管理进程。
- 熟练掌握与信号相关的操作,包括阻塞信号、捕获信号,以及实现这些操作的接口。
- 掌握解决信号机制中出现的时序竞态问题的方法。
- 掌握通过信号机制回收多个子进程的方法。
内容 - 练习使用kill命令管理进程
①使用vi编辑程序,该程序可启动一个父进程和5个子进程,且父子进程都一直处于运行态。
②执行上述程序,使用kill命令对该程序创建的进程执行挂起、终止等操作。
③使用kill命令对该程序创建的整个进程组中的进程进行操作。
- 屏蔽指定信号,实现信号阻塞
①编写程序,在程序中通过设置进程的信号集,屏蔽3号信号SIGQIUT,并在循环中不断
打印信号集中信号的状态。
②编写程序,在程序中通过signal()函数屏蔽3号信号SIGQIUT。
- 捕获指定信号,执行自定义的信号处理函数
①编写程序,在程序中通过signal()函数捕获7号信号SIGBUS,若成功捕获,
则打印“catch SIGBUS”。
②编写程序,在程序中通过sigaction()函数捕获7号信号SIGBUS,若成功捕获,
则打印“catch SIGBUS”。
③编写程序,实现父子间进程同步,要求子进程等待父进程执行完毕后再结束运行。
- 使用sigsuspend()函数解决时序竞态问题
编写程序,在程序中实现sleep()函数的功能,并通过自定义的sleep()函数使
进程沉睡一定时间,沉睡结束后打印“x seconds passed\n”。
- 使用SIGCHLD信号实现多个子进程的回收
编写程序,在程序中创建多个子进程,使父进程利用信号机制,非阻塞地回收所
有子进程。
1、kill命令
使用vi编辑程序,在程序中创建多个子进程,并保证程序启动后创建的
父子进程能一直运行。
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <pthread.h>
5 int main()
6 {
7 pid_t pid;
8 int i;
9 for(i=0;i<5;i++)
10 {
11 if((pid=fork())==0)
12 break;
13 }
14 if(pid<0){
15 perror("fork error.\n");
16 exit(0);
17 }
18 else if(pid==0){ //child
19 while(1){}
20 }
21 else if(pid>0){
22 while(1){}
23 }
24 return 0;
25 }
编译程序test_kill.c并执行,在终端通过kill命令对其启动的进程进行操作。
gcc test_kill.c –o test_kill ——编译程序
./test_kill & ——在后台执行
ps ao stat,pid,pgrp,command | grep ./test_kill
——查看test_kill启动的进程,依次打印状态、pid、进程组id和
启动进程的命令,打印结果如下:
使用“ps ao stat,pid,pgrp,command | grep ./test_kill”命令,打印结果如下:
kill -1 3912 ——发送1号信号到3912
kill -2 3913 ——发送2号信号到3913
kill -19 3914 ——发送19号信号到3914
使用“ps ao stat,pid,pgrp,command | grep ./test_kill”命令,打印结果如下:
kill -19 -3911
发送信号到进程组3911,此时使用“ps ao stat,pid,pgrp,command | grep ./test_kill”
命令,打印结果如下:
kill -9 -3911
——发送信号到进程组3911,此时使用“ps ao stat,pid,pgrp,command | grep ./test_kill”
命令,打印结果如下:
2、 屏蔽指定信号,实现信号阻塞
1 编写程序,在程序中通过设置进程的信号集,屏蔽3号信号SIGQUIT,
并在循环中不断打印信号集中信号的状态。
预备知识: sigemptyset()、sigfillset()、sigaddset()、sigdelset()、sigismember()。
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <signal.h>
4 #include <unistd.h>
5 void printset(sigset_t *ped)
6 {
7 int i;
8 for(i=1;i<32;i++)
9 {
10 if((sigismember(ped,i)==1))
11 putchar('1');
12 else
13 putchar('0');
14 }
15 printf("\n");
16 }
17 int main()
18 {
19 sigset_t set,oldset,ped;
20 sigemptyset(&set);
21 sigaddset(&set,SIGQUIT);
22 sigprocmask(SIG_BLOCK,&set,&oldset);
23 while(1){
24 sigpending(&ped);
25 printset(&ped);
26 sleep(1);
27 }
28 return 0;
29 }
编译程序并执行,通过快捷键组合Ctrl+\发送3号信号到进程,程序的执行结果如下:
Ctril+C才能终止程序。
② 编写程序,在程序中通过signal()函数屏蔽3号信号SIGQUIT。
通过signal()函数实现信号屏蔽
预备知识:signal()函数
捕获指定信号,执行自定义的信号处理函数
``写程序,在程序中通过signal()函数捕获7号信号SIGBUS,若成功捕获,则打印“catch SIGBUS”。
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <signal.h>
4 #include <unistd.h>
5 void func(int signo)
6 {
7 if(signo==SIGBUS)
8 printf("catch signal SIGBUS\n");
9 }
10 int main()
11 {
12 signal(SIGBUS,func);
13 while(1)
14 {
15 printf("hello itlanmao\n");
16 sleep(2);
17 }
18 return 0;
19 }
编写程序,在程序中通过sigaction()函数捕获2号信号SIGINT,若成功捕获
,则打印“catch SIGINT”。
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <signal.h>
4 #include <unistd.h>
5
6 void func(int signo)
7 {
8 if(signo==SIGBUS)
9 {
10 printf("catch signal SIGBUS\n");
11 }
12 }
13 int main()
14 {
15 struct sigaction act,oldact;
16 act.sa_handler=func;
17 sigemptyset(&act.sa_mask);
18 sigaddset(&act.sa_mask,SIGBUS);
19 act.sa_flags=0;
20 sigaction(SIGBUS,&act,&oldact);
21 while(1)
22 {
23 printf("hello itlanmao\n");
24 sleep(2);
25 }
26 return 0;
27 }
编写程序,实现父子间进程同步,要求父进程中使用循环打印3次提示信息,
子进程等待父进程中的提示信息打印完毕后再结束运行。
预备知识:signal()函数、kill()函数
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <signal.h>
4 #include <unistd.h>
5 #include <sys/wait.h>
6 int k1;
7 void func1(int signo)
8 {
9 if(signo==SIGUSR2)
10 k1=0;
11 }
12 int main()
13 {
14 int k,p1;
15 while((p1=fork())==-1);
16 if(p1>0)
17 {
18 for(k=1;k<4;k++)
19 {
20 printf("parent:pid = %d\n",getpid());
21 sleep(1);
22 }
23 kill(p1,12);
24 wait(0);
25 printf("OK!\n");
26 exit(0);
27 }
28 else
29 {
30 signal(12,func1);
31 k1=1;
32 while(k1==1)
33 {
34 printf("I'm child\n");
35 sleep(1);
36 }
37 printf("Child exited!\n");
38 exit(0);
39 }
40 return 0;
41 }
使用sigsuspend()函数解决时序竞态问题
编写程序,在程序中实现sleep()函数的功能,并通过自定义的sleep()函数使进程沉睡一定时间,
沉睡结束后打印“x seconds passed\n”。
预备知识:alarm()函数、sigsuspend()函数
1 #include <stdio.h>
2 #include <signal.h>
3 #include <stdio.h>
4 #include <unistd.h>
5
6 void sig_alrm(int signo)
7 {
8 if (signo == SIGALRM)
9 printf("sleep...\n");
10 }
11 unsigned int mysleep(unsigned int seconds)
12 {
13 struct sigaction newact, oldact;
14 sigset_t newmask, oldmask, suspmask;
15 unsigned int unslept;
16 newact.sa_handler = sig_alrm;
17 sigemptyset(&newact.sa_mask);
18 newact.sa_flags = 0;
19 sigaction(SIGALRM, &newact, &oldact);
20 sigemptyset(&newmask);
21 sigaddset(&newmask, SIGALRM);
22 sigprocmask(SIG_BLOCK, &newmask, &oldmask);
23 alarm(seconds);
24 suspmask = oldmask;
25 sigdelset(&suspmask, SIGALRM);
26 sigsuspend(&suspmask); //挂起进程,等待信号递达
27 unslept = alarm(0);
28 sigaction(SIGALRM, &oldact, NULL);
29 sigprocmask(SIG_SETMASK, &oldmask, NULL);
30 return unslept;
31 }
32 int main()
33 {
34 while (1){
35 mysleep(2);
36 printf("two seconds passed\n");
37 }
38 return 0;
39 }
使用SIGCHLD信号实现多个子进程的回收
编写程序,在程序中创建多个子进程,使父进程利用信号机制,非阻塞地回收所有子进程。
预备知识:fork()、sigaction()函数
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <sys/wait.h>
5 #include <signal.h>
6 void sys_err(char *str)
7 {
8 perror(str);
9 exit(1);
10 }
11 void do_sig_child(int signo) //信号处理函数
12 {
13 int status;
14 pid_t pid;
15 while ((pid = waitpid(0, &status, WNOHANG)) > 0) {//判断子进程状态
16 if (WIFEXITED(status))
17 printf("child %d exit %d\n", pid, WEXITSTATUS(status));
18 else if (WIFSIGNALED(status))
19 printf("child %d cancel signal %d\n", pid, WTERMSIG(status));
20 }
21 }
22 int main(void)
23 {
24 pid_t pid;
25 int i;
26 for (i = 0; i < 5; i++) {
27 if ((pid = fork()) == 0) //创建一个子进程
28 break;
29 else if (pid < 0) //容错处理
30 sys_err("fork");
31 }
32 if (pid == 0) { //子进程执行流程
33 int n = 1;
34 while (n--) {
35 printf("child ID %d\n", getpid());
36 sleep(1);
37 }
38 return i + 1;
39 }
40 else if (pid > 0) { //父进程执行流程
41 struct sigaction act;
42 struct sigaction act;
43 act.sa_handler = do_sig_child;
44 sigemptyset(&act.sa_mask);
45 act.sa_flags = 0;
46 sigaction(SIGCHLD, &act, NULL); //注册捕获函数
47 while (1) { //保证父进程运行
48 sleep(1);
49 }
50 }
51 return 0;
52 }