实验八 进程通信应用
- 格式:doc
- 大小:273.50 KB
- 文档页数:6
进程间通信的⽅式及应⽤场景开头 每个进程的⽤户地址空间都是独⽴的,进程与进程之间,内部空间是隔离的,进程 A 不可能直接使⽤进程 B 的变量名的形式得到进程B 中变量的值。
但内核空间是每个进程都共享的,所以进程之间要通信必须通过内核。
实现进程与进程之间的通信,常⽤的⽅式主要有:管道、消息队列、共享内存、信号量、信号、socket等等。
⼀、管道 在 Linux 命令中,常见的“|”符号就是⼀种管道。
⽐如:ps auxf | grep mysql 上⾯的命令中,“|”的功能是将前⼀个命令(ps auxf)的输出,作为后⼀个命令(grep mysql)的输⼊。
这种管道没有名字,匿名管道,⽤完就销毁。
命名管道也被叫做 FIFO,因为数据的传输⽅式是先进先出(first in first out)。
管道传输数据是单向的,如果想相互通信,需要创建两个管道才⾏。
管道创建、写⼊、读取创建mkfifo myPipe myPipe 是新创建的管道的名称,基于 Linux ⼀切皆⽂件的理念,管道也是以⽂件的⽅式存在,可以⽤ ls 看到⽂件类型是 p,也就是pipe(管道)的意思:$ ls -lprw-r--r--. 1 root root 0 Jul 1702:45 myPipeecho "hello" > myPipe # 将数据写进管道。
程序会阻塞,只有当管道⾥的数据被读完后,程序才会正常继续。
管道写⼊数据cat < myPipe # 读取管道⾥的数据# hello管道读取数据管道的优缺点 缺点:管道的通信⽅式效率低,不适合进程间频繁地交换数据。
优点:简单。
⼆、消息队列 前⾯说到管道的通信⽅式效率很低,因此管道不适合进程间频繁地交换数据。
对于这个问题,消息队列可以解决。
⽐如,A 进程要给 B 进程发送消息,A 进程将数据存⼊消息队列,B 进程只需要读取数据即可。
反之亦如此。
消息队列的本质是保存在内核中的⼀种消息链表,在发送数据时,会分成独⽴的数据单元,也就是消息体(数据块)。
计算机工程学院实验报告课程名称:操作系统实验班级实验成绩:指导教师:姓名:实验项目名称:进程与进程通信学号:上机实践日期:2009-11-13实验项目编号:实验二组号:上机实践时间:2学时一、目的1、深刻理解进程和线程的概念;2、掌握线程和进程的差别以及与之相适应的通信方式;3、掌握在Linux环境下创建进程: fork()的应用;4、了解用fork()创建进程、以及整个程序的运行过程;5、掌握多进程的程序设计与进程之间通信的方法;6、掌握共享内存、信号灯集实现进程通信的方法;7、理解、掌握Linux下文件系统,以及其安装与卸载过程。
二、实验内容1、在Linux环境下,用fork()创建多个进程,分别运行不同的函数;2、一部分进程代表读者,一部分进程代表写者;用共享内存、信号灯集机制实现各个读者、写者进程之间的通信;3、掌握shmget()、shmat()、shmctl()以及semget()、semctl()、semop()等函数在进程通信中的使用方法;4、用信号灯加PV操作实现进程间的互斥与同步。
三、实验环境1、操作系统:Red Hat Linux四、实验原理1、枚举数据类型,在信号灯集初始化时使用。
该结构在sys/sem.h中没有定义,必须程序设计者自行定义。
其中:semid—已经创建的信号灯集ID,sn—操作的元素的IDunion semun{int val;struct semid_ds *buf;ushort *array;};2、对信号灯集中的某个元素进行P操作。
首先要定义一个sembuf 类型的变量(该类型已经在sys/sem.h中预定义,可以直接引用),然后对该变量的各个元素进行赋值,注意进行P操作,主要是sem_op元素赋值为 -1。
其中:semid—已经创建的信号灯集ID,sn—操作的元素的IDvoid down(int semid,int sn){/* define P operating*/struct sembuf op;op.sem_num=sn;op.sem_op=-1;op.sem_flg=0;semop(semid,&op,1);}3、对信号灯集中的某个元素进行V操作。
一、实验目的1. 理解进程通信的概念和作用。
2. 掌握进程通信的常用方法,包括管道、消息队列、信号量等。
3. 通过编程实践,加深对进程通信机制的理解和应用。
二、实验环境操作系统:Linux开发环境:gcc三、实验内容1. 管道通信2. 消息队列通信3. 信号量通信四、实验步骤及分析1. 管道通信(1)实验步骤1)创建一个父进程和一个子进程;2)在父进程中创建一个管道,并将管道的读端和写端分别赋给父进程和子进程;3)在父进程中,通过管道的写端发送数据给子进程;4)在子进程中,通过管道的读端接收父进程发送的数据;5)关闭管道的读端和写端;6)结束进程。
(2)实验分析通过管道通信,实现了父进程和子进程之间的数据传递。
管道是半双工通信,数据只能单向流动。
在本实验中,父进程向子进程发送数据,子进程接收数据。
2. 消息队列通信(1)实验步骤1)创建一个消息队列;2)在父进程中,向消息队列中发送消息;3)在子进程中,从消息队列中接收消息;4)删除消息队列;5)结束进程。
(2)实验分析消息队列是一种进程间通信机制,允许不同进程之间传递消息。
消息队列的创建、发送、接收和删除等操作都是通过系统调用实现的。
在本实验中,父进程向消息队列发送消息,子进程从消息队列接收消息,实现了进程间的消息传递。
3. 信号量通信(1)实验步骤1)创建一个信号量;2)在父进程中,对信号量执行P操作,请求资源;3)在子进程中,对信号量执行V操作,释放资源;4)结束进程。
(2)实验分析信号量是一种用于实现进程同步的机制。
在进程通信中,信号量可以用来协调多个进程对共享资源的访问。
在本实验中,父进程和子进程通过信号量实现了对共享资源的同步访问。
五、实验结果1. 管道通信实验结果:父进程成功向子进程发送数据,子进程成功接收数据。
2. 消息队列通信实验结果:父进程成功向消息队列发送消息,子进程成功从消息队列接收消息。
3. 信号量通信实验结果:父进程成功获取资源,子进程成功释放资源。
第1篇一、实验目的1. 理解进程通信的概念和原理;2. 掌握进程通信的常用机制和方法;3. 能够使用进程通信机制实现进程间的数据交换和同步;4. 增强对操作系统进程管理模块的理解。
二、实验环境1. 操作系统:Linux2. 编程语言:C3. 开发环境:GCC三、实验内容1. 进程间通信的管道机制2. 进程间通信的信号量机制3. 进程间通信的共享内存机制4. 进程间通信的消息队列机制四、实验步骤1. 管道机制(1)创建管道:使用pipe()函数创建管道,将管道文件描述符存储在两个变量中,分别用于读和写。
(2)创建进程:使用fork()函数创建子进程,实现父子进程间的通信。
(3)管道读写:在父进程中,使用read()函数读取子进程写入的数据;在子进程中,使用write()函数将数据写入管道。
(4)关闭管道:在管道读写结束后,关闭对应的管道文件描述符。
2. 信号量机制(1)创建信号量:使用sem_open()函数创建信号量,并初始化为1。
(2)获取信号量:使用sem_wait()函数获取信号量,实现进程同步。
(3)释放信号量:使用sem_post()函数释放信号量,实现进程同步。
(4)关闭信号量:使用sem_close()函数关闭信号量。
3. 共享内存机制(1)创建共享内存:使用mmap()函数创建共享内存区域,并初始化数据。
(2)映射共享内存:在父进程和子进程中,使用mmap()函数映射共享内存区域。
(3)读写共享内存:在父进程和子进程中,通过指针访问共享内存区域,实现数据交换。
(4)解除映射:在管道读写结束后,使用munmap()函数解除映射。
4. 消息队列机制(1)创建消息队列:使用msgget()函数创建消息队列,并初始化消息队列属性。
(2)发送消息:使用msgsnd()函数向消息队列发送消息。
(3)接收消息:使用msgrcv()函数从消息队列接收消息。
(4)删除消息队列:使用msgctl()函数删除消息队列。
进程控制与进程通信程序实验报告一、引言进程是计算机系统中最基本的概念之一,是操作系统中最小的资源管理单位。
进程控制与进程通信是操作系统中重要的内容,涉及到进程的创建、调度和终止,以及进程间的信息传递和同步管理。
本实验旨在通过编写进程控制与进程通信程序,加深对操作系统中进程管理和通信机制的理解。
二、实验目的1. 理解进程的概念和特点,掌握进程的创建、调度和终止方法。
2. 掌握进程通信的基本原理和方法,包括共享内存、管道、消息队列和信号量等。
3. 能够编写简单的进程控制和进程通信程序。
三、实验内容1. 进程控制实验:编写一个程序,实现进程的创建、调度和终止。
通过调用系统调用函数,创建多个子进程,并通过进程控制函数实现父子进程的协作与同步。
2. 进程通信实验:编写一个程序,实现进程间的信息传递和同步管理。
通过共享内存、管道、消息队列或信号量等机制,实现不同进程之间的数据交换和共享。
四、实验步骤1. 进程控制实验:(1)创建父进程和子进程:使用fork()函数创建子进程,并通过判断返回值来区分父子进程。
(2)调度子进程:使用wait()函数等待子进程的结束,以实现父子进程的同步。
(3)终止子进程:使用exit()函数终止子进程的运行。
2. 进程通信实验:(1)共享内存:使用shmget()函数创建共享内存段,使用shmat()函数映射共享内存到进程的地址空间,实现共享数据的读写。
(2)管道:使用pipe()函数创建管道,使用fork()函数创建子进程,通过读写管道实现进程间的数据传输。
(3)消息队列:使用msgget()函数创建消息队列,使用msgsnd()函数向消息队列发送消息,使用msgrcv()函数从消息队列接收消息,实现进程间的消息传递。
(4)信号量:使用semget()函数创建信号量,使用semop()函数对信号量进行P操作和V操作,实现进程间的同步和互斥。
五、实验结果通过实验,我们成功实现了进程的创建、调度和终止,以及进程间的信息传递和同步管理。
实现进程间通信的实验原理进程间通信(Inter-Process Communication,IPC)是指在操作系统中,不同的进程之间进行数据交换和共享的一种机制。
常见的进程间通信的方法有:1. 管道(Pipe):管道是一种半双工的通信机制,它可以实现父子进程之间的通信。
通常由操作系统创建,父进程创建一个管道后,可以通过fork系统调用创建子进程,从而共享管道。
子进程可以通过管道进行写入一端,父进程可以通过管道进行读取。
2. 命名管道(Named Pipe):命名管道也是一种管道,但它允许不相关的进程之间进行通信。
命名管道被创建时,通过指定一个路径名,从而使不同进程能够通过路径名来访问同一管道。
3. 信号量(Semaphore):信号量是一种计数器,用于控制多个进程对共享资源的访问。
进程可以通过特定的操作(比如P操作和V操作)来对信号量进行增加或减少操作。
同一时刻只允许一个进程对信号量进行P操作,其他进程需要等待。
4. 共享内存(Shared Memory):共享内存是一种进程之间共享数据的方式,它在物理内存中创建一块共享区域,多个进程可以将这块内存映射到各自的虚拟地址空间中。
进程可以直接读写共享内存,而无需进行数据拷贝。
5. 消息队列(Message Queue):消息队列是一种可以实现不同进程之间通过消息进行通信的机制。
进程可以通过特定的操作将消息发送到消息队列中,其他进程可以从消息队列中读取消息。
6. 套接字(Socket):套接字是一种网络编程中常用的进程间通信方式。
它可以在不同主机上的进程之间进行通信。
进程可以通过套接字进行网络数据的读取和写入。
以上是常见的几种进程间通信的方法,每种方法都有自己的优势和适用场景。
根据具体的需求,可以选择适合的方式进行进程间通信。
进程间通信方式及应用场景进程间通信是操作系统中的一个重要概念,它指的是不同进程之间进行数据交换和共享的一种方式。
进程间通信可以通过多种机制来实现,每种机制都有其优缺点和适用场景。
1. 管道(Pipe):管道是进程间通信中最简单的一种方式,它是一个字节流的通道。
管道可以分为匿名管道和命名管道两种形式。
匿名管道通常是单向的、存在于父子进程之间,并且不能用于无关联的进程之间通信。
匿名管道适用于需要父子进程之间的双向通信,如进程的输入输出重定向。
命名管道是一种特殊的文件,允许无关联进程之间进行通信,可以用于多个进程之间的双向通信。
命名管道适用于多个进程之间进行数据交换,如服务器和客户端之间的通信。
2. 消息队列(Message Queue):消息队列是一种先进先出的消息传递机制。
消息队列在内核中创建,用于进程间传递数据。
消息队列可以通过消息标识符来寻址,而不是进程标识符。
消息队列适用于多个进程之间的数据传递,具有较大的灵活性和可靠性,可以避免数据丢失。
它可以实现进程之间的异步通信,提高系统性能,如网络数据包的传输、进程间的任务交换等。
3. 信号量(Semaphore):信号量是一种非负整数变量,用于多个进程之间的同步和互斥操作。
它可以用来实现进程之间的互斥和资源的共享。
信号量可以通过P操作(等待信号量)和V操作(释放信号量)来实现进程之间的同步。
P操作会将信号量值减一,如果信号量值为负,则进程进入等待状态。
V操作会将信号量值加一,并唤醒等待的进程。
信号量适用于需要多个进程之间的同步和互斥操作,如进程的互斥访问、控制资源的分配等。
4. 共享内存(Shared Memory):共享内存是进程间通信中最快、最高效的一种方式,它允许多个进程共享同一块物理内存空间。
共享内存通过将一段虚拟内存区域映射到多个进程的地址空间来实现。
共享内存适用于大量数据的高速传输和共享,比如图像处理、音视频编码、数据库等需要高速数据交换和共享的应用场景。
2.编辑结果如下。
3.编译和运行程序。
4. 运行解释结果
父进程创建一个子进程和一个无名管道fd。
父进程wait(0)等待子进程的终止信号,子进程向管道写入信息“This is a message。
”然后终止,父进程收到终止信号后从管道读出信息并显示在屏幕上后结束。
2.编辑结果如下。
3.编译和运行程序。
4. 运行解释结果
父进程先创建一个子进程1,子进程1往管道fd里写入信息,并输出parent的信息,父进程接着有创造了一个子进程2,子进程2也往fd管道fd里写入信息,父进程wait(0)等待两个子进程结束信号,等两个子进程都结束后,输出管道里的信息。
实现了父子进程之间的管道通信。
1.编辑源程序。
2.编辑结果如下。
3.编译和运行程序。
4. 运行解释结果
发送进程将要发送的信息从键盘输入,每输入一行就作为一条信息发送,用“end”作为结
束的信息,存入共享的内存区。
接收进程从信息队列逐个取出信息并显示输出。
1.编辑源程序。
2.编辑结果如下。
3.编译和运行程序。
4. 运行解释结果
发送进程将要发送的信息从键盘输入,每输入一行就作为一条信息发送,用“end”作为结束的信息,存入信息缓冲区。
接收进程从信息队列逐个取出信息并显示输出。
实验:进程之间的通信管道1.Pipe函数与进程通信下面实验为使用管道进行父子进程间通信。
程序首先判断参数是否合法,因为输入的字符将从父进程通过发送到子进程中。
然后,调用pipe函数创建父子进程用于通信的管道。
使用fork函数创建子进程时,子进程会获得与父进程相同的资源,其中包括文件描述符信息。
因此,调用fork函数须在pipe函数调用前。
当父子进程通过管道进行通信时,files[1]为用于数据写入的文件描述符.因此,在子进程中,要读取管道中的数据可以调用read函数,而读取得文件描述符为files[0]。
对于父进程而言,写入数据需要调用write 函数,要写入的文件描述为files[1]。
#include <stdio.h>#include <unistd.h>int main(int argc,char* argv[]){int f_des[2];int pid;char msg[BUFSIZ];if(argc!=2){printf("Usage: %s message\n",argv[0]);return 1;}if(pipe(f_des)==-1){perror("cannot create the IPC pipe");return 1;}pid=fork();if(pid==-1){perror("cannot create new process");return 1;}else if(pid==0){close(f_des[1]);if(read(f_des[0],msg,BUFSIZ)==-1){perror("child process cannot read data from pipe");return 1;}elseprintf("in child process, receive message: %s\n",msg);_exit(0);}else {close(f_des[0]);if(write(f_des[1],argv[1],strlen(argv[1]))==-1){perror("parent process cannot write data to pipe");return 1;}elseprintf("in parent process, send message: %s\n",argv[1]);wait(NULL);_exit(0);}return 0;}2. Shell管道重订向的实现实现了在SHELL中的两个命令的组合。
实验八进程通信一.实验引子:进程之间的信息交换称为进程通信。
进程通信因其交换的信号量多少又可分为低级通信和高级通信。
每次交换的信息量较少,甚至只是一个状态值,如进程的同步与互斥,这样的通信叫低级通信;若进程之间交换的信息量比较大,这样的通信叫作高级通信。
管道通信就是其中的一种高级通信机制。
从本质上说,管道就是一种用于连接读进程和写进程,以实现它们之间通信的共享文件。
向管道提供输入数据的发送进程(写进程)以字符流的形式将大量的数据送入管道,而接收进程则从管道中接收数据。
本实验中,Client为发送方,Server为接收方。
二.实验目的:通过该实验,让学生体会操作系统提供的进程通信机制之一——管道通信。
三.实验过程:按照实验指导的要求,分别建立一个Client工程、一个Server工程。
待两个工程都编译通过没有错误之后,分别打开两个工程下的Debug文件夹,执行其中的可执行程序。
进而观察两个进程之间的通信。
要求:写出实验过程,给出实验结果。
编辑框——右击建立类向导——Member Variables——Add Variables——m_msg——ok ——确定编辑框——右击建立类向导——Member Variables——Add V ariables——m_list——ok——确定将client文件夹和server文件夹下的Debug文件夹打开,分别运行client.exe和server.exe四.实验小结:管道是用来连接读进程和写进程的,实现通信间共享文件。
Client即向管道提供输入数据的发送进程以字符流的形式将大量的数据送入管道,而Server即接收进程则从管道中接收数据。
实现进程之间的高级通信。
实验八进程通信应用
实验目的
1. 掌握命名管道的原理和应用
2. 掌握文件映射共享内存的原理
3、掌握基于进程通信的程序设计方法
实验内容
1.(1)命令行下创建有名管道myfifo,ls –l查看,分别在两个终端运行cat < myfifo 和echo hello >myfifo分析执行情况,将cat在后台运行,echo在前台运行,执行情况如何?分析FIFO的打开规则。
(2)设计两个程序,要求用命名管道FIFO,实现文本文件复制功能,即实现copy file1 file2的功能,file1是已经存在的文件,file2可以不存在,如果存在就清空原来的内容,通过建立的有名管道实现file1的内容复制到file2。
一、 FIFO
1.有名管道的创建
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char * pathname, mode_t mode)
该函数的第一个参数是一个普通的路径名,也就是创建后FIFO的名字。
第二个参数与打开普通文件的open()函数中的mode 参数相同。
2. 有名管道的打开
有名管道创建成功后并没有打开,需要使用系统调用open( )将对应的文件打开。
open(const char*pathname,oflg)
其中,参数pathname是要打开的有名管道的路径名,oflg是文件打开时的存取方式。
必须指定的互斥模式:
O_RDONLY, O_WRONLY, O_NONBLOCK
O_RDONLY:若无进程写方式打开FIFO,open阻塞
O_RDONLY |O_NONBLOCK:若无进程写方式打开FIFO,open立即返回
O_WRONLY:若无进程读方式打开FIFO,open阻塞
O_WRONLY| O_NONBLOCK:若无进程读方式打开FIFO,open返回ENXIO错误,-1
FIFO的打开规则:
(1)如果当前打开操作是为读而打开FIFO时,若已经有相应进程为写而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为写而打开该FIFO(当前打开操作设置了阻塞标志);或者,成功返回(当前打开操作没有设置阻塞标志)。
(2)如果当前打开操作是为写而打开FIFO时,如果已经有相应进程为读而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为读而打开该FIFO(当前打开操作设置了阻塞标志);或者,返回ENXIO错误(当前打开操作没有设置阻塞标志)。
3.有名管道的读写
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
返回值: 读到的字节数,若已到文件尾为0,若出错为-1
ssize_t write(int fd, const void *buf, size_t count);
返回值: 若成功为已写的字节数,若出错为-1
从FIFO中读取数据read
以阻塞方式打开的FIFO,read调用:
①当前FIFO内没有数据,read调用将一直阻塞。
②当前FIFO内有数据,但有其它进程在读这些数据
解除阻塞的原因则是FIFO中有新的数据写入,不论信写入数据量的大小,也不论读操作请求多少数据量。
●以非阻塞方式打开的FIFO,read调用:
当前FIFO内没有数据,立即返回-1,当前errno值为EAGAIN,提醒以后再试。
向FIFO中写入数据write
●对于设置了阻塞标志的写操作:
当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
如果此时管道空闲缓冲区不足以容纳要写入的字节数,则进入睡眠,直到当缓冲区中能够容纳要写入的字节数时,才开始进行一次性写操作。
当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。
FIFO缓冲区一有空闲区域,写进程就会试图向管道写入数据,写操作在写完所有请求写的数据后返回。
●对于没有设置阻塞标志的写操作:
当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
如果当前FIFO 空闲缓冲区能够容纳请求写入的字节数,写完后成功返回;如果当前FIFO空闲缓冲区不能够容纳请求写入的字节数,则返回EAGAIN错误,提醒以后再写;
当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。
在写满所有FIFO空闲缓冲区后,写操作返回。
4.有名管道删除
有名管道和普通文件一样,存在于磁盘中。
但是有名管道不能直接存储文件,其存储的通讯信息在通信的两个进程结束后自动丢失。
与无名管道不同,通信的两个进程结束后,有名管道文件依然存在。
需要使用系统调用unlink( )删除有名管道文件。
头文件:
#include <unistd.h>
系统调用格式:
int unlink(const char *pathname);
unlink( )会删除参数pathname指定的文件。
二、 mmap
Linux提供了内存映射函数mmap, 它把文件内容映射到一段内存上, 通过对这段内存的读取和修改, 实现对文件的读取和修改。
头文件:<unistd.h> <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offsize);
返回值: 成功则返回映射区起始地址, 失败则返回MAP_FAILED(-1).
参数:
addr: 指定映射的起始地址, 通常设为NULL, 由系统指定.
length: 将文件的多大长度映射到内存.
prot: 映射区的保护方式, 可以是:
PROT_EXEC: 映射区可被执行.
PROT_READ: 映射区可被读取.
PROT_WRITE: 映射区可被写入.
PROT_NONE: 映射区不能存取.
flags: 映射区的特性, 可以是:
MAP_SHARED: 对映射区域的写入数据会复制回文件, 且允许其他映射该文件的进程共
享.
MAP_PRIVATE: 对映射区域的写入操作会产生一个映射的复制(copy-on-write), 对此区域所做的修改不会写回原文件.
MAP_ANONYMOUS:采取匿名映射。
fd: 由open返回的文件描述符, 代表要映射的文件,匿名映射时指定为-1。
offset: 以文件开始处的偏移量, 必须是分页大小的整数倍, 通常为0, 表示从文件头开始映射.
内存映射的步骤:
用open系统调用打开文件, 并返回描述符fd.
用mmap建立内存映射, 并返回映射首地址指针start.
对映射(文件)进行各种操作, 显示(printf), 修改(sprintf).
用munmap(void *start, size_t lenght)关闭内存映射.
用close系统调用关闭文件fd.
在修改映射的文件时, 只能在原长度上修改, 不能增加文件长度, 因为内存是已经分配好的。