进程之间的通信Word格式.docx
《进程之间的通信Word格式.docx》由会员分享,可在线阅读,更多相关《进程之间的通信Word格式.docx(12页珍藏版)》请在冰点文库上搜索。
![进程之间的通信Word格式.docx](https://file1.bingdoc.com/fileroot1/2023-5/10/cfc524b1-1ce0-4ae8-b9b7-9ecf9b976818/cfc524b1-1ce0-4ae8-b9b7-9ecf9b9768181.gif)
(1)pipe()函数调用格式如下:
#include<
unistd.h>
intpipe(intfdes[2]);
参数:
fdes[2]数组存放返回的两个文件描述符。
返回值:
正确返回0,错误返回-1。
使用无名管道通信时,是使用临时文件的方式实现进程之间的批量数据传输。
若通信双方不是父子关系,不能直接建立通信联系。
必须由父进程先创建一个管道,再创建需要通信的两个子进程,两者是通过复制父进程映像使两者建立联系的。
(2)无名管道pipe()的使用示例
2、popen()和pclose()函数
系统提供了创建管道的简单函数popen()和pclose()。
当使用这两个函数对管道操作时,所有复杂的操作,如创建管道、fork()子进程、关闭管道的无用端,执行一个shell命令,等待命令执行完成后返回等操作都在系统内部自动完成。
(1)popen()和pclose()函数的调用格式如下:
stdio.h>
FILEpopen(constcharcmdstring,constchar*open_mode);
intpclose(FILE*fp);
其中,cmdstring是shell要求的包括参数和选项的一个完整的命令行。
open_mode指出管道访问方式:
读(“r”)或写(“w”)。
返回值:
popen()函数正确返回文件结构的指针,错误返回空指针NULL;
pclose()函数正确返回cmdstring结束时的退出状态,错误返回-1。
popen()函数先执行fork()函数创建一个子进程,然后调用exec()函数执行命令cmdstring,并且返回一个标准I/O文件指针。
若open_mode为“r”,那么管道的输入端与命令行cmdstring的标准输出端相连,即父进程以“r”方式打开管道并成功获得管道文件的指针,用于从管道读。
被创建的子进程执行cmdstring命令行,向管道输出,具体可见下图1所示若open_mode为“w”,那么管道的输出端与命令行cmdstring的标准输入端相连,即父进程以“w”方式打开管道并成功地获得管道文件的指针,用于向管道写,被创建的子进程从管道读入,以完成命令行的执行,如图2所示。
以命令行为参数的管道文件的示例。
二、有名管道的通信
1、创建一个有名管道的系统调用mkdfifo()
有名管道的使用方式与无名管道不同。
有名管道可被任何知道其名字的进程打开和使用。
为了使用有名管道,进程要先建立它,并与它的一端相连,创建有名管道的进程叫服务器进程,存取管道的其他进程叫客户进程。
通信双方必须首先创建有名管道后,才能打开进行读写。
当文件不再需要时,要显式删除。
调用格式如下:
#include<
sys/types.h>
sys/stat.h>
fcntl.h>
intmknod(constchar*pathname,mode_tmode,dev_tdev);
或
intmkfifo(constchar*pathname,mode_tmode);
其中pathname是新创建的有名管道的文件路径名,mode是被创建文件的类型和存取方式,dev是文件所在的设备。
对于有名管道,dev这个参数是0。
这两个系统调用隐含指定以“O_CREAT|O_EXCL”方式,创建一个新的FIFO文件。
返回值:
成功时,返回0;
不成功时(即文件已经存在),返回-1。
2、打开一个有名管道
由于有名管道创建时并没有打开,因此必须显式地使用如下的系统调用将文件打开:
open(pathname,oflg);
其中,pathname是要打开的有名管道的路径名,oflg是文件打开时的存取方式。
打开一个有名管道与一个普通文件没有区别,只是通信的发送者以OWRONLY只写方式、接收方以ORDONLY只读方式打开。
进程间使用有名管道实现通信时,必须有三次同步:
第一次是打开同步。
当一个进程以读方式打开有名管道时,若已有写者打开过,则唤醒写者后继续前进,否则,睡眠等待写者。
当一个进程以写方式打开有名管道时,若已有读者打开过,则唤醒读者后继续前进,否则等待读者。
第二次是读写同步。
其同步方式与pipe相同,运行写者超前读者1024个字符。
当一次写超过1024时,超过的字符要写入时,则写者必须等待。
读者从有名管道读时,若没有数据可读则等待。
若有数据可读,读完后要检查有无写者等待,若有唤醒写者。
而且要求读写两方要随时检查通信的另一方是否还存在,一旦有一方不存在,应立即终止通信过程。
第三次是关闭同步。
当一个写进程关闭有名管道时,若发现有进程睡眠等待从管道读,则唤醒它,被唤醒进程立即从读调用返回。
当一个读进程关闭有名管道时,若发现有进程睡眠等待向管道写,则唤醒它,并向它发一个指示错误条件的信号后返回。
最后一个关闭有名管道的基础,释放该管道占用的全部盘块及相应主存i节点。
有名管道打开后,就可以使用文件的读写命令进行读写,读写完成后就立即关闭。
有名管道文件关闭后,文件本身并没有消失。
有名管道的读、写和关闭动作与普通玩家完全相同。
有名管道的使用示例
习题:
编写一个有名管道程序。
一个(客户)进程从键盘循环读一系列字符,将这些字符和发送者的pid发给服务器进程,让其统计输入的是字符还是数字,分别为多少个,完成后再向客户进程发回服务的结果,由客户进程输出
4.源代码
stdarg.h>
#defineCLIENT2
#defineMAX_BUF2000
intmain(intargc,char*argv[])
{
mkfifo("
server"
010777,0);
client"
charbuf[MAX_BUF];
if(argc==CLIENT)
printf("
clienthasbeguntorun!
\n"
);
freopen("
./input.txt"
"
r"
stdin);
clienthasopenedinput.txt\n"
intfd;
clientiswaitingfortheservertofireup...\n"
fd=open("
O_WRONLY);
clienthasopenedtheserverpipe\n"
intid=getpid();
clienthasgotpid=%d.\n"
id);
sprintf(buf,"
%d"
clienthaswrittenpidtobuf\n"
gets(buf+strlen(buf));
clienthasappendinputdatatobuf\n"
write(fd,buf,sizeof(buf));
clientwirttendata=%stopipe.\n"
buf);
close(fd);
O_RDONLY);
clienthasopenedtheclientpipe\n"
read(fd,buf,sizeof(buf));
clienthasgottheresultfromserver\n"
%s\n"
}
else
serveriswaitingfortheclienttofireup...\n"
serverhasopenedtheserverpipe\n"
serverhasgotdata=%s\n"
intid;
sscanf(buf,"
%d"
&
id);
serverhasgotthepid=%d\n"
char*p=buf;
while(*p&
&
*p!
='
'
)p++;
serverisgoingtopasetheleftdata=%s\n"
p);
charc;
intnum=0,alph=0;
while(c=*p)
{
if('
0'
<
=c&
c<
9'
)
num++;
elseif('
a'
z'
||'
A'
Z'
alph++;
serverhasgotdata=%c\n"
c);
p++;
}
serverhasopenedtheclientpipe\n"
Inputfrompidof%dwith%dnumbersand%dalphabets\n"
id,num,alph);
serverhassendtheresulttoclient\n"
return0;
5.运行效果
cgrambler@ubuntu:
~/cgr$gcc-ompipempipe.c
mpipe.c:
Infunction‘main’:
44:
warning:
incompatibleimplicitdeclarationofbuilt-infunction‘strlen’
/tmp/ccikCWwT.o:
Infunction`main'
:
(.text+0x12c):
the`gets'
functionisdangerousandshouldnotbeused.
~/cgr$./mpipe1&
[1]2698
~/cgr$clienthasbeguntorun!
clienthasopenedinput.txt
clientiswaitingfortheservertofireup...
clienthasopenedtheserverpipe
clienthasgotpid=2698.
clienthaswrittenpidtobuf
clienthasappendinputdatatobuf
clientwirttendata=2698abc123topipe.
clienthasopenedtheclientpipe
clienthasgottheresultfromserver
Inputfrompidof2698with3numbersand3alphabets
~/cgr$./mpipe
serveriswaitingfortheclienttofireup...
serverhasopenedtheserverpipe
serverhasgotdata=2698abc123
serverhasgotthepid=2698
serverisgoingtopasetheleftdata=abc123
serverhasgotdata=
serverhasgotdata=a
serverhasgotdata=b
serverhasgotdata=c
serverhasgotdata=1
serverhasgotdata=2
serverhasgotdata=3
serverhasopenedtheclientpipe
serverhassendtheresulttoclie*[
6.实验体会
通过这次实验,我加深了能Linux进程通信,特别是有名管道的理解。
管道是半双工的,数据只能向一个方向流动;
需要双方通信时,需要建立起两个管道;
只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);
单独构成一种独立的文件系统:
管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。
数据的读出和写入:
一个进程向管道中写的内容被管道另一端的进程读出。
写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。
管道应用的一个重大限制是它没有名字,因此,只能用于具有亲缘关系的进程间通信,在有名管道(namedpipe或FIFO)提出后,该限制得到了克服。
FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。
这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信(能够访问该路径的进程以及FIFO的创建进程之间),因此,通过FIFO不相关的进程也能交换数据。
值得注意的是,FIFO严格遵循先进先出(firstinfirstout),对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。
它们不支持诸如lseek()等文件定位操作。