linux 操作系统实验(六)

  1. 掌握进程间通信之信号的用法
  2. 掌握进程间通信之共享内存的使用方法
  3. 掌握进程间通信之消息队列使用。
  1. 信号实验;参考下以关于信号注册和信号捕捉的函数代码/*signal.c*/
#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

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux操作系统实验指导书是一本用于教学和学习Linux操作系统的指导书籍。该指导书的目的是帮助读者快速入门Linux操作系统,并且提供实验练习来巩固所学知识和技能。 指导书的内容一般包括以下几个方面:首先是操作系统的基本概念和原理,包括Linux操作系统的历史、特点、优势等。其次是系统的安装与配置,包括硬件要求、安装步骤、分区与文件系统配置等。然后是系统的基本操作,包括文件与目录管理、用户与权限管理、进程管理、网络配置等。此外,指导书还会介绍一些常用的系统工具和命令,如文本编辑器、压缩解压工具、网络诊断工具等。最后,指导书会提供一些实验案例,通过实践操作来巩固所学知识,并且引导读者进行更深入的学习和应用。 指导书的优势在于能够系统地讲解Linux操作系统的各个方面,让读者能够全面了解和掌握Linux操作系统使用。通过实验练习,读者能够更加深入地理解所学知识,并且培养自己的动手能力和解决问题的能力。另外,指导书还可以根据读者的学习进度和兴趣,提供一些扩展内容和拓展实验,让读者能够自主学习和拓宽知识面。 总之,Linux操作系统实验指导书是一本很实用的学习工具,能够帮助读者快速入门Linux操作系统,并且提供实验练习来巩固所学知识和技能。无论是初学者还是有一定经验的用户,都可以从中受益,并且逐步提升自己的技术水平。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值