操作系统lecture5_进程间通信
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
#include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> main() { char buffer[80]; int fd; char *FIFO=“pipe1”; mkfifo(FIFO,0666); if(fork()>0){ char s[ ] = "hello!\n"; fd = open (FIFO,O_WRONLY); write(fd,s,sizeof(s)); close(fd); } else{ fd= open(FIFO,O_RDONLY); read(fd,buffer,80); printf("%s",buffer); close(fd); } }
Linux操作系统原理与应用
第五讲:进程间通信
主讲:张齐勋 zhangqx@
1
提纲
概述 管道 消息队列 共享内存 信号量
一、概述
在Linux系统中,以进程为单位来分配和管理资源。 由于保护的缘故,一个进程不能直接访问另一个 进程的资源,也就是说,进程之间互相封闭。 在一个复杂的应用系统中,通常会使用多个相关 的进程来共同完成一项任务,因此要求进程之间 必须能够互相通信,从而来共享资源和信息。 所以,一个操作系统内核必须提供进程间的通 信机制。
int msgget (key_t key, int fnt msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int flag) 其中:msqid是消息队列的队列ID;
fd[0]――Pipe的出口
•进程通信 管道 (pipe系统调用) pipe(fd): 创建一个管道,fd[0]为管道的读端;fd[1]为管道的写端。 管道可用来实现父进程与其子孙进程之间的通信。管道以FIFO方式传送消息。
#include <stdio.h> #include <stdlib.h> main() { int x,fd[2]; char buf[30],s[30]; pipe(fd); /*创建管道*/ while((x=fork()) = = -1); /*创建子进程失败时,循环*/ if(x = = 0) { sprintf(buf,“This is an example\n”); write(fd[1],buf,30); /*把buf中的字符写入管道*/ exit(0); } wait(0); read(fd[0],s,30); /*父进程读管道中的字符*/ printf(“%s”,s); }
系统文件 write(fd[1],buf,size) 功能:把buf中的长度为size字符的消息送入管道入口fd[1]
fd[1]—pipe入口
buf:存放消息的空间 size :要写入的字符长度
系统文件 read(fd[0],buf,size)
功能:从pipe出口fd[0]读出size字符的消息置入 buf中。
有名管道的创建 #include <sys/types.h> #include <sys/stat.h> int mkfifo(const char * pathname, mode_t mode)
该函数的第一个参数是一个普通的路径名,也就是创建后FIFO的 名字。 第二个参数与打开普通文件的open()函数中的mode 参数相同。 如果mkfifo的第一个参数是一个已经存在的路径名时,会返回 EEXIST错误,所以一般典型的调用代码首先会检查是否返回该错 误,如果确实返回该错误,那么只要调用打开FIFO的函数就可以 了。一般文件的I/O函数都可以用于FIFO,如close、read、write 等等。
此命令只能由下列两种进程执行:一种是其有效用户ID等于 msg_perm.cuid或msg_perm.uid;另一种是具有超级用户特权 的进程。
消息 实例分析
问题描述
设计两个程序,要求用消息队列实现聊天程序。增 加结束字符,比如最后输入“end”后结束进程。 设消息队列的key为1234。
send.c
int msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz,long msgtyp, int flag)
msqid是消息队列的引用标识符; msgp是接收到的消息将要存放的缓冲区; msgsz是消息的大小; msgtyp是期望接收的消息类型; msgflg是标志。
命令1:“ls > tmp” 命令2:”more < tmp” 命令1把ls的输出重定向到tmp文件中; 命令2把more的输入重定向到tmp文件
创建管道
管道的创建: #include <unistd.h> int pipe(int fd[2]) 该函数创建的管道的两端处于一个 进程中间,在实际应用中没有太大意 义,因此,一个进程在由pipe()创建管 道后,一般再fork一个子进程,然后 通过管道实现父子进程间的通信。
有名管道 管道应用的一个重大限制是它没有名字,因此,只能用于 具有亲缘关系的进程间通信,在有名管道(named pipe 或FIFO)提出后,该限制得到了克服。 FIFO不同于管道之处在于它提供一个路径名与之关联, 以FIFO的文件形式存在于文件系统中。这样,即使与 FIFO的创建进程不存在亲缘关系的进程,只要可以访问 该路径,就能够彼此通过FIFO相互通信(能够访问该路 径的进程以及FIFO的创建进程之间)。 因此,通过FIFO不相关的进程也能交换数据。值得注意 的是,FIFO严格遵循先进先出(first in first out),对 管道及FIFO的读总是从开始处返回数据,对它们的写则 把数据添加到末尾。
int msgctl (int msqid, int cmd, struct msqid_ds *buf) msqid是消息队列的引用标识符; cmd是执行命令; buf是一个缓冲区。 cmd参数指定对于由msqid规定的队列要执行的命令:
IPC_STAT 取此队列的msqid_ds结构,并将其存放在buf指向的结 构中。 IPC_SET 按由buf指向的结构中的值,设置与此队列相关的结构中的 下列四个字段: msg_perm.uid、msg_perm.gid、msg_perm;mode和 msg_qbytes。此命令只能由下列两种进程执行:一种是其有效用户ID 等于msg_perm.cuid或msg_perm.uid;另一种是具有超级用户特权的 进程。只有超级用户才能增加msg_qbytes的值。 IPC_RMID 从系统中删除该消息队列以及仍在该队列上的所有数据。 这种删除立即生效。仍在使用这一消息队列的其他进程在它们下一次 试图对此队列进行操作时,将出错返回EIDRM。
三、消息队列
消息队列就是消息的一个链表,它允许一个或多个进 程向它写消息,一个或多个进程从中读消息。具有一定的 FIFO的特性,但是可实现消息的随即查询。这些消息存 在于内核中,由“队列ID”来标识。 消息队列的实现包括创建和打开队列、添加消息、读 取消息和控制消息队列这四种操作。 msgget:创建和打开队列,其消息数量受系统限制。 msgsnd:添加消息,将消息添加到消息队列尾部。 msgrcv:读取消息,从消息队列中取走消息。 msgctl:控制消息队列。
注意:fd[0] 用于读取管道,fd[1] 用于写入 管道。
管道读写
管道主要用于不同进程间通信。实际上,通常先创建 一个管道,再通过fork函数创建一个子进程。
子进程写入和父进程读的命名管道:
关闭父进程fd[1] 和 子进程[0]
管道的读写规则
管道两端可分别用描述字fd[0]以及fd[1]来描 述。 需要注意的是,管道的两端是固定了任务的。 即一端只能用于读,由描述字fd[0]表示,称其为 管道读端;另一端则只能用于写,由描述字fd[1] 来表示,称其为管道写端。 如果试图从管道写端读取数据,或者向管道 读端写入数据都将导致错误发生。 一般文件的I/O函数都可以用于管道,如close、 read、write等等。
msgp是消息内容所在的缓冲区; msgsz是消息的大小; msgflg是标志,IPC_NOWAIT若消息并没有立交发送 而调用进程会立即返回。
struct msgbuf { long mtype; /* type of message */ char mtext[1]; /* message text */ };
管道是Linux支持的最初Unix IPC形式之一,具有以下特点: 管道是半双工的,数据只能向一个方向流动;需要双方通 信时,需要建立起两个管道; 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进 程); 单独构成一种独立的文件系统:管道对于管道两端的进程 而言,就是一个文件,但它不是普通的文件,它不属于某 种文件系统,而是自立门户,单独构成一种文件系统,并 且只存在与内存中。 数据的读出和写入:一个进程向管道中写的内容被管道另 一端的进程读出。写入的内容每次都添加在管道缓冲区的 末尾,并且每次都是从缓冲区的头部读出数据。
server.c代码
运行结果
四、共享内存
共享内存可以说是最有用的进程间通信方 式,也是最快的IPC形式。两个不同进程A、B 共享内存的意思是,同一块物理内存被映射到 进程A、B各自的进程地址空间。进程A可以 即时看到进程B对共享内存中数据的更新,反 之亦然。由于多个进程共享同一块内存区域, 必然需要某种同步机制,互斥锁和信号量都可 以。
信号 实例分析
问题描述:
设计一个程序,由父进程创建一个子进程,在父进 程中显示3行“How are you!”,然后向子进程 发送软中断信号,等待子进程终止后输出结束信息 “OK”,然后终止执行。子进程中循环显示“I am child”,接受到父进程发来的软中断信号后停 止循环,显示“child exited!”并终止运行。
一、管道
管道和有名管道是最早的进程间通信机制之 一,管道可用于具有亲缘关系进程间的通信, 有名管道克服了管道没有名字的限制,因此, 除具有管道所具有的功能外,它还允许无亲缘 关系进程间的通信。 管道是指用于连接一个读进程和一个写进程, 以实现它们之间通信的共享方式,又称pipe文 件。
管道
有名管道比管道多了一个打开操作:open。 FIFO的打开规则: 如果当前打开操作是为读而打开FIFO时,若已经 有相应进程为写而打开该FIFO,则当前打开操作 将成功返回;否则,可能阻塞直到有相应进程为 写而打开该FIFO(当前打开操作设置了阻塞标 志);或者,成功返回(当前打开操作没有设置 阻塞标志)。 如果当前打开操作是为写而打开FIFO时,如果已 经有相应进程为读而打开该FIFO,则当前打开操 作将成功返回;否则,可能阻塞直到有相应进程 为读而打开该FIFO(当前打开操作设置了阻塞标 志);或者,返回ENXIO错误(当前打开操作没 有设置阻塞标志)。
管道是所有Unix都提供的一种IPC机制
一个进程将数据写入管道,另一个进程从管道中读取数 据 命令:“ls | more” 使用pipeline “|”将两个命令”ls”和“more”连接起来, 使得ls的输出成为more的输入 也可以使用如下的两个命令
在shell中使用管道的例子
管道的局限性
从IPC的角度看,管道提供了从一个进程向另一个进 程传输数据的有效方法。但是,管道有一些固有的局限性: 1.因为读数据的同时也将数据从管道移去,因此,管 道不能用来对多个接收者广播数据。 2.管道中的数据被当作字节流,因此无法识别信息的 边界。 如果一个管道有多个读进程,那么写进程不能发送数 据到指定的读进程。同样,如果有多个写进程,那么没有 办法判断是它们中哪一个发送的数据。