操作系统实验三进程的管道通信
- 格式:docx
- 大小:13.23 KB
- 文档页数:6
电大操作系统实验报告3_ 进程管理实验电大操作系统实验报告 3 进程管理实验一、实验目的进程管理是操作系统的核心功能之一,本次实验的目的是通过实际操作和观察,深入理解进程的概念、状态转换、进程调度以及进程间的通信机制,掌握操作系统中进程管理的基本原理和方法,提高对操作系统的整体认识和实践能力。
二、实验环境本次实验使用的操作系统为 Windows 10,编程语言为 C 语言,开发工具为 Visual Studio 2019。
三、实验内容及步骤(一)进程的创建与终止1、编写一个 C 程序,使用系统调用创建一个子进程。
2、在父进程和子进程中分别输出各自的进程 ID 和父进程 ID。
3、子进程执行一段简单的计算任务,父进程等待子进程结束后输出结束信息。
以下是实现上述功能的 C 程序代码:```cinclude <stdioh>include <stdlibh>include <unistdh>int main(){pid_t pid;pid = fork();if (pid < 0) {printf("创建子进程失败\n");return 1;} else if (pid == 0) {printf("子进程:我的进程 ID 是%d,父进程 ID 是%d\n",getpid(), getppid());int result = 2 + 3;printf("子进程计算结果:2 + 3 =%d\n", result);exit(0);} else {printf("父进程:我的进程 ID 是%d,子进程 ID 是%d\n",getpid(), pid);wait(NULL);printf("子进程已结束\n");}return 0;}```编译并运行上述程序,可以观察到父进程和子进程的输出信息,验证了进程的创建和终止过程。
(二)进程的状态转换1、编写一个 C 程序,创建一个子进程,子进程进入睡眠状态一段时间,然后被唤醒并输出状态转换信息。
进程实验3 Linux 进程间通信一、软中断信号的处理,实现同一用户的各进程之间的通信。
●相关的系统调用⏹kill(pid ,sig):发送信号⏹signal(sig, func):指定进程对信号sig的处理行为是调用函数func。
●程序清单#include <unistd.h>#include <stdio.h>#include <signal.h>void waiting();void stop();int wait_mark;main(){int p1,p2;while((p1=fork())==-1);if(p1>0){while((p2=fork())==-1);if(p2>0){ printf("parent\n");/*父进程在此完成某个操作、或接收到用户从键盘输入的特殊按键命令后发出下面的信号。
这里省略。
*/kill(p1,16);kill(p2,17);wait(0);wait(0);printf("parent process id killed! \n");exit(0);}else/* p2==0*/{printf("p2\n");wait_mark=1;signal(17,stop);waiting();printf("child process 2 is killed by parent! \n");exit(0);}}else/*p1==0*/{printf("p1\n");wait_mark=1;signal(16,stop);waiting();printf("child process 1 is kelled by parent! \n");exit(0);}}void waiting(){while(wait_mark!=0);}void stop(){wait_mark=0;}●输入并运行此程序,分析程序的运行结果。
实验三线程控制和进程间通信一、实验目的通过Linux管道通信机制、消息队列通信机制的使用,加深对不同类型的进程通信方式的理解。
二、实验内容:1.熟悉Linux的管道通信机制2.熟悉Linux的消息队列通信机制三、思考1.有名管道和无名管道之间有什么不同?2.管道的读写与文件的读写有什么异同?3.Linux消息队列通信机制中与教材中的消息缓冲队列通信机制存在哪些异同?四、实验指导<一>Linux管道通信机制管道是所有UNIX都提供的一种进程间通信机制,它是进程之间的一个单向数据流,一个进程可向管道写入数据,另一个进程则可以从管道中读取数据,从而达到进程通信的目的。
1.无名管道无名管道通过pipe()系统调用创建,它具有如下特点:(1)它只能用于具有亲缘关系的进程(如父子进程或者兄弟进程)之间的通信。
(2)管道是半双工的,具有固定的读端和写端。
虽然pipe()系统调用返回了两个文件描述符,但每个进程在使用一个文件描述符之前仍需先将另一个文件描述符关闭。
如果需要双向的数据流,则必须通过两次pipe()建立起两个管道。
(3)管道可以看成是一种特殊的文件,对管道的读写与文件的读写一样使用普通的read、write等函数,但它不是普通的文件,也不属于任何文件系统,而只存在于内存中。
2.pipe系统调用(1)函数原型#include <unistd.h>int pipe(int filedes[2]);(2)参数filedes参数是一个输出参数,它返回两个文件描述符,其中filedes[0]指向管道的读端,filedes[1]指向管道的写端。
(3)功能pipe在内存缓冲区中创建一个管道,并将读写该管道的一对文件描述符保存在filedes所指的数组中,其中filedes[0]用于读管道,filedes[1]用于写管道。
(4)返回值成功返回0;失败返回-1,并在error中存入错误码。
(5)错误代码EMFILE:进程使用的文件描述符过多ENFILE :系统文件表已满EFAULT :非法参数filedes3.无名管道的阻塞型读写管道缓冲区有4096B的长度限制,因此,采用阻塞型读写方式时,当管道已经写满时,写进程必须等待,直到读进程取走信息为止。
实验三进程间的通信1、实验目的学习如何利用管道机制、消息缓冲队列进行进程间的通信,并加深对上述通信机制的理解。
2、实验内容(1)了解系统调用pipe()、msgget()、msgsnd()、msgrcv()的功能和实现过程。
(2)编写一段程序,使其用管道来实现父子进程之间的进程通信。
子进程向父进程发送自己的进程标识符,以及字符串“is sending a message to parent!”。
父进程则通过管道读出子进程发来的消息,将消息显示在屏幕上,然后终止。
(3)编写一段程序,使用消息缓冲队列来实现client进程和server进程之间的通信。
server进程先建立一个关键字为SVKEY (如75)的消息队列,然后等待接收类型为REQ(如1)的消息;在收到请求消息后,它便显示字符串“serving for client”和接收到的client进程的进程标识数,表示正在为client进程服务;然后再向client进程发送一应答消息,该消息类型是client 进程的进程标识数,而正文则是server进程自己的标识数。
client进程则向消息队列发送类型为REQ的消息(消息的正文为自己的进程标识数)以取得server进程的服务,并等待server 进程发来的应答;然后显示字符串“receive reply form”和接收到的server进程的标识符。
1、client.c2、server.c3、思考题上述通信机制各有什么特点?它们分别适合于何种场合?答:管道通信的特点:(1)管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;(2)只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);(3)单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。
(4)数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。
操作系统实验报告实验三、进程通信(一)——管道及共享存一、实验目的1)加深对管道通信的了解2)掌握利用管道进行通信的程序设计3)了解共享存通信的程序设计方法4)了解和熟悉Linux支持的共享存储区机制二、实验容任务一、(1)阅读以上父子进程利用管道进行通信的例子(例1),写出程序的运行结果并分析。
(2)编写程序:父进程利用管道将一字符串交给子进程处理。
子进程读字符串,将里面的字符反向后再交给父进程,父进程最后读取并打印反向的字符串。
任务二、(1)阅读例2的程序,运行一次该程序,然后用ipcs命令查看系统中共享存储区的情况,再次执行该程序,再用ipcs命令查看系统中共享存的情况,对两次的结果进行比较,并分析原因。
最后用ipcrm命令删除自己建立的共享存储区。
(有关ipcs和ipcrm介绍见后面一页)(2)每个同学登陆两个窗口,先在一个窗口中运行例3程序1(或者只登陆一个窗口,先在该窗口中以后台方式运行程序1),然后在另一个窗口中运行例3程序2,观察程序的运行结果并分析。
运行结束后可以用ctrl+c结束程序1的运行。
(3)编写程序:使用系统调用shmget(),shmat(),shmdt(),shmctl(),编制程序。
要求在父进程中生成一个30字节长的私有共享存段。
接下来,设置一个指向共享存段的字符指针,将一串大写字母写入到该指针指向的存贮区。
调用fork()生成子进程,让子进程显示共享存段中的容。
接着,将大写字母改成小写,子进程修改共享存中的容。
之后,子进程将脱接共享存段并退出。
父进程在睡眠5秒后,在此显示共享存段中的容(此时已经是小写字母)。
三、代码及运行结果分析(1)阅读以上父子进程利用管道进行通信的例子(例1),写出程序的运行结果并分析实验代码:#include<stdio.h>main(){ int x,fd[2];char buf[30],s[30];pipe(fd);while ((x=fork())==-1);if (x==0){close(fd[0]);printf("Child Process!\n");strcpy(buf,"This is an example\n");write(fd[1],buf,30);exit(0);}else{close(fd[1]);printf("Parent Process!\n");read(fd[0],s,30);printf("%s\n",s);}}运行结果:分析:调用pipe(fd);创建一个管道后,接着调用fork()函数产生两个进程,首先开始执行子进程,关闭管道出口,通过管道入口向管道中写入容。
进程的管道通信实验报告一、实验目的本实验旨在通过实际操作,深入理解进程间通信(IPC)的原理,掌握管道通信的实现方法,提高对操作系统进程管理的理解。
二、实验环境实验环境为Linux操作系统,使用Shell脚本进行进程的管道通信实验。
三、实验内容1. 创建两个Shell脚本文件,分别命名为sender.sh和receiver.sh。
2. 在sender.sh中,编写一个简单的程序,用于向管道中写入数据。
程序包括一个无限循环,每次循环中随机生成一个数字并写入管道。
3. 在receiver.sh中,编写一个简单的程序,用于从管道中读取数据。
程序同样包括一个无限循环,每次循环中从管道中读取一个数字并输出。
4. 使用Shell命令将sender.sh和receiver.sh链接起来,实现进程间的管道通信。
四、实验过程1. 打开两个终端窗口,分别用于运行sender.sh和receiver.sh。
2. 在第一个终端窗口中,输入命令“bash sender.sh”运行sender.sh脚本。
该脚本将创建一个无限循环,每次循环中随机生成一个数字并写入管道。
3. 在第二个终端窗口中,输入命令“bash receiver.sh”运行receiver.sh脚本。
该脚本将创建一个无限循环,每次循环中从管道中读取一个数字并输出。
4. 观察两个终端窗口的输出,可以看到sender.sh进程向管道中写入的数字被receiver.sh进程读取并输出。
五、实验总结通过本次实验,我们成功实现了进程间的管道通信。
在实验过程中,我们深入了解了进程间通信的原理和实现方法,掌握了管道通信的基本操作。
通过实际操作,我们更好地理解了操作系统中进程管理、进程间通信的相关知识。
同时,我们也发现了一些不足之处,例如在程序中没有添加异常处理机制等。
在今后的学习中,我们将继续深入探索进程间通信的相关知识,提高自己的编程技能和系统设计能力。
进程的管道通信实验是一个非常有用的实验,它允许两个进程之间进行数据交换。
这个实验主要涉及到了管道、管道缓冲区以及进程之间的通信机制。
以下是对这个实验的总结:
1. 管道的概念和作用:
管道是一种用于进程间通信的机制,它允许两个进程之间进行数据交换。
在管道通信实验中,我们创建了一个管道,并使用它来在两个进程之间传递数据。
管道的作用是连接两个进程,使得它们可以相互发送和接收数据。
2. 管道缓冲区:
管道缓冲区是管道中的一个重要概念。
当一个进程向管道写入数据时,数据会被写入缓冲区中,等待另一个进程读取。
当缓冲区中的数据被读取后,缓冲区中的数据会被移除,为新的数据腾出空间。
3. 进程间的通信:
在管道通信实验中,我们创建了两个进程,并使用管道来在它们之间进行通信。
一个进程向管道写入数据,另一个进程从管道读取数据。
通过这种方式,两个进程可以相互发送和接收数据。
4. 实验中的问题和解决方案:
在实验中,我们遇到了一些问题,如管道中的数据读写错误、进程间的通信问题等。
为了解决这些问题,我们采取了一些措施,如检查数据的读写是否正确、确保进程间的通信畅通等。
5. 实验的意义和收获:
通过这个实验,我们深入了解了进程间通信的概念和机制,并掌握了管道通信的基本原理和方法。
此外,我们还学会了如何解决实验中遇到的问题,提高了我们的编程能力和解决问题的能力。
总之,进程的管道通信实验是一个非常有意义的实验,它让我们深入了解了进程间通信的原理和方法。
通过这个实验,我们不仅掌握了相关的知识和技能,还提高了我们的编程能力和解决问题的能力。
实验三进程的管道通信
一、实验目的:
(1)加深对进程概念的理解,明确进程和程序的区别;
(2)学习进程创建的过程,进一步认识并发执行的实质;
(3)分析进程争用资源的现象,学习解决进程互斥的方法;
(4)学习解决进程同步的方法;
(5)掌握Linux系统进程间通过管道通信的具体实现方法。
二、实验内容及要求:
(1)使用系统调用pipe()建立一条管道线,两个子进程分别向管道写一句话(写的内容自己定,但要有该进程的一些信息);
(2)父进程从管道中读出来自两个子进程的消息,显示在屏幕上;
(3)要求:父进程首先接收子进程p1发来的消息,然后再接收子进程p2发来的消息;
(4)两个子进程要并发执行;
(5)实现管道的互斥使用。
当一个子进程正在对管道进行写操作时,另一个欲写入管道的子进程必须等待。
使用系统调用lockf(fd[1],1,0)实现对管道的加锁操作,用lockf(fd[1],0,0)解除对管道的锁定;
(6)实现父子进程的同步,当父进程试图从一空管道中读取数据时,便进入等待状态,直到子进程将数据写入管道返回后,才将其唤醒。
三、实现:
相关的系统调用
fork() 用于创一个子进程。
格式:int fork();
返回值:在子进程中返回0;在父进程中返回所创建的子进程的ID值;当返回-1时,创建失败。
wait() 常用来控制父进程与子进程的同步。
在父进程中调用wait(),则父进程被阻塞,进入等待队列,等待子进程结束。
当子进程结束时,父进程从wait()返回继续执行原来的程序。
返回值:大于0时,为子进程的ID值;等于-1时,调用失败。
exit() 是进程结束时最常调用的。
格式:void exit( int status); 其中,status为进程结束状态。
pipe() 用于创建一个管道
格式:pipe(int fd);
其中fd是一个由两个数组元素fd[0]和fd[1]组成的整型
数组,fd[0]是管道的读端口,用于从管道读出数据,fd[1]是管道的写端口,用于向管道写入数据。
返回值:0 调用成功;-1 调用失败。
sleep() 使调用进程睡眠若干时间,之后唤醒。
格式:sleep(int t); 其中t为睡眠时间。
lockf() 用于对互斥资源加锁和解锁。
在本实验中该调用的格式为:
lockf(fd[1],1,0);/* 表示对管道的写入端口加锁。
lockf(fd[1],0,0);/* 表示对管道的写入端口解锁。
write(fd[1],String,Length) 将字符串String的内容写入管道的写入口。
read(fd[0],String,Length) 从管道的读入口读出信息放入字符串String中。
程序流程图
图1 父进程流程图
图2 子进程P1流程图
四、运行结果及说明
五、源代码
#include <>
#include <sys/>
#include <>
#include <sys/>
#include <>
#include <>
#include <>
#include <>
int main( ){
int pid1,pid2,pid3;
int fd[2];
char outpipe[60],inpipe[60];
pipe(fd);//创建一个管道
while ((pid1=fork( ))==-1);
printf("pid1=%d\n",pid1);
if(pid1==0){
printf("The Child process 1 is sending message!\n");
lockf(fd[1],1,0);//互斥
sprintf(outpipe,"This is the child 1 process's message!\n");
write(fd[1],outpipe,60);
sleep(1);//自我阻塞1秒,让出机会执行下一个进程,增加并发度 lockf(fd[1],0,0);
exit(0);
}
else{
while((pid2=fork( ))==-1);
printf("pid2=%d\n",pid2);
if(pid2==0){
printf("The Child process 2 is sending message!\n");
lockf(fd[1],1,0);
sprintf(outpipe,"This is the child 2 process's message!\n"); write(fd[1],outpipe,60);
sleep(1);
lockf(fd[1],0,0);
exit(0);
}
else{
while((pid3=fork( ))==-1);
printf("pid3=%d\n",pid3);
if(pid3==0){
printf("The Child process 3 is sending message!\n");
lockf(fd[1],1,0);
sprintf(outpipe,"This is the child 3 process's message!\n"); write(fd[1],outpipe,60);
sleep(1);
lockf(fd[1],0,0);
exit(0);
}
else{
wait(0);//同步
read(fd[0],inpipe,60);
printf("\n%s",inpipe);
wait(0);
read(fd[0],inpipe,60);
printf("%s\n",inpipe);
wait(0);
read(fd[0],inpipe,60);
printf("%s\n",inpipe);
exit(0);
}
}
}
return 0;
}
六、回答问题
(1)指出父进程与两个子进程并发执行的顺序,并说明原因。
子进程先执行,然后父进程才执行。
这是由进程的同步机制决定的,因为只有子进程向管道中写入信息后,父进程才能读取;否则父进程自己调用wait()系统调用将自己阻塞,将处理机交由子进程。
(2)若不对管道加以互斥控制,会有什么后果?
管道进行互斥控制,是为防止两个子进程对管道资源进行争夺而产生信息丢失或覆盖。
如果不加控制,那么可能一个子进程写入的信息还没来得及
被父进程读出,另一个子进程又先写入信息,那么之前的进程写入的信息将被覆盖,父进程也就读不到之前进程传递来的信息了。
(3)说明你是如何实现父子进程之间的同步的。
1、父进程读出之前确定管道中有数据,否则阻塞自己。
这一点很容一般到,通过系统调用wait()函数,即可以实现,当子进程结束时父进程才执行,那么此时管道中肯定已经有子进程写入的数据了。
2、子进程在写入之前要确定管道中的数据已经被父进程读出,否则不能写入或者阻塞自己。
3、这可以通过进程间的互斥来间接的办到。
因为子进程间的互斥,所以每个子进程在执行开始都对管道pipe加锁,那么这样同时就只能有一个子进程向管道写入数据,并且子进程在向管道中写入数据后还要调用sleep()系统调用睡眠若干时间,那么这样就可以保证父进程能够从管道中读出数据。
然后下一子进程才能写入。
那么这样就保证了开头所说的子进程在写入之前要确定管道中的数据已经被父进程读出,否则不能写入或者阻塞自己。