赞
踩
- #include <stdio.h>
-
- #include <unistd.h>
-
- #include<signal.h>
-
- void f(int sig)
-
- {
-
- printf("catch signal %d\n",sig);
-
- }
-
- int main()
-
- {
- int i=0;
-
- signal(SIGINT,f);
-
- printf("pid=%d\n",getpid());
-
- while(1)
-
- {
-
- printf("%d\n",i++);
-
- sleep(1);
-
- }
-
- return 0;
-
- }
要求:
1)将上题中的signal()函数改为sigaction()函数。
2)编写一段程序,使用系统调用函数fork( )创建两个子进程,再用系统调用函数
signal( )让父进程捕捉信号SIGINT(可以键入ctrl+c 来触发,也可以用kill 命令来
触发),当捕捉到中断信号后,父进程用系统调用函数kill( )向两个子进程发出信
号,子进程捕捉到父进程发来的信号后,分别输出下列信息后终止:
Child process 1 is killed by parent!
Child process 2 is killed by parent!
2.
TIPS:
1.pause()函数是阻塞型的,并且非常重要的一点就是pause是不分信号类型的,只要能够捕捉到任何信号,它就会正确返回。它经常用来判断信号到底有没有到达,因为没有到达就会阻塞。
2.kill()函数是属于一个系统接口,它的用法是用来发送信号,而不是直接就kill掉一个进程。
3.linux系统中专门会建立一个信号队列(实时信号),用来处理接收到的各种信号,但注意,(非实时信号)同一种信号只会处理一次,这个队列中每个格子对应一种信号,同种信号如果有非常多个,也只会处理一次。而实时信号则不会造成信号丢失。
参考博客:https://blog.csdn.net/c1194758555/article/details/52848114
解析信号机制的一篇非常好的博客:https://blog.csdn.net/return_cc/article/details/78845346
基础工作流程:
1.用户可以使用alarm定时器,定时从内核发起信号发送给用户进程(或者使用kill发送信号给某进程)。它的流程大概是这样:
2.接下来是信号的收集以及后续的处理,signal(或者sigaction)函数,用于对指定的信号做指定的处理。如果不对信号处理,那么信号会被操作系统默认处理。
以上两个步骤只是针对32个标准信号的处理。
如果要处理一些特定的用户非常感性趣的信号,一般常用信号集合函数处理
1.创建信号集合,这个集合会专门编写一个处理函数去处理,这个处理函数就是sigaction。
2.信号集合创建完成后并不能马上就去使用信号处理函数,这个集合里可能含有不需要/不希望被处理的函数,因此使用信号屏蔽字sigprocmask去指定集合里面哪些信号被阻塞(信号一旦被阻塞就不能被进程接收到),起到过滤作用
3.使用信号处理函数sigaction处理特定希望处理的信号
4.还可以让进程检测,处理那些被屏蔽的信号,因为被屏蔽后,进程不知道还有哪些信号是存在的。
PS:所谓的屏蔽信号就是阻塞信号。
对于做实验一第二小题,我搞了大概6天,是的,一个实验搞了6天。终于弄通了。上面的例子只是科普下信号的基本知识,但是对于信号执行的机制还不是很懂,下面我用我的实验程序来讲解:
首先的我实验结论是:
1.子进程是以什么样的结果结束的,就必然会返回一个结果给父进程,当然这个结果可以是一个信号值,也可以是一个代表正常结束的0值,如果由于信号原因终止,父进程也会收到信号,即子进程退出时会给父进程发信号。
2.非可靠信号不排队,且执行信号处理程序时,会自动BLOCK当前处理的信号。这句话出自APUE这本书。意思就是,信号处理函数在捕捉到信号时,会执行信号 处理函数,并且,该种信号会被阻塞。这个结论,等下程序会证明。
3.kill不允许自身信号的发送
4.通过下面的宏来判断子进程的状态,尽管实际上编程时是比较少用的:
常用宏函数分为日如下几组:
(1)、 WIFEXITED(status) 若此值为非0 表明进程正常结束。
若上宏为真,此时可通过WEXITSTATUS(status)获取进程退出状态(exit时参数)
(2)、 WIFSIGNALED(status)为非0 表明进程异常终止。
若上宏为真,此时可通过WTERMSIG(status)获取使得进程退出的信号编号
(3)、 WIFSTOPPED(status)为非0 表明进程处于暂停状态
若上宏为真,此时可通过WSTOPSIG(status)获取使得进程暂停的信号编号
(4)、 WIFCONTINUED(status) 非0表示暂停后已经继续运行。
(5) sleep函数虽然会使得进程暂时进入睡眠,但是一旦收到信号,进程马上会取消sleep态而进入响应,所以如果进程仍然是sleep的说明没有收到信号。
以上宏说明来自博客 :https://blog.csdn.net/y396397735/article/details/53769865
实验如图:
实验结果:
结果分析:
请看实验程序和实验结果的箭头,当循环到1的时候,才设置了signal函数。所以,从一开始,由于kill发送给子进程信号中断,同时父进程没有设置signal函数,所以,i的值为0,1时,signal函数是不生效的,实验也看到,子进程也是中断才退出的。到了2以后,signal生效了,但是子进程确是正常退出了,首先父进程是没有办法收到中断信号的,除非是子进程退出会发送,父进程就能接收到。那么在i的值为1 的时候,子进程退出后,就发了中断信号给父进程了,那么由于信号处理函数会阻塞当前信号,下一次创建的子进程就收不到信号,中断信号停在了父进程这里(此时父进程还是活的,信号是被保留的,只不过阻塞了打不了给子进程)。即使你使用kill函数发送,也是失效的。
为了严谨些,注释掉一些代码:
上述实验代码把这个signal注释掉看看:
可以看到,子进程全部都是由于中断信号退出,非常棒!(子进程sleep后面的语句不执行,因为被中断了。)
2.练习sigprocmask函数的用法,程序填空。
实验结果:
可以看到,一旦设置屏蔽,进程将收不到这个中断,直到解除信号阻塞。
3、用共享内存实现进程间通讯,实现 reader 和 writer 两个进程分别为rosse.c 和jack.c,使它们通过共享内存交换数据。writer 从标准输入读入字符串写入共享内存,reader 把共享内存里的字符串打印到标准输出。
reader 和 writer 通过信号量实现同步
要做共享内存的实验,需要创建一片共享内存,顺便提一下,在操作系统中,提及的共享内存,一般以段表的形式实现,那么共享内存就是内核的一片虚拟内存,等到各个进程需要访问时,再进行映射到各个进程真实地址,言归正传,共享内存的创建需要IPC键值,这个东西是可以用ftok来创建,注意,这个ftok是利用同一个文件系统内inode值的唯一性加上一个子序列,创建出IPC键值的,这个键值就是个共享内存块标志,没有别的意义(你可以这样想,inode是磁盘的东西,某个文件标志啥的跟你内存有啥关系)。
shmget函数是创建一片虚拟共享内存,返回值是一个flag,标识着这片空间。
shmat函数是将创建的共享内存进行映射,返回值是具体的各个进程的内存地址。
实验如图:
rose.c:
结果:
这就是共享内存实验。
上述是属于linux的system-V信号量
还有POSIX有名信号量和无名信号量:
附上有名信号量版本的rose.c:
4.参考课本消息队列的实例,分别编写代表两个人的程序,他们之间用消息队列进行通信。消息队列中的消息类型是两类,分别是J2R代表jack发给rose的消息,R2J 代表Rose发给Jack的消息。
写端代码:
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/msg.h>
- #include <stdio.h>
- #include <string.h>
- struct message
- {
- long type;
- char text[80];
-
-
- };
- int main()
- {
- key_t key=ftok(".",123);
- int msgid=msgget(key,IPC_CREAT|0666);
- struct message a;
- bzero(&a,sizeof a);
- fgets(a.text,80,stdin);
- a.type=200;
- msgsnd(msgid,&a,strlen(a.text),0);
- }
读端:
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/msg.h>
- #include <stdio.h>
- #include <string.h>
- struct message
- {
- long type;
- char text[80];
-
-
- };
- int main()
- {
- key_t key=ftok(".",123);
- int msgid=msgget(key,IPC_CREAT|0666);
- struct message a;
- bzero(&a,sizeof a);
-
- //a.type=200;
- msgrcv(msgid,&a,80,200,0);
- printf("%s",a.text);
- }
消息队列是存在于系统内核维护的一种数据结构,使用时会在内核中保存,需要撤销时,用msgctl删除。
关于消息队列的使用,参考博客:https://www.cnblogs.com/linjiqin/p/5720865.html
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。