实验六:进程间通信
实验目的:
学会进程间通信方式:无名管道,有名管道,信号,消息队列,
实验要求:
(一)在父进程中创建一无名管道,并创建子进程来读该管道,父进程来写该管道(二)在进程中为SIGBUS注册处理函数,并向该进程发送SIGBUS信号(三)创建一消息队列,实现向队列中存放数据和读取数据
实验器材:
软件:安装了Linux的vmware虚拟机
硬件:PC机一台
实验步骤:
(一)无名管道的使用
1、编写实验代码pipe_rw.c
#include
#include
#include
#include
#include
#include
int main()
{
int pipe_fd[2];//管道返回读写文件描述符
pid_t pid;
char buf_r[100];
char* p_wbuf;
int r_num;
memset(buf_r,0,sizeof(buf_r));//将buf_r初始化
char str1[]=”parent write1 “holle””;
char str2[]=”parent write2 “pipe”\n”;
r_num=30;
/*创建管道*/
if(pipe(pipe_fd)<0)
{
printf("pipe create error\n");
return -1;
}
/*创建子进程*/
if((pid=fork())==0) //子进程执行代码
{
//1、子进程先关闭了管道的写端
close(pipe_fd[1]);
//2、让父进程先运行,这样父进程先写子进程才有内容读sleep(2);
//3、读取管道的读端,并输出数据
if(read(pipe_fd[0],buf_r, r_num)<0)
{
printf(“read error!”);
exit(-1);
}
printf(“%s\n”,buf_r);
//4、关闭管道的读端,并退出
close(pipe_fd[1]);
}
else if(pid>0) //父进程执行代码
{
//1、父进程先关闭了管道的读端
close(pipe_fd[0]);
//2、向管道写入字符串数据
p_wbuf=&str1;
write(pipe_fd[1],p_wbuf,sizof(p_wbuf));
p_wbuf=&str2;
write(pipe_fd[1],p_wbuf,sizof(p_wbuf));
//3、关闭写端,并等待子进程结束后退出
close(pipe_fd[1]);
}
return 0;
}
/***********************
#include
#include
#include
#include
#include
#include
int main()
{
int pipe_fd[2];//管道返回读写文件描述符
pid_t pid;
char buf_r[100];
char* p_wbuf;
int r_num;
memset(buf_r,0,sizeof(buf_r));//将buf_r初始化
char str1[]="holle";
char str2[]="pipe";
r_num=10;
/*创建管道*/
if(pipe(pipe_fd)<0)
{
printf("pipe create error\n");
return -1;
}
/*创建子进程*/
if((pid=fork())==0) //子进程执行代码
{
close(pipe_fd[1]);//1、子进程先关闭了管道的写端//2、让父进程先运行,这样父进程先写子进程才有内容读
//3、读取管道的读端,并输出数据
if(read(pipe_fd[0],buf_r, r_num)<0)
{
printf("read1 error!");
exit(-1);
}
printf("\nparent write1 %s!",buf_r);
sleep(1);
if(read(pipe_fd[0],buf_r, r_num)<0)
{
printf("read2 error!");
exit(-1);
}
printf("\nparent write2 %s!",buf_r);
close(pipe_fd[1]);//4、关闭管道的读端,并退出exit(1);
//printf("child error!");
}
else if(pid>0) //父进程执行代码
{
close(pipe_fd[0]);//1、父进程先关闭了管道的读端
p_wbuf=str1;//2、向管道写入字符串数据
write(pipe_fd[1],p_wbuf,sizeof(str1));
sleep(1);
p_wbuf=str2;
write(pipe_fd[1],p_wbuf,sizeof(str2));
close(pipe_fd[1]);//3、关闭写端,并等待子进程结束后退出
exit(1);
//printf("father error!");
}
return 0;
}
**************************/
2、编译应用程序pipe_rw.c
3、运行应用程序
子进程先睡两秒让父进程先运行,父进程分两次写入“hello”和“pipe”,然后阻塞等待子进程退出,子进程醒来后读出管道里的内容并打印到屏幕上再退出,父进程捕获到子进程退出后也退出
4、由于fork函数让子进程完整地拷贝了父进程的整个地址空间,所以父子进程都有管道的
读端和写端。我们往往希望父子进程中的一个进程写一个进程读,那么写的进程最后关掉读端,读的进程最好关闭掉写端
(二)信号处理
1、编写实验代码sig_bus.c
#include
#include
#include
//1、自定义信号处理函数,处理SIGBUS信号,打印捕捉到信号即可static void signal_handler(int signo)
{
if(signo ==SIGBUS)
printf(“\n I have get SIGBUS”);
exit(EXIT_FAILURE);
}
int main()
{
printf("Waiting for signal SIGBUS \n ");
//2、注册信号处理函数
if(signal(SIGBUS,signal_handler)==SIG_ERR)
{
fprintf(stderr,”cannot handle SIGBUS\n”);
exit(EXIT_FAILURE);
}
pause();//将进程挂起直到捕捉到信号为止
exit(0);
return 0;
}
/********************************
#include
#include
#include
#include
//1、自定义信号处理函数,处理SIGBUS信号,打印捕捉到信号即可static void signal_handler(int signo)
{
if(signo ==SIGBUS)
printf("I have get SIGBUS");
exit(EXIT_FAILURE);
}
int main()
{
printf("Waiting for signal SIGBUS \n ");
//2、注册信号处理函数
if(signal(SIGBUS,signal_handler)==SIG_ERR)
{
fprintf(stderr,"cannot handle SIGBUS\n");
exit(EXIT_FAILURE);
}
pause();//将进程挂起直到捕捉到信号为止
exit(0);
return 0;
}
***************************/
用signal系统调用为SIGBUS信号注册信号处理函数my_func,然后将进程挂起等待SIGBUS信号。所以需要向该进程发送SIGBUS信号才会执行自定义的信号处理函数
2、编译应用程序sig_bus.c
3、运行应用程序
先先一个终端中运行sig_bus,会看到进程挂起,等待信号
然后在另一个终端中,查找到运行sig_bus这个产生的进程号,用kill命令发送SIGBUS信号给这个进程
我们可以看到前面挂起的进程在接收到这个信号后的处理
用自定义信号处理函数my_func来处理,所以打印了I have get SIGBUS这样一句话
上机报告要求:
1、总结pipe(),signal()的函数定义原型,返回值和参数的意义
表头文件:#include
定义函数:int pipe(int filedes[2]);
函数说明(参数):pipe()会建立管道,并将文件描述词由参数filedes数组返回。
filedes[0]为管道里的读取端,filedes[1]则为管道的写入端。
返回值:若成功则返回零,否则返回-1,错误原因存于errno中。
阻塞问题:当管道中的数据被读取后,管道为空。一个随后的read()调用将默认的被阻塞,等待某些数据写入。
功能:管道是一种把两个进程之间的标准输入和标准输出连接起来的机制,从而提供一种让多个进程间通信的方法,当进程创建管道时,每次都需要提供两个文件描述符来操作管
道。其中一个对管道进行写操作,另一个对管道进行读操作。对管道的读写与一般的
IO系统函数一致,使用write()函数写入数据,使用read()读出数据。
表头文件: #include
功能:设置某一信号的对应动作
函数原型:void (*signal(int signum,void(* handler)(int)))(int);
或者:typedef void(*sig_t) ( int ); sig_t signal(int signum,sig_t handler);
可看成是signal()函数(它自己是带有两个参数,一个为整型,一个为函数指针的函数),而这个signal()函数的返回值也为一个函数指针,这个函数指针指向一个带整型参数,
并且返回值为void的一个函数。
参数说明:第一个参数signum指明了所要处理的信号类型,它可以取除了SIGKILL和SIGSTOP外的任何一种信号。
第二个参数handler描述了与信号关联的动作,它可以取以下三种值:
(1)一个返回值为正数的函数地址
此函数必须在signal()被调用前申明,handler中为这个函数的名字。当接收到一个类型为sig的信号时,就执行handler 所指定的函数。这个函数应有如下形式的定义:
intfunc(int sig); sig是传递给它的唯一参数。执行了signal()调用后,进程只要接收到类型为sig的信号,不管其正在执行程序的哪一部分,就立即执行func()函数。当func()函数执行结束后,控制权返回进程被中断的那一点继续执行。
(2)SIGIGN
这个符号表示忽略该信号,执行了相应的signal()调用后,进程会忽略类型为sig的信号。
(3)SIGDFL
这个符号表示恢复系统对信号的默认处理。
函数说明:signal()会依参数signum 指定的信号编号来设置该信号的处理函数。当指定的信
号到达时就会跳转到参数handler指定的函数执行。当一个信号的信号处理函数执行时,如果进程又接收到了该信号,该信号会自动被储存而不会中断信号处理函数的执行,直到信号处理函数执行完毕再重新调用相应的处理函数。但是如果在信号处理函数执行时进程收到了其它类型的信号,该函数的执行就会被中断。
返回值:返回先前的信号处理函数指针,如果有错误则返回SIG_ERR(-1)。
附加说明:在信号发生跳转到自定的handler处理函数执行后,系统会自动将此处理函数换回原来系统预设的处理方式,如果要改变此操作请改用sigaction()。
下面的情况可以产生Signal:
1. 按下CTRL+C产生SIGINT
2. 硬件中断,如除0,非法内存访问(SIGSEV)等等
3. Kill函数可以对进程发送Signal
4. Kill命令。实际上是对Kill函数的一个包装
5. 软件中断。如当Alarm Clock超时(SIGURG),当Reader中止之后又向管道写数据
(SIGPIPE),等等
命名管道FIFO
功能:管道最大的劣势就是没有名字,只能用于有一个共同祖先进程的各个进程之间。FIFO 代表先进先出,单它是一个单向数据流,也就是半双工,和管道不同的是:每个FIFO 都有一个路径与之关联,从而允许无亲缘关系的进程访问。
头文件:#include
#include
函数定义原型:int mkfifo(const char *pathname, mode_t mode);
参数:这里pathname是路径名,mode是sys/stat.h里面定义的创建文件的权限.
2、利用有名管道FIFO实现类似第一个实验的功能,一个程序fifo_read.c写数据”Hi Linux”,另一个程序fifo_write.c读数据并打印出来。
//fifo.read.c
#include
#include
#include
#include
#include
#include
#include
#include
//#define FIFO "fifo"
int main()
{
int fdr,fd;
char buf_r[]="Hi Linux\n";
fd=mkfifo("fifo.txt",O_CREAT|O_RDWR|0666);
if(fd<0)
{printf("fifo creat error(R)!\n");
exit(-1);
}
fdr=open("fifo.txt",O_WRONL Y|O_CREA T,0666); if(fdr<0)
{
printf("open error!");
exit(-1);
}
if(write(fdr,buf_r,sizeof(buf_r))<0)
{
printf("write error");
exit(-1);
}
close(fdr);
close(fd);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
//#define FIFO "fifo"
int main()
{
int fdw,fd;
char buf[100];
memset(buf,0,sizeof(buf));
fdw=open("fifo.txt",O_RDONL Y);
if(fdw<0)
{
printf("open error(W)!");
exit(-1);
}
//sleep(2);
if(read(fdw,buf,100)<0)
{
printf("read error(W)");
exit(-1);
}
printf("\n%s",buf);
close(fdw);
return 0;
}
实验六:Linux进程间通信(2)(4课时) 实验目的: 理解进程通信原理;掌握进程中信号量、共享内存、消息队列相关的函数的使用。实验原理: Linux下进程通信相关函数除上次实验所用的几个还有: 信号量 信号量又称为信号灯,它是用来协调不同进程间的数据对象的,而最主要的应用是前一节的共享内存方式的进程间通信。要调用的第一个函数是semget,用以获得一个信号量ID。 int semget(key_t key, int nsems, int flag); key是IPC结构的关键字,flag将来决定是创建新的信号量集合,还是引用一个现有的信号量集合。nsems是该集合中的信号量数。如果是创建新集合(一般在服务器中),则必须指定nsems;如果是引用一个现有的信号量集合(一般在客户机中)则将nsems指定为0。 semctl函数用来对信号量进行操作。 int semctl(int semid, int semnum, int cmd, union semun arg); 不同的操作是通过cmd参数来实现的,在头文件sem.h中定义了7种不同的操作,实际编程时可以参照使用。 semop函数自动执行信号量集合上的操作数组。 int semop(int semid, struct sembuf semoparray[], size_t nops); semoparray是一个指针,它指向一个信号量操作数组。nops规定该数组中操作的数量。 ftok原型如下: key_t ftok( char * fname, int id ) fname就是指定的文件名(该文件必须是存在而且可以访问的),id是子序号,虽然为int,但是只有8个比特被使用(0-255)。 当成功执行的时候,一个key_t值将会被返回,否则-1 被返回。 共享内存 共享内存是运行在同一台机器上的进程间通信最快的方式,因为数据不需要在不同的进程间复制。通常由一个进程创建一块共享内存区,其余进程对这块内存区进行读写。首先要用的函数是shmget,它获得一个共享存储标识符。 #include
进程通信实验报告 一、实验名称:进程通信 二、实验目的:掌握用邮箱方式进行进程通信的方法,并通过设计实现简单邮箱理解进程通信中的同步问题以及解决该问题的方法。 三、实验原理:邮箱机制类似于日常使用的信箱。对于用户而言使用起来比较方便,用户只需使用send ()向对方邮箱发邮件 receive ()从自己邮箱取邮件, send ()和 receive ()的内部操作用户无需关心。因为邮箱在内存中实现,其空间有大小限制。其实send ()和 receive ()的内部实现主要还是要解决生产者与消费者问题。 四、实验内容:进程通信的邮箱方式由操作系统提供形如send ()和receive ()的系统调用来支持,本实验要求学生首先查找资料了解所选用操作系统平台上用于进程通信的系统调用具体形式,然后使用该系统调用编写程序进行进程间的通信,要求程序运行结果可以直观地体现在界面上。在此基础上查找所选用操作系统平台上支持信号量机制的系统调用具体形式,运用生产者与消费者模型设计实现一个简单的信箱,该信箱需要有创建、发信、收信、撤销等函数,至少能够支持两个进程互相交换信息,比较自己实现的信箱与操作系统本身提供的信箱,分析两者之间存在的异同。 五、背景知识介绍: 1、sembuf 数据结构 struct sembuf { unsigned short int sem_num; //semaphore number short int sem_op; //semaphore operation short int sem_flg; //operation flag }; sem_num :操作信号在信号集中的编号,第一个信号的编号是0。 进程A 进程B 信箱A 信箱B Send() Send() receive() receive()
实验三进程通信(二) 实验目的: 1、理解进程通信原理和基本技术 2、了解linux系统进程间通信机构(IPC); 3、理解linux关于共享内存的概念; 4、掌握linux支持进程间共享内存的系统调用; 5、巩固进程同步概念。 实验内容: (一)闹钟。用c语言编程,实现进程间通过信号进行通信。 用fork()创建两个进程,子进程在等待5秒后用系统调用kill()向父进程发送SIGALRM 信号,父进程用系统调用signal()捕捉SIGALRM信号。参考程序如下: #include
实验六:进程间通信 实验目的: 学会进程间通信方式:无名管道,有名管道,信号,消息队列, 实验要求: (一)在父进程中创建一无名管道,并创建子进程来读该管道,父进程来写该管道(二)在进程中为SIGBUS注册处理函数,并向该进程发送SIGBUS信号(三)创建一消息队列,实现向队列中存放数据和读取数据 实验器材: 软件:安装了Linux的vmware虚拟机 硬件:PC机一台 实验步骤: (一)无名管道的使用 1、编写实验代码pipe_rw.c #include
//1、子进程先关闭了管道的写端 close(pipe_fd[1]); //2、让父进程先运行,这样父进程先写子进程才有内容读sleep(2); //3、读取管道的读端,并输出数据 if(read(pipe_fd[0],buf_r, r_num)<0) { printf(“read error!”); exit(-1); } printf(“%s\n”,buf_r); //4、关闭管道的读端,并退出 close(pipe_fd[1]); } else if(pid>0) //父进程执行代码 { //1、父进程先关闭了管道的读端 close(pipe_fd[0]); //2、向管道写入字符串数据 p_wbuf=&str1; write(pipe_fd[1],p_wbuf,sizof(p_wbuf)); p_wbuf=&str2; write(pipe_fd[1],p_wbuf,sizof(p_wbuf)); //3、关闭写端,并等待子进程结束后退出 close(pipe_fd[1]); } return 0; } /*********************** #include
实验一进程管理 1.实验目的: (1)加深对进程概念的理解,明确进程和程序的区别; (2)进一步认识并发执行的实质; (3)分析进程争用资源的现象,学习解决进程互斥的方法; (4)了解Linux系统中进程通信的基本原理。 2.实验预备内容 (1)阅读Linux的源码文件,加深对进程管理概念的理解; (2)阅读Linux的fork()源码文件,分析进程的创建过程。 3.实验内容 (1)进程的创建: 编写一段程序,使用系统调用fork() 创建两个子进程。当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符:父进程显示字符“a”,子进程分别显示字符“b”和“c”。试观察记录屏幕上的显示结果,并分析原因。 源代码: #include <> #include <> #include
int p1,p2; p1=fork(); ockf()函数是将文件区域用作信号量(监视锁),或控制对锁定进程的访问(强制模式记录锁定)。试图访问已锁定资源的其他进程将返回错误或进入休态,直到资源解除锁定为止。而上面三个进程,不存在要同时进入同一组共享变量的临界区域的现象,因此输出和原来相同。 (3) a) 编写一段程序,使其实现进程的软中断通信。 要求:使用系统调用fork() 创建两个子进程,再用系统调用signal() 让父进程捕捉键盘上来的中断信号(即按DEL键);当捕捉到中断信号后,父进程用系统调用Kill() 向两个子进程发出信号,子进程捕捉到信号后分别输出下列信息后终止: Child Process 1 is killed by Parent! Child Process 2 is killed by Parent! 父进程等待两个子进程终止后,输出如下的信息后终止: Parent Process is killed!
进程间通信实验报告 班级:10网工三班学生姓名:谢昊天学号:1215134046 实验目的和要求: Linux系统的进程通信机构 (IPC) 允许在任意进程间大批量地交换数据。本实验的目的是了解和熟悉Linux支持的消息通讯机制及信息量机制。 实验内容与分析设计: (1)消息的创建,发送和接收。 ①使用系统调用msgget (), msgsnd (), msgrev (), 及msgctl () 编制一长度为1k 的消息的发送和接收程序。 ②观察上面的程序,说明控制消息队列系统调用msgctl () 在此起什么作用? (2)共享存储区的创建、附接和段接。 使用系统调用shmget(),shmat(),sgmdt(),shmctl(),编制一个与上述功能相同的程序。(3)比较上述(1),(2)两种消息通信机制中数据传输的时间。 实验步骤与调试过程: 1.消息的创建,发送和接收: (1)先后通过fork( )两个子进程,SERVER和CLIENT进行通信。 (2)在SERVER端建立一个Key为75的消息队列,等待其他进程发来的消息。当遇到类型为1的消息,则作为结束信号,取消该队列,并退出SERVER 。SERVER每接收到一个消息后显示一句“(server)received”。 (3)CLIENT端使用Key为75的消息队列,先后发送类型从10到1的消息,然后退出。最后的一个消息,既是 SERVER端需要的结束信号。CLIENT每发送一条消息后显示一句“(client)sent”。 (4)父进程在 SERVER和 CLIENT均退出后结束。 2.共享存储区的创建,附接和断接: (1)先后通过fork( )两个子进程,SERVER和CLIENT进行通信。 (2)SERVER端建立一个KEY为75的共享区,并将第一个字节置为-1。作为数据空的标志.等待其他进程发来的消息.当该字节的值发生变化时,表示收到了该消息,进行处理.然后再次把它的值设为-1.如果遇到的值为0,则视为结束信号,取消该队列,并退出SERVER.SERVER 每接收到一次数据后显示”(server)received”. (3)CLIENT端建立一个为75的共享区,当共享取得第一个字节为-1时, Server端空闲,可发送请求. CLIENT 随即填入9到0.期间等待Server端再次空闲.进行完这些操作后, CLIENT退出. CLIENT每发送一次数据后显示”(client)sent”. (4)父进程在SERVER和CLIENT均退出后结束。 实验结果: 1.消息的创建,发送和接收: 由 Client 发送两条消息,然后Server接收一条消息。此后Client Server交替发送和接收消息。最后一次接收两条消息。Client 和Server 分别发送和接收了10条消息。message 的传送和控制并不保证完全同步,当一个程序不再激活状态的时候,它完全可能继续睡眠,造成上面现象。在多次send message 后才 receive message.这一点有助于理解消息转送的实现机理。
李建伟版实用操作系统第二版最新习题 3 进程同步与通信 一、选择题 题号1 2 3 4 5 6 7 8 9 10 答案A D D C B C A B A A 题号11 12 答案D C 二、综合题 1、答:临界资源也称独占资源、互斥资源,它是指某段时间内只充许一个进程使用的资源。比如打印机等硬件资源,以及只能互斥使用的变量、表格、队列等软件资源。各个进程中访问临界资源的、必须互斥执行的程序代码段称为临界区,各进程中访问同一临界资源的程序代码段必须互斥执行。 为防止两个进程同时进入临界区,可采用软件解决方法或同步机构来协调它们。但是,不论是软件算法还是同步机构都应遵循下述准则: ①空闲让进。②忙则等待。③有限等待。④让权等待。 2、答:忙等待意味着一个进程正在等待满足一个没有闲置处理器的严格循环的条件。因为只有一个CPU 为多个进程服务,因此这种等待浪费了CPU 的时钟。 其他类型的等待:与忙等待需要占用处理器不同,另外一种等待则允许放弃处理器。如进程阻塞自己并且等待在合适的时间被唤醒。忙等可以采用更为有效的办法来避免。例如:执行请求(类似于中断)机制以及PV 信号量机制,均可避免“忙等待”现象的发生。 3、答: 在生产者—消费者问题中,Producer 进程中P(empty)和P(mutex)互换先后次序。先 执行P(mutex),假设成功,生产者进程获得对缓冲区的访问权,但如果此时缓冲池已满,没有空缓冲区可供其使用,后续的P(empty)原语没有通过,Producer 阻塞在信号量empty 上,而此时mutex 已被改为0,没有恢复成初值1。切换到消费者进程后,Consumer 进程执行P(full)成功,但其执行P(mutex)时由于Producer 正在访问缓冲区,所以不成功,阻塞在信号量mutex 上。生产者进程和消费者进程两者均无法继续执行,相互等待对方释放资源,会产生死锁。 在生产者和消费者进程中,V 操作的次序无关紧要,不会出现死锁现象。 4、答:
实验6 进程及进程间的通信 ●实验目的: 1、理解进程的概念 2、掌握进程复制函数fork的用法 3、掌握替换进程映像exec函数族 4、掌握进程间的通信机制,包括:有名管道、无名管道、信 号、共享内存、信号量和消息队列 ●实验要求: 熟练使用该节所介绍fork函数、exec函数族、以及进程间通信的相关函数。 ●实验器材: 软件: 1.安装了Ubunt的vmware虚拟机 硬件:PC机一台 ●实验步骤: 1、用进程相关API 函数编程一个程序,使之产生一个进程 扇:父进程产生一系列子进程,每个子进程打印自己的PID 然后退出。要求父进程最后打印PID。 进程扇process_fan.c参考代码如下:
2、用进程相关API 函数编写一个程序,使之产生一个进程 链:父进程派生一个子进程后,然后打印出自己的PID,然后退出,该子进程继续派生子进程,然后打印PID,然后退出,以此类推。
要求:1) 实现一个父进程要比子进程先打印PID 的版本。(即 打印的PID 一般是递增的) 2 )实现一个子进程要比父进程先打印PID 的版本。(即打印的PID 一般是递减的) 进程链1,process_chain1.c的参考代码如下:
进程链2,process_chain2.c的参考代码如下:
3、编写程序execl.c,实现父进程打印自己的pid号,子进程调用 execl函数,用可执行程序file_creat替换本进程。注意命令行参数。 参考代码如下: /*execl.c*/ #include
Linux进程通信实验报告 一、实验目的和要求 1.进一步了解对进程控制的系统调用方法。 2.通过进程通信设计达到了解UNIX或Linux系统中进程通信的基本原理。 二、实验内容和原理 1.实验编程,编写程序实现进程的管道通信(设定程序名为pipe.c)。使 用系统调用pipe()建立一条管道线。而父进程从则从管道中读出来自 于两个子进程的信息,显示在屏幕上。要求父进程先接受子进程P1 发来的消息,然后再接受子进程P2发来的消息。 2.可选实验,编制一段程序,使其实现进程的软中断通信(设定程序名为 softint.c)。使用系统调用fork()创建两个子进程,再用系统调用 signal()让父进程捕捉键盘上来的中断信号(即按Del键),当父进程 接受这两个软中断的其中一个后,父进程用系统调用kill()向两个子 进程分别发送整数值为16和17的软中断信号,子进程获得对应软中 断信号后分别输出相应信息后终止。 三、实验环境 一台安装了Red Hat Linux 9操作系统的计算机。 四、实验操作方法和步骤 进入Linux操作系统,利用vi编辑器将程序源代码输入并保存好,然后 打开终端对程序进行编译运行。 五、实验中遇到的问题及解决 六、实验结果及分析 基本实验 可选实验
七、源代码 Pipe.c #include"stdio.h" #include"unistd.h" main(){ int i,j,fd[2]; char S[100]; pipe(fd); if(i=fork==0){ sprintf(S,"child process 1 is sending a message \n"); write(fd[1],S,50); sleep(3); return; } if(j=fork()==0){ sprintf(S,"child process 2 is sending a message \n"); write(fd[1],S,50); sleep(3); return;
实验三进程间通信(2学时) 一、实验目的 (1)了解什么是信号。 (2)熟悉LINUX系统中进程之间软中断通信的基本原理。 (3)熟悉LINUX支持的管道通信方式。 二、实验内容 (1)编写一段程序,使其现实进程的软中断通信。 即:使用系统调用fork()创建两个子进程,再用系统调用signal()让父进程捕捉键盘上来的中断信号(即按 ctrl+c 键);当捕捉到中断信号后,父进程用系统调用kill( )向两个子进程发出信号,子进程捕捉到信号后,分别输出下列信息后终止: Child Process11 is killed by Parent! Child Process12 is killed by Parent! 父进程等待两个子进程终止后,输出如下的信息后终止 Parent Process is killed! 要求:运行以下参考程序并分析结果。 <参考程序> #include
signal(SIGALRM,alarming);/*接受SIGALRM*/ waiting(); kill(p1,16); /*向p1发软中断信号16*/ kill(p2,17); /*向p2发软中断信号17*/ wait(0); /*同步*/ wait(0); printf("parent process is killed!\n"); exit(0); //会暂时停止目前进程的执行,直到有信号来到或子进程结束。 } else { wait_mark=1; signal(17,stop); signal(SIGINT,SIG_IGN); /*忽略 ^c信号*/ while (wait_mark!=0); lockf(1,1,0); printf("child process2 is killed by parent!\n"); lockf(1,0,0); exit(0); } } else { wait_mark=1; signal(16,stop); signal(SIGINT,SIG_IGN); /*忽略^c信号*/ while (wait_mark!=0); lockf(1,1,0); printf("child process1 is killed by parent!\n"); lockf(1,0,0); exit(0); } } void waiting() { sleep(5); if (wait_mark!=0) kill(getpid(),SIGALRM); } void alarming()
实验三进程的同步 一、实验目的 1、了解进程同步和互斥的概念及实现方法; 2、更深一步的了解fork()的系统调用方式。 二、实验内容 1、预习操作系统进程同步的概念及实现方法。 2、编写一段源程序,用系统调用fork()创建两个子进程,当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符:父进程显示字符“a”;子进程分别显示字符“b”和字符“c”。程序的输出是什么?分析原因。 3、阅读模拟火车站售票系统和实现进程的管道通信源代码,查阅有关进程创建、进程互斥、进程同步的系统功能调用或API,简要解释例程中用到的系统功能或API的用法,并编辑、编译、运行程序,记录程序的运行结果,尝试给出合理的解释。 4、(选做)修改问题2的代码,使得父子按顺序显示字符“a”;“b”、“c”编辑、编译、运行。记录程序运行结果。 三、设计思想 1、程序框架 (1)创建两个子进程:(2)售票系统:
(3)管道通信: 先创建子进程,然后对内容加锁,将输出语句存入缓存,并让子进程自己进入睡眠,等待别的进程将其唤醒,最后解锁;第二个子进程也执行这样的过程。父进程等待子进程后读内容并输出。 (4)修改程序(1):在子进程的输出语句前加上sleep()语句,即等待父进程执行完以后再输出。 2、用到的文件系统调用函数 (1)创建两个子进程:fork() (2)售票系统:DWORD WINAPI Fun1Proc(LPVOID lpPartameter); CreateThread(NULL,0,Fun1Proc,NULL,0,NULL); CloseHandle(hThread1); (HANDLE)CreateMutex(NULL,FALSE,NULL); Sleep(4000)(sleep调用进程进入睡眠状态(封锁), 直到被唤醒); WaitForSingleObject(hMutex,INFINITE); ReleaseMutex(hMutex); (3)管道通信:pipe(fd),fd: int fd[2],其中: fd[0] 、fd[1]文件描述符(读、写); lockf( fd,function,byte)(fd: 文件描述符;function: 1: 锁定 0:解锁;byte: 锁定的字节数,0: 从当前位置到文件尾); write(fd,buf,byte)、read(fd,buf,byte) (fd: 文件描述符;buf : 信息传送的源(目标)地址;byte: 传送的字节数); sleep(5); exit(0); read(fd[0],s,50) (4)修改程序(1):fork(); sleep(); 四、调试过程 1、测试数据设计 (1)创建两个子进程:
武汉工程大学计算机科学与工程学院 《操作系统》实验报告[Ⅰ]
一、实验目的 创建进程,实现进程消息通信和共享内存通信,了解进程的创建、退出和获取进程信。了解什么是映像文件、管道通信及其作用,掌握通过内存映像文件和管道技术实现进程通信。 二、实验内容 本例用三种方法实现进程通信,仅用于示例目的,没有进行功能优化。 1、创建进程A和B后,在进程A中输入一些字符,点“利用 SendMessage发送消息”按钮可将消息发到进程B。 2、在进程A中输入一些字符,点“写数据到内存映像文件”按钮, 然后在进程B中点“从内存映像文件读数据”按钮可收到消息。其中在点“写数据到内存映像文件”时,要求创建映像文件,B进程在印象文件中读取数据。 3、先在进程B中点“创建管道并接收数据”按钮,然后在进程A 中输入一些字符,点“写数据到管道文件”按钮可将消息发到进程B。管道是连接读/写进程使他们进行通信的一个共享文件,目的是更好地实现进程间的通信。 三、实验思想 这次试验最主要的内容和核心思想就是学会创建进程并实现进程间的简单通信、创建映像文件和创建管道文件来通信,后两者是实现进程通信的高级通信机制中的两种。. 创建一个程序A和程序B,其中程序A和B各有一个主窗体,A主窗体上要求可以实现创建进程B(即调用函数B)、结束进程B、关闭进程A、向进程B发送数据、创建映像文件、创建管道文件等功能,进程B要求有从映像文件读取数据、创建管道并接收数据、结束进程B功能。最终让A、B进程相互通信。
四、设计分析: 首先设得设计A、B两个程序的操作界面,然后编写各个功能模块。对于A 程序窗体,在“利用SendMessage发送消息”按钮的消息响应函数中,主要是利用Windows API函数CWnd::FindWindow来找到接收消息的窗体,即进程B,找到进程B后,利用这个函数返回的窗体指针的SendMessage函数来发送消息。在“写数据到内存印象文件”按钮的消息响应函数中,主要是利用函数CreateFileMapping来创建一个印象文件,这个函数返回的是这个印象文件的句柄,然后将这个句柄和要发送的消息字符串传递到函数sprintf中,就可以所要发送的消息写入印象文件,在B程序窗体中有个“从内存印象文件读数据”按钮,在这个按钮的消息响应函数中读取父进程所创建的印象文件中的数据就可以实现通信了。在B程序窗体按钮“写数据到管道文件”的消息响应函数中,不能直接将要发送的消息发送到管道文件,因为管道必须先由子进程通过函数CreateNamedPipe创建,只有待子进程创建好管道后父进程才能根据管道创建管道文件,将消息写入管道文件并及时发送给子进程。而且这个管道只能使用一次,即每次发送完消息后那个管道不能在使用了,必须再由子进程创建一个管道,A 进程才能再次创建管道文件并向其中写入消息。这个程序也不一定要MFC实现,还可以用其他的技术和语言实现,比如说Java、VB等,外表构架可以不一样,但核心技术都是一样的,只是不同的调用形式和调用方法,比如说在VB中,实现进程间的一般通信就是使用动态数据交换DDE,实现起来就比较简单,但是要创建映像文件和管道文件就比较繁琐,可以根据不同的需求采用不同的语言。 五、程序部分源代码: 1.“利用SendMessage发送消息”按钮中的主要代码 //找到接收消息的窗口(窗口名为Receiver) CString str="进程B"; CWnd *pWnd=CWnd::FindWindow(NULL,str); if(pWnd) { COPYDATASTRUCT buf; char * s=new char[m_Msg1.GetLength()]; //m_Msg1为CString类型的变量 s=m_Msg1.GetBuffer(0);
实验四:进程的管道通信 实验题目 进程的管道通信 实验目的 加深对进程概念的理解,明确进程和程序的区别。学习进程创建的过程,进一步认识进程并发执行的实质。分析进程争用资源的现象,学习解决进程互斥的方法。学习解决进程同步的方法。掌握Linux系统中进程间通过管道通信的具体实现 实验内容 使用系统调用pipe()建立一条管道,系统调用fork()分别创建两个子进程,它们分别向管道写一句话,如: Child process1 is sending a message! Child process2 is sending a message! 父进程分别从管道读出来自两个子进程的信息,显示在屏幕上。 当然,仅仅通过屏幕上输出这两句话还不能说明实现了进程的管道通信,为了能够更好的证明和显示出进程的同步互斥和通信,在其中要加入必要的跟踪条件,如一定的输出语句等,来反映程序的并发执行情况 实验要求 这是一个设计型实验,要求自行、独立编制程序。两个子进程要并发执行。实现管道的互斥使用。当一个子进程正在对管道进行写操
作时,另一个欲写入管道的子进程必须等待。使用系统调用lockf(fd[1],1,0)实现对管道的加锁操作,用lockf(fd[1],0,0)解除对管道的锁定。实现父子进程的同步,当父进程试图从一空管道中读取数据时,便进入等待状态,直到子进程将数据写入管道返回后,才将其唤醒。 为了清楚的反应进程的同步,在子进程完成相应的操作后,调用sleep()函数睡眠一段时间(程序中定为3s)。父进程先执行wait()函数,当有子进程执行完毕后,会得到子进程的返回结果并清理子进程。若子进程没执行完,父进程一直执行wait()进行监听,知道有一个子进程执行完成为僵尸进程。 程序中用到的系统调用 因为程序时在linux系统上进行编写的,所以其中要利用到相关的linux提供的系统调用。 所用到的系统调用包含在如下头文件中。 #include
操作系统实验报告 课程名称:操作系统 开课学期: 班级: 指导老师: 实验题目:共享存储区的创建、附接和断接学号: 姓名: 提交时间:
一、实验目的和要求 了解和熟悉共享存储机制 二、实验内容 编制一长度为1k的共享存储区发送和接收的程序。 程序设计 (1)为了便于操作和观察结果,用一个程序作为“引子”,先后fork()两个子进程,SERVER和CLIENT,进行通信。 (2)SERVER端建立一个key为75的共享区,并将第一个字节设置为-1,。作为数据空的标志。等待其他进程发来的消息。当字节的值发生变化时,表示收到了信息,进行处理。然后再次把它的值设为-1。如果遇到的值为0,则视为结束信号,取消该队列,并退出SERVER。SERVER每接收到一个数据后显示“(server)receive”。 (3)CLIENT端建立一个key 为75的共享区,当共享取得第一个字节为-1时,Server端空闲,可发送请求。CLIENT随即填入9到0。期间等待server 端的再次空闲。进行完这些操作后,CLIENT退出。CLIENT每发出一次数据后显示“(client)sent”。 (4)父进程在SERVER和CLIENT均退出后结束。 三、实验运行结果 1.涉及的系统调用 (1)shmget( ) 创建、获得一个共享存储区。 系统调用格式:shmid=shmget(key,size,flag) (2)、shmat( ) 共享存储区的附接。从逻辑上将一个共享存储区附接到进程的虚拟地址 空间上。 系统调用格式:virtaddr=shmat(shmid,addr,flag) (3)3、shmdt( ) 把一个共享存储区从指定进程的虚地址空间断开。 系统调用格式:shmdt(addr) (4)、shmctl( ) 共享存储区的控制,对其状态信息进行读取和修改。 系统调用格式:shmctl(shmid,cmd,buf) 2.实验源代码:
工程大学实验报告 专业班级:姓名:学号: 课程名称:操作系统 实验成绩:指导教师:蔡敦波 实验名称:进程控制与进程间通信 一、实验目的: 1、掌握进程的概念,明确进程和程序的区别。 2、认识和了解并发执行的实质。 3、了解什么是信号。 4、熟悉LINUX系统中进程之间软中断通信的基本原理。 二、实验内容: 1、进程的创建(必做题) 编写一段程序,使用系统调用fork( )创建两个子进程,在系统中有一个父进程和两个子进程活动。让每个进程在屏幕上显示一个字符;父进程显示字符“a”,子进程分别显示字符“b”和“c”。试观察记录屏幕上的显示结果,并分析原因。 <参考程序>
运行的结果是bca. 首先创建进程p1,向子进程返回0,输出b.又创建进程p2,向子进程返回0,输出c,同时向父进程返回子进程的pid,输出a 2、修改已编写的程序,将每个进程的输出由单个字符改为一句话,再观察程序执行时屏幕上出现的现象,并分析其原因。(必做题) <参考程序> # include
运行的结果是如上图所示. 首先创建进程p1,向子进程返回0,并for语句循环输出child +i字符串.又创建进程p2,向子进程返回0,输出字符串son+i,同时向父进程返回子进程的pid,输出字符串duaghter +i ,各打印5次。
实验4 进程的管道通信 1. 目的 1)加深对进程概念的理解,明确进程和程序的区别。 2)进一步认识并发执行的实质。 3)分析进程争用资源的现象,学习解决进程互斥的方法。 4)学习解决进程同步的方法。 5)了解Linux系统中进程通信的基本原理。 进程是操作系统中最重要的概念,贯穿始终,也是学习现代操作系统的关键。通过本次实验,要求理解进程的实质和进程管理的机制。在Linux系统下实现进程从创建到终止的全过程,从中体会进程的创建过程、父进程和子进程之间的关系、进程状态的变化、进程之间的互斥、同步机制、进程调度的原理和以管道为代表的进程间的通信方式的实现。 2. 内容及要求 这是一个设计型实验,要求自行编制程序。 使用系统调用pipe()建立一条管道,两个子进程分别向管道写一句话: Child process1 is sending a message! Child process2 is sending a message! 父进程从管道读出来自两个子进程的信息,显示在屏幕上。 要求: 1)父进程先接收子进程1发来的消息,然后再接收子进程2发来的消息。 2)实现管道的互斥使用,当一个子进程正在对管道进行写操作时,另一子进程必须等待。使用系统调用lockf(fd[1],1,0)实现对管道的加锁操作,用lockf(fd[1],0,0)解除对 管道的锁定。 3)实现父子进程的同步,当子进程把数据写入管道后,便去睡眠等待;当父进程试图从一空管道中读取数据时,也应等待,直到子进程将数据写入管道后,才将其唤醒。 3.相关的系统调用 1)fork() 用于创建一个子进程。 格式:int fork(); 返回值:在子进程中返回0;在父进程中返回所创建的子进程的ID值;当返回-1时,创建失败。 2)wait() 常用来控制父进程与子进程的同步。 在父进程中调用wait(),则父进程被阻塞,进入等待队列,等待子进程结束。当子进程结束时,父进程从wait()返回继续执行原来的程序。 返回值:大于0时,为子进程的ID值;等于-1时,调用失败。 3)exit() 是进程结束时最常调用的。 格式:void exit( int status); 其中,status为进程结束状态。 4)pipe() 用于创建一个管道 格式:pipe(int fd); 其中fd是一个由两个数组元素fd[0]和fd[1]组成的整型数组,fd[0]是管道的读端口,用
实验6 进程间通信 一、实验目的: 通过本实验了解和掌握进程间通讯的相关知识, (1)了解进程通信的基本原理。 (2)了解和熟悉管道通信,消息传送机制及共享存储机制。二.实验内容 1.进程的管道通信 阅读下列程序,完成实验任务。 #include
实验三进程管理及进程通信 实验环境: Linux操作系统 实验目的: (1)利用Linux提供的系统调用设计程序,加深对进程概念的理解。 (2)体会系统进程调度的方法和效果。 (3)了解进程之间的通信方式以及各种通信方式的使用。 实验方法: 用vi 编写c 程序(假定程序文件名为prog1.c)编 译程序 $ gcc -o prog1.o prog1.c 或 $ cc -o prog1.o prog1.c 运行 $./prog1.o 实验内容及步骤: 实验1 编写程序。显示进程的有关标识(进程标识、组标识、用户标识等)。经过5 秒钟后,执行另一个程序,最后按用户指示(如:Y/N)结束操作。 编程截图:
运行结果: 实验2 参考例程1,编写程序。实现父进程创建一个子进程。体会子进程与父进程分 别获得不同返回值,进而执行不同的程序段的方法。 例程1:利用fork()创建子进程 /* 用fork()系统调用创建子进程的例子*/ main() { int i; if (fork()) /*父进程执行的程序段*/ i=wait(); /* 等待子进程结束*/{ printf("It is parent process.\n"); printf("The child process,ID number %d, is finished.\n",i); } else{
Printf(“It is child process.\n”); Sleep(10); Exit(); } } 运行结果: 思考: 子进程是如何产生的?又是如何结束的?子进程被创建后它的运行环境是怎样建立的? 答:是由父进程用fock()函数创建形成的,通过exit()函数自我结束,子进程被创建后核心 将其分配一个进程表项和进程标识符,检查同时运行的进程数目,并且拷贝进程表项的数据,由子进程继承父进程所有文件。 实验3 参考例程2,编写程序。父进程通过循环语句创建若干子进程。探讨进程的家族树 以及子进程继承父进程的资源的关系。 例程2:循环调用fork()创建多个子进程。 /*建立进程树*/ #include