搜档网
当前位置:搜档网 › Linux环境进程间通信(一) 管道及有名管

Linux环境进程间通信(一) 管道及有名管

管道及有名管道

在本系列序中作者概述了 linux 进程间通信的几种主要手段。其中管道和有名管道是最早的进程间通信机制之一,管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。认清管道和有名管道的读写规则是在程序中应用它们的关键,本文在详细讨论了管道和有名管道的通信机制的基础上,用实例对其读写规则进行了程序验证,这样做有利于增强读者对读写规则的感性认识,同时也提供了应用范例。

1、管道概述及相关API应用

1.1 管道相关的关键概念

管道是Linux支持的最初Unix IPC形式之一,具有以下特点:

?管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;

?只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);

?单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。

?数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

1.2管道的创建:

#include

int pipe(int fd[2])

该函数创建的管道的两端处于一个进程中间,在实际应用中没有太大意义,因此,一个进程在由pipe()创建管道后,一般再fork一个子进程,然后通过管道实现父子进程间的通信(因此也不难推出,只要两个进程中存在亲缘关系,这里的亲缘关系指的是具有共同的祖先,都可以采用管道方式来进行通信)。

1.3管道的读写规则:

管道两端可分别用描述字fd[0]以及fd[1]来描述,需要注意的是,管道的两端是固定了任务的。即一端只能用于读,由描述字fd[0]表示,称其为管道读端;另一端则只能用于写,由描述字fd[1]来表示,称其为管道写端。如果试图从管道写端读取数据,或者向管道读端写入数据都将导致错误发生。一般文件的I/O函数都可以用于管道,如close、read、writ e等等。

从管道中读取数据:

?如果管道的写端不存在,则认为已经读到了数据的末尾,读函数返回的读出字节数为0;

?当管道的写端存在时,如果请求的字节数目大于PIPE_BUF,则返回管道中现有的数据字节数,如果请求的字节数目不大于PIPE_BUF,则返回管道中现有数据字节数(此时,管道中数据量小于请求的数据量);或者返回请求的字节数(此时,管道中数

据量不小于请求的数据量)。注:(PIPE_BUF在include/linux/limits.h中定义,不同的内核版本可能会有所不同。Posix.1要求PIPE_BUF至少为512字节,red ha t 7.2中为4096)。

关于管道的读规则验证:

向管道中写入数据:

向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。

注:只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIFPIPE信号,应用程序可以处理该信号,也可以忽略(默认动作则是应用程序终止)。

对管道的写规则的验证1:写端对读端存在的依赖性

则输出结果为: Broken pipe,原因就是该管道以及它的所有fork()产物的读端都已经被关闭。如果在父进程中保留读端,即在写完pipe后,再关闭父进程的读端,也会正常写入pi pe,读者可自己验证一下该结论。因此,在向管道写入数据时,至少应该存在某一个进程,其中管道读端没有被关闭,否则就会出现上述错误(管道断裂,进程收到了SIGPIPE信号,默认动作是进程终止)

对管道的写规则的验证2:linux不保证写管道的原子性验证

#include

#include

main(int argc,char**argv)

{

int pipe_fd[2];

pid_t pid;

char r_buf[4096];

char w_buf[4096*2];

int writenum;

int rnum;

memset(r_buf,0,sizeof(r_buf));

if(pipe(pipe_fd)<0)

{

printf("pipe create error\n");

return -1;

}

if((pid=fork())==0)

{

close(pipe_fd[1]);

while(1)

{

sleep(1);

rnum=read(pipe_fd[0],r_buf,1000);

printf("child: readnum is %d\n",rnum);

}

close(pipe_fd[0]);

exit();

}

else if(pid>0)

{

close(pipe_fd[0]);//write

memset(r_buf,0,sizeof(r_buf));

if((writenum=write(pipe_fd[1],w_buf,1024))==-1)

printf("write to pipe error\n");

else

printf("the bytes write to pipe is %d \n", writenum);

writenum=write(pipe_fd[1],w_buf,4096);

close(pipe_fd[1]);

}

}

输出结果:

结论:

写入数目小于4096时写入是非原子的!

如果把父进程中的两次写入字节数都改为5000,则很容易得出下面结论:

写入管道的数据量大于4096字节时,缓冲区的空闲空间将被写入数据(补齐),直到写完所有数据为止,如果没有进程读数据,则一直阻塞。

1.4管道应用实例:

实例一:用于shell

管道可用于输入输出重定向,它将一个命令的输出直接定向到另一个命令的输入。比如,当在某个shell程序(Bourne shell或C shell等)键入who│wc -l后,相应shell程序将创建who以及wc两个进程和这两个进程间的管道。考虑下面的命令行:

$kill -l 运行结果见附一。

$kill -l | grep SIGRTMIN 运行结果如下:

实例二:用于具有亲缘关系的进程间通信

下面例子给出了管道的具体应用,父进程通过管道发送一些命令给子进程,子进程解析命令,并根据命令作相应处理。

#include

main()

{

int pipe_fd[2];

pid_t pid;

char r_buf[4];

char** w_buf[256];

int childexit=0;

int i;

int cmd;

memset(r_buf,0,sizeof(r_buf));

if(pipe(pipe_fd)<0)

{

printf("pipe create error\n");

return -1;

}

if((pid=fork())==0)

//子进程:解析从管道中获取的命令,并作相应的处理

{

printf("\n");

close(pipe_fd[1]);

sleep(2);

while(!childexit)

{

read(pipe_fd[0],r_buf,4);

cmd=atoi(r_buf);

if(cmd==0)

{

printf("child: receive command from parent over\n now child process exit\n");

childexit=1;

}

else if(handle_cmd(cmd)!=0)

return;

sleep(1);

}

close(pipe_fd[0]);

exit();

}

else if(pid>0)

1.5管道的局限性

管道的主要局限性正体现在它的特点上:

?只支持单向数据流;

?只能用于具有亲缘关系的进程之间;

?没有名字;

?管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小);

?管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息(或命令、或记录)等等;

2、有名管道概述及相关API应用

2.1 有名管道相关的关键概念

管道应用的一个重大限制是它没有名字,因此,只能用于具有亲缘关系的进程间通信,在有名管道(named pipe或FIFO)提出后,该限制得到了克服。FIFO不同于管道之处在于它提

供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信(能够访问该路径的进程以及FIFO的创建进程之间),因此,通过FIFO不相关的进程也能交换数据。值得注意的是,FIFO严格遵循先进先出(first in first out),对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。它们不支持诸如lseek()等文件定位操作。

2.2有名管道的创建

该函数的第一个参数是一个普通的路径名,也就是创建后FIFO的名字。第二个参数与打开普通文件的open()函数中的mode 参数相同。如果mkfifo的第一个参数是一个已经存在的路径名时,会返回EEXIST错误,所以一般典型的调用代码首先会检查是否返回该错误,如果确实返回该错误,那么只要调用打开FIFO的函数就可以了。一般文件的I/O函数都可以用于FIFO,如close、read、write等等。

2.3有名管道的打开规则

有名管道比管道多了一个打开操作:open。

FIFO的打开规则:

如果当前打开操作是为读而打开FIFO时,若已经有相应进程为写而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为写而打开该FIFO(当前打开操作设置了阻塞标志);或者,成功返回(当前打开操作没有设置阻塞标志)。

如果当前打开操作是为写而打开FIFO时,如果已经有相应进程为读而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为读而打开该FIFO(当前打开操作设置了阻塞标志);或者,返回ENXIO错误(当前打开操作没有设置阻塞标志)。

对打开规则的验证参见附2。

2.4有名管道的读写规则

从FIFO中读取数据:

约定:如果一个进程为了从FIFO中读取数据而阻塞打开FIFO,那么称该进程内的读操作为设置了阻塞标志的读操作。

?如果有进程写打开FIFO,且当前FIFO内没有数据,则对于设置了阻塞标志的读操作来说,将一直阻塞。对于没有设置阻塞标志读操作来说则返回-1,当前errno值为EAGAIN,提醒以后再试。

?对于设置了阻塞标志的读操作说,造成阻塞的原因有两种:当前FIFO内有数据,但有其它进程在读这些数据;另外就是FIFO内没有数据。解阻塞的原因则是FIFO中有新的数据写入,不论信写入数据量的大小,也不论读操作请求多少数据量。

?读打开的阻塞标志只对本进程第一个读操作施加作用,如果本进程内有多个读操作序列,则在第一个读操作被唤醒并完成读操作后,其它将要执行的读操作将不再阻塞,即使在执行读操作时,FIFO中没有数据也一样(此时,读操作返回0)。

?如果没有进程写打开FIFO,则设置了阻塞标志的读操作会阻塞。

注:如果FIFO中有数据,则设置了阻塞标志的读操作不会因为FIFO中的字节数小于请求读的字节数而阻塞,此时,读操作会返回FIFO中现有的数据量。

向FIFO中写入数据:

约定:如果一个进程为了向FIFO中写入数据而阻塞打开FIFO,那么称该进程内的写操作为设置了阻塞标志的写操作。

对于设置了阻塞标志的写操作:

?当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果此时管道空闲缓冲区不足以容纳要写入的字节数,则进入睡眠,直到当缓冲区中能够容纳要写入的字节数时,才开始进行一次性写操作。

?当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。FIFO缓冲区一有空闲区域,写进程就会试图向管道写入数据,写操作在写完所有请求写的数据后返回。

对于没有设置阻塞标志的写操作:

?当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。在写满所有FIFO空闲缓冲区后,写操作返回。

?当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果当前FIF O空闲缓冲区能够容纳请求写入的字节数,写完后成功返回;如果当前FIFO空闲缓冲区不能够容纳请求写入的字节数,则返回EAGAIN错误,提醒以后再写;

对FIFO读写规则的验证:

下面提供了两个对FIFO的读写程序,适当调节程序中的很少地方或者程序的命令行参数就可以对各种FIFO读写规则进行验证。

程序1:写FIFO的程序

#include

#include

#include

#define FIFO_SERVER "/tmp/fifoserver"

main(int argc,char** argv)

//参数为即将写入的字节数

{

int fd;

char w_buf[4096*2];

int real_wnum;

memset(w_buf,0,4096*2);

if((mkfifo(FIFO_SERVER,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST)) printf("cannot create fifoserver\n");

if(fd==-1)

if(errno==ENXIO)

printf("open error; no reading process\n");

fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);

//设置非阻塞标志

//fd=open(FIFO_SERVER,O_WRONLY,0);

//设置阻塞标志

real_wnum=write(fd,w_buf,2048);

if(real_wnum==-1)

{

if(errno==EAGAIN)

printf("write to fifo error; try later\n");

}

else

printf("real write num is %d\n",real_wnum);

real_wnum=write(fd,w_buf,5000);

//5000用于测试写入字节大于4096时的非原子性

//real_wnum=write(fd,w_buf,4096);

//4096用于测试写入字节不大于4096时的原子性

if(real_wnum==-1)

if(errno==EAGAIN)

printf("try later\n");

}

程序2:与程序1一起测试写FIFO的规则,第一个命令行参数是请求从FIFO读出的字节数

#include

程序应用说明:

把读程序编译成两个不同版本:

?阻塞读版本:br

?以及非阻塞读版本nbr

把写程序编译成两个四个版本:

?非阻塞且请求写的字节数大于PIPE_BUF版本:nbwg

?非阻塞且请求写的字节数不大于PIPE_BUF版本:版本nbw

?阻塞且请求写的字节数大于PIPE_BUF版本:bwg

?阻塞且请求写的字节数不大于PIPE_BUF版本:版本bw

下面将使用br、nbr、w代替相应程序中的阻塞读、非阻塞读

验证阻塞写操作:

1.当请求写入的数据量大于PIPE_BUF时的非原子性:

o nbr 1000

o bwg

2.当请求写入的数据量不大于PIPE_BUF时的原子性:

o nbr 1000

o bw

验证非阻塞写操作:

1.当请求写入的数据量大于PIPE_BUF时的非原子性:

o nbr 1000

o nbwg

2.请求写入的数据量不大于PIPE_BUF时的原子性:

o nbr 1000

o nbw

不管写打开的阻塞标志是否设置,在请求写入的字节数大于4096时,都不保证写入的原子性。但二者有本质区别:

对于阻塞写来说,写操作在写满FIFO的空闲区域后,会一直等待,直到写完所有数据为止,请求写入的数据最终都会写入FIFO;

而非阻塞写则在写满FIFO的空闲区域后,就返回(实际写入的字节数),所以有些数据最终不能够写入。

对于读操作的验证则比较简单,不再讨论。

2.5有名管道应用实例

在验证了相应的读写规则后,应用实例似乎就没有必要了。

小结:

管道常用于两个方面:(1)在shell中时常会用到管道(作为输入输入的重定向),在这种应用方式下,管道的创建对于用户来说是透明的;(2)用于具有亲缘关系的进程间通信,用户自己创建管道,并完成读写操作。

FIFO可以说是管道的推广,克服了管道无名字的限制,使得无亲缘关系的进程同样可以采用先进先出的通信机制进行通信。

管道和FIFO的数据是字节流,应用程序之间必须事先确定特定的传输"协议",采用传播具有特定意义的消息。

要灵活应用管道及FIFO,理解它们的读写规则是关键。

附1:kill -l 的运行结果,显示了当前系统支持的所有信号:

除了在此处用来说明管道应用外,接下来的专题还要对这些信号分类讨论。

附2:对FIFO打开规则的验证(主要验证写打开对读打开的依赖性)

int handle_client(char*);

main(int argc,char** argv)

{

int r_rd;

int w_fd;

pid_t pid;

if((mkfifo(FIFO_SERVER,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST)) printf("cannot create fifoserver\n");

handle_client(FIFO_SERVER);

}

int handle_client(char* arg)

{

int ret;

ret=w_open(arg);

switch(ret)

{

case 0:

{

printf("open %s error\n",arg);

printf("no process has the fifo open for reading\n");

return -1;

}

case -1:

{

printf("something wrong with open the fifo except for ENXIO");

return -1;

}

case 1:

{

printf("open server ok\n");

return 1;

}

default:

{

printf("w_no_r return ????\n");

return 0;

}

}

unlink(FIFO_SERVER);

}

参考文献:

1.UNIX网络编程第二卷:进程间通信,作者:W.Richard Stevens,译者:杨继张,

清华大学出版社。丰富的UNIX进程间通信实例及分析,对Linux环境下的程序开发有极大的启发意义。

2.linux内核源代码情景分析(上、下),毛德操、胡希明著,浙江大学出版社,当

要验证某个结论、想法时,最好的参考资料;

3.UNIX环境高级编程,作者:W.Richard Stevens,译者:尤晋元等,机械工业出版

社。具有丰富的编程实例,以及关键函数伴随Unix的发展历程。

4.https://www.sodocs.net/doc/de12115996.html,.tw/CLDP/gb/Secure-Programs-HOWTO/x346.html点明lin

ux下sigaction的实现基础,linux源码../kernel/signal.c更说明了问题;

5.pipe手册,最直接而可靠的参考资料

6.fifo手册,最直接而可靠的参考资料

关于作者

郑彦兴,男,现攻读国防科大计算机学院网络方向博士学位。您可以通过电子邮件mlinux @https://www.sodocs.net/doc/de12115996.html,和他联系。

Linux系统编程实验六进程间通信

实验六:进程间通信 实验目的: 学会进程间通信方式:无名管道,有名管道,信号,消息队列, 实验要求: (一)在父进程中创建一无名管道,并创建子进程来读该管道,父进程来写该管道(二)在进程中为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;

进程同步与通信作业习题与答案

第三章 一.选择题(50题) 1.以下_B__操作系统中的技术是用来解决进程同步的。 A.管道 B.管程 C.通道 2.以下_B__不是操作系统的进程通信手段。 A.管道 B.原语 C.套接字 D.文件映射 3.如果有3个进程共享同一程序段,而且每次最多允许两个进程进入该程序段,则信号量的初值应设置为_B__。 4.设有4个进程共享一个资源,如果每次只允许一个进程使用该资源,则用P、V操作管理时信号量S的可能取值是_C__。 ,2,1,0,-1 ,1,0,-1,-2 C. 1,0,-1,-2,-3 ,3,2,1,0 5.下面有关进程的描述,是正确的__A__。 A.进程执行的相对速度不能由进程自己来控制 B.进程利用信号量的P、V 操作可以交换大量的信息 C.并发进程在访问共享资源时,不可能出现与时间有关的错误 、V操作不是原语操作 6.信号灯可以用来实现进程之间的_B__。 A.调度 B.同步与互斥 C.同步 D.互斥 7.对于两个并发进程都想进入临界区,设互斥信号量为S,若某时S=0,表示_B__。 A.没有进程进入临界区 B.有1个进程进入了临界区 C. 有2个进程进入了临界区 D. 有1个进程进入了临界区并且另一个进程正等待进入 8. 信箱通信是一种_B__方式 A.直接通信 B.间接通信 C.低级通信 D.信号量 9.以下关于临界区的说法,是正确的_C__。

A.对于临界区,最重要的是判断哪个进程先进入 B.若进程A已进入临界区,而进程B的优先级高于进程A,则进程B可以 打断进程A而自己进入临界区 C. 信号量的初值非负,在其上只能做PV操作 D.两个互斥进程在临界区内,对共享变量的操作是相同的 10. 并发是指_C__。 A.可平行执行的进程 B.可先后执行的进程 C.可同时执行的进程 D.不可中断的进程 11. 临界区是_C__。 A.一个缓冲区 B.一段数据区 C.一段程序 D.栈 12.进程在处理机上执行,它们的关系是_C__。 A.进程之间无关,系统是封闭的 B.进程之间相互依赖相互制约 C.进程之间可能有关,也可能无关 D.以上都不对 13. 在消息缓冲通信中,消息队列是一种__A__资源。 A.临界 B.共享 C.永久 D.可剥夺 14. 以下关于P、V操作的描述正确的是__D_。 A.机器指令 B. 系统调用 C.高级通信原语 D.低级通信原语 15.当对信号量进行V源语操作之后,_C__。 A.当S<0,进程继续执行 B.当S>0,要唤醒一个就绪进程 C. 当S<= 0,要唤醒一个阻塞进程 D. 当S<=0,要唤醒一个就绪 16.对临界区的正确论述是__D_。 A.临界区是指进程中用于实现进程互斥的那段代码 B. 临界区是指进程中用于实现进程同步的那段代码 C. 临界区是指进程中用于实现进程通信的那段代码 D. 临界区是指进程中访问临界资源的那段代码 17. __A__不是进程之间的通信方式。 A.过程调用 B.消息传递 C.共享存储器 D.信箱通信 18. 同步是指进程之间逻辑上的__A__关系。

实验9 进程间管道通信实验

试验9 进程间管道通信实验 学生姓名:李亚军学号:6100412196 专业班级:卓越计科121班 1.实验目的 通过编写有名管道多路通信实验,读者可进一步掌握管道的创建、读写等操作,同时,也复习使用select()函数实现管道的通信。 2.实验内容 (1)画出流程图。 该实验流程图如图8.9所示。 图8.9 8.6.1实验流程图 (2)编写代码。 该实验源代码如下所示。 /* pipe_select.c*/ #include #include #include #include #include #include #include #define FIFO1"in1" #define FIFO2"in2" #define MAX_BUFFER_SIZE 1024 /* 缓冲区大小*/ #define IN_FILES 3 /* 多路复用输入文件数目*/ #define TIME_DELAY 60 /* 超时值秒数*/

#define MAX(a, b)((a > b)?(a):(b)) int main(void) { int fds[IN_FILES]; char buf[MAX_BUFFER_SIZE]; int i, res, real_read, maxfd; struct timeval tv; fd_set inset,tmp_inset; fds[0] = 0; /* 创建两个有名管道*/ if (access(FIFO1, F_OK) == -1) { if ((mkfifo(FIFO1, 0666) < 0) && (errno != EEXIST)) { printf("Cannot create fifo file\n"); exit(1); } } if (access(FIFO2, F_OK) == -1) { if ((mkfifo(FIFO2, 0666) < 0) && (errno != EEXIST)) { printf("Cannot create fifo file\n"); exit(1); } } /* 以只读非阻塞方式打开两个管道文件*/ if((fds[1] = open (FIFO1, O_RDONL Y|O_NONBLOCK)) < 0) { printf("Open in1 error\n"); return 1; } if((fds[2] = open (FIFO2, O_RDONL Y|O_NONBLOCK)) < 0) { printf("Open in2 error\n"); return 1; } /*取出两个文件描述符中的较大者*/ maxfd = MAX(MAX(fds[0], fds[1]), fds[2]);

Linux进程间通信(2)实验报告

实验六: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 #include #include int shmget(key_t key, int size, int flag); 当共享内存创建后,其余进程可以调用shmat()将其连接到自身的地址空间中。 void *shmat(int shmid, void *addr, int flag); shmid为shmget函数返回的共享存储标识符,addr和flag参数决定了以什么方式来确定连接的地址,函数的返回值即是该进程数据段所连接的实际地

进程间通信方式比较

进程间的通信方式: 1.管道(pipe)及有名管道(named pipe): 管道可用于具有亲缘关系进程间的通信,有名管道除了具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。 2.信号(signal): 信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上可以说是一致得。 3.消息队列(message queue): 消息队列是消息的链接表,它克服了上两种通信方式中信号量有限的缺点,具有写权限得进程可以按照一定得规则向消息队列中添加新信息;对消息队列有读权限得进程则可以从消息队列中读取信息。 消息缓冲通信技术是由Hansen首先提出的,其基本思想是:根据”生产者-消费者”原理,利用内存中公用消息缓冲区实现进程之间的信息交换. 内存中开辟了若干消息缓冲区,用以存放消息.每当一个进程向另一个进程发送消息时,便申请一个消息缓冲区,并把已准备好的消息送到缓冲区,然后把该消息缓冲区插入到接收进程的消息队列中,最后通知接收进程.接收进程收到发送里程发来的通知后,从本进程的消息队列中摘下一消息缓冲区,取出所需的信息,然后把消息缓冲区不定期给系统.系统负责管理公用消息缓冲区以及消息的传递. 一个进程可以给若干个进程发送消息,反之,一个进程可以接收不同进程发来的消息.显然,进程中关于消息队列的操作是临界区.当发送进程正往接收进程的消息队列中添加一条消息时,接收进程不能同时从该消息队列中到出消息:反之也一样. 消息缓冲区通信机制包含以下列内容:

(1) 消息缓冲区,这是一个由以下几项组成的数据结构: 1、消息长度 2、消息正文 3、发送者 4、消息队列指针 (2)消息队列首指针m-q,一般保存在PCB中。 (1)互斥信号量m-mutex,初值为1,用于互斥访问消息队列,在PCB中设置。 (2)同步信号量m-syn,初值为0,用于消息计数,在PCB中设置。(3)发送消息原语send (4)接收消息原语receive(a) 4.共享内存(shared memory): 可以说这是最有用的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。 这种通信模式需要解决两个问题:第一个问题是怎样提供共享内存;第二个是公共内存的互斥关系则是程序开发人员的责任。 5.信号量(semaphore): 主要作为进程之间及同一种进程的不同线程之间得同步和互斥手段。 6.套接字(socket); 这是一种更为一般得进程间通信机制,它可用于网络中不同机器之间的进程间通信,应用非常广泛。 https://www.sodocs.net/doc/de12115996.html,/eroswang/archive/2007/09/04/1772350.aspx linux下的进程间通信-详解

linux进程间通讯的几种方式的特点和优缺点

1. # 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。 # 有名管道(named pipe) :有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。 # 信号量( semophore ) :信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。 # 消息队列( message queue ) :消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。 # 信号( sinal ) :信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。#共享内存( shared memory):共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。 # 套接字( socket ) :套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。 管道的主要局限性正体现在它的特点上: 只支持单向数据流; 只能用于具有亲缘关系的进程之间; 没有名字; 管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小);管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息(或命令、或记录)等等; 2. 用于进程间通讯(IPC)的四种不同技术: 1. 消息传递(管道,FIFO,posix和system v消息队列) 2. 同步(互斥锁,条件变量,读写锁,文件和记录锁,Posix和System V信号灯) 3. 共享内存区(匿名共享内存区,有名Posix共享内存区,有名System V共享内存区) 4. 过程调用(Solaris门,Sun RPC) 消息队列和过程调用往往单独使用,也就是说它们通常提供了自己的同步机制.相反,共享内存区

Linux进程通信实验报告

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 #include #include #include void waiting(),stop(),alarming(); int wait_mark; main() { int p1,p2; if(p1=fork()) /*创建子进程p1*/ { if(p2=fork()) /*创建子进程p2*/ { //父进程 wait_mark=1; signal(SIGINT,stop); /*接收到^c信号,转stop*/

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()

进程的管道通信

计算机操作系统实验第六次实验报告 学院:计算机科学与信息学院专业:通信工程班级:081姓名学号 实验 组 实验时间2010年11月17日指导教师成绩 实验项目名称进程的管道通信实 验目的 1、了解什么是管道; 2、熟悉UNIX/LINUX支持的管道通信方式。 实 验要求 1、了解管道的概念和管道的类型; 2、熟悉UNIX/LINUX支持的管道通信方式。 实 验 原 理 在管道通信时系统会调用:pipe( )建立一无名管道;read( );write( ) 。 实 验 仪 器 PC机或工作站一台; RedHat9.0操作系统;

实验步骤一、什么是管道 UNIX系统在OS的发展上,最重要的贡献之一便是该系统首创了管道(pipe)。这也是UNIX系统的一大特色。 所谓管道,是指能够连接一个写进程和一个读进程的、并允许它们以生产者—消费者方式进行通信的一个共享文件,又称为pipe文件。由写进程从管道的写入端(句柄1)将数据写入管道,而读进程则从管道的读出端(句柄0)读出数据。 句柄fd[0] 句柄fd[1] 读出端 写入端 二、管道的类型: 1、有名管道 一个可以在文件系统中长期存在的、具有路径名的文件。用系统调用mknod( )建立。它克服无名管道使用上的局限性,可让更多的进程也能利用管道进行通信。因而其它进程可以知道它的存在,并能利用路径名来访问该文件。对有名管道的访问方式与访问其他文件一样,需先用open( )打开。 2、无名管道 一个临时文件。利用pipe( )建立起来的无名文件(无路径名)。只用该系统调用所返回的文件描述符来标识该文件,故只有调用pipe( )的进程及其子孙进程才能识别此文件描述符,才能利用该文件(管道)进行通信。当这些进程不再使用此管道时,核心收回其索引结点。 二种管道的读写方式是相同的,本文只讲无名管道。 3、pipe文件的建立 分配磁盘和内存索引结点、为读进程分配文件表项、为写进程分配文件表项、分配用户文件描述符 4、读/写进程互斥 内核为地址设置一个读指针和一个写指针,按先进先出顺序读、写。 为使读、写进程互斥地访问pipe文件,需使各进程互斥地访问pipe文件索引结点中的直接地址项。因此,每次进程在访问pipe文件前,都需检查该索引文件是否已被上锁。若是,进程便睡眠等待,否则,将其上锁,进行读/写。操作结束后解锁,并唤醒因该索引结点上锁而睡眠的进程。 三、所涉及的系统调用 1、pipe( ) 建立一无名管道。 系统调用格式 pipe(filedes) 参数定义 int pipe(filedes); int filedes[2]; 其中,filedes[1]是写入端,filedes[0]是读出端。 该函数使用头文件如下: #include #inlcude #include 2、read( ) 系统调用格式

实验4 进程的管道通信

实验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]是管道的读端口,用

进程间的通信

实验三进程间的通信 【实验类型】 综合性实验 【目的要求】 学习如何利用管道机制、消息缓冲队列、共享存储区机制进行进程间的通讯,并加深对上述通信机制的理解。 【内容提要】 1、了解系统调用pipe()、msgget()、msgsnd()、msgrcv ()、msgctl()、shmget()、shmat()、shmdt()、shmctl()的功能和实现过程。 2、编写一段程序,使其用管道来实现父子进程之间的进程通讯。子进程向父进程发送自己的进程标识符,以及字符串“is sending a message to parent!”。父进程则通过管道读出子进程发来的消息,将消息显示在屏幕上,然后终止。 3、编写一段程序,使用系统调用fork()来创建两个子进程CLIENT进程和SERVER进程,使其用消息缓冲队列来实现CLIENT进程和SERVER进程之间的通信。SERVER端建立一个Key为75的消息队列,等待其他进程发来的消息。当遇到类型为1的消息,则作为结束信号,取消该队列,并退出SERVER。SERVER每接收到一条消息后显示一句“(server) received”。CLIENT端使用Key为75的消息队列,先后发送类型从10到1的消息,然后退出。最后的一个消息,即是SERVER端需要的结束信号。CLIENT每发送一条消息后显示一句“(client) sent”。父进程在SERVER和CLIENT均退出后结束。 4、编写一个与3具有类似功能的程序,使其用共享存储区来实现两个进程之间的通讯。 【主要仪器设备】 每人一台计算机,硬件要求:CPU PII以上,64M内存,1OOM硬盘空间即可;软件要求: Linux操作系统。

Linux下的进程间通信-详解

Linux下的进程间通信-详解 详细的讲述进程间通信在这里绝对是不可能的事情,而且笔者很难有信心说自己对这一部分内容的认识达到了什么样的地步,所以在这一节的开头首先向大家推荐著 名作者Richard Stevens的著名作品:《Advanced Programming in the UNIX Environment》,它的中文译本《UNIX环境高级编程》已有机械工业出版社出版,原文精彩,译文同样地道,如果你的确对在Linux下编程有浓 厚的兴趣,那么赶紧将这本书摆到你的书桌上或计算机旁边来。说这么多实在是难抑心中的景仰之情,言归正传,在这一节里,我们将介绍进程间通信最最初步和最 最简单的一些知识和概念。 首先,进程间通信至少可以通过传送打开文件来实现,不同的进程通过一个或多个文件来传递信息,事实上,在很多应用系统里,都使用了这种方法。但一般说来, 进程间通信(IPC:InterProcess Communication)不包括这种似乎比较低级的通信方法。Unix系统中实现进程间通信的方法很多,而且不幸的是,极少方法能在所有的Unix系 统中进行移植(唯一一种是半双工的管道,这也是最原始的一种通信方式)。而Linux作为一种新兴的操作系统,几乎支持所有的Unix下常用的进程间通信 方法:管道、消息队列、共享内存、信号量、套接口等等。下面我们将逐一介绍。 2.3.1 管道 管道是进程间通信中最古老的方式,它包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者用于运行于同一台机器上的任意两个进程间的通信。 无名管道由pipe()函数创建: #include int pipe(int filedis[2]); 参数filedis返回两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开。filedes[1]的输出是filedes[0]的输入。下面的例子示范了如何在父进程和子进程间实现通信。 #define INPUT 0 #define OUTPUT 1 void main() { int file_descriptors[2]; /*定义子进程号 */ pid_t pid; char buf[256]; int returned_count; /*创建无名管道*/ pipe(file_descriptors); /*创建子进程*/ if((pid = fork()) == -1) { printf("Error in fork\n"); exit(1); } /*执行子进程*/ if(pid == 0) { printf("in the spawned (child) process...\n"); /*子进程向父进程写数据,关闭管道的读端*/ close(file_descriptors[INPUT]); write(file_descriptors[OUTPUT], "test data", strlen("test data"));

04--Linux系统编程-进程间通信

IPC方法 Linux环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到,所以进程和进程之间不能相互访问,要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)。 在进程间完成数据传递需要借助操作系统提供特殊的方法,如:文件、管道、信号、共享内存、消息队列、套接字、命名管道等。随着计算机的蓬勃发展,一些方法由于自身设计缺陷被淘汰或者弃用。现今常用的进程间通信方式有: ①管道(使用最简单) ②信号(开销最小) ③共享映射区(无血缘关系) ④本地套接字(最稳定) 管道 管道的概念: 管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。有如下特质: 1. 其本质是一个伪文件(实为内核缓冲区) 2.由两个文件描述符引用,一个表示读端,一个表示写端。 3. 规定数据从管道的写端流入管道,从读端流出。 管道的原理: 管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。 管道的局限性: ①数据自己读不能自己写。 ②数据一旦被读走,便不在管道中存在,不可反复读取。 ③由于管道采用半双工通信方式。因此,数据只能在一个方向上流动。 ④只能在有公共祖先的进程间使用管道。

常见的通信方式有,单工通信、半双工通信、全双工通信。 pipe函数 创建管道 int pipe(int pipefd[2]); 成功:0;失败:-1,设置errno 函数调用成功返回r/w两个文件描述符。无需open,但需手动close。规定:fd[0] →r;fd[1] →w,就像0对应标准输入,1对应标准输出一样。向管道文件读写数据其实是在读写内核缓冲区。 管道创建成功以后,创建该管道的进程(父进程)同时掌握着管道的读端和写端。如何实现父子进程间通信呢?通常可以采用如下步骤: 1.父进程调用pipe函数创建管道,得到两个文件描述符fd[0]、fd[1]指向管道的读端和写端。 2.父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。 3.父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信。 练习:父子进程使用管道通信,父写入字符串,子进程读出并,打印到屏幕。【pipe.c】 思考:为甚么,程序中没有使用sleep函数,但依然能保证子进程运行时一定会读到数据呢? 管道的读写行为 使用管道需要注意以下4种特殊情况(假设都是阻塞I/O操作,没有设置O_NONBLOCK标志): 1.如果所有指向管道写端的文件描述符都关闭了(管道写端引用计数为0),而仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样。

进程间通讯机制

进程间通讯机制 进程在核心的协调下进行相互间的通讯。Linux支持大量进程间通讯(IPC) 机制。除了信号和管道外,Linux 还支持Unix系统V中的IPC机制。 信号 信号是Unix系统中的最古老的进程间通讯方式。它们用来向一个或多个进程发送异步事件信号。信号可以从键盘中断中产生,另外进程对虚拟内存的非法存取等系统错误环境下也会有信号产生。信号还被shell程序用来向其子进程发送任务控制命令。 系统中有一组被详细定义的信号类型,这些信号可以由核心或者系统中其它具有适当权限的进程产生。使用kill命令(kill -l)可以列出系统中所有已经定义的信号。在我的系统(Intel系统)上运行结果如下: 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGIOT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 当我在Alpha AXP中运行此命令时,得到了不同的信号个数。除了两个信号外,进程可以忽略这些信号中的绝大部分。其一是引起进程终止执行的SIGSTOP信号,另一个是引起进程退出的SIGKILL信号。至于其它信号,进程可以选择处理它们的具体方式。进程可以阻塞信号,如若不阻塞,则可以在自行处理此信号和将其转交核心处理之间作出选择。如果由核心来处理此信号,它将使用对应此信号的缺省处理方法。比如当进程接收到SIGFPE(浮点数异常)时,核心的缺省操作是引起core dump和进程的退出。信号没有固有的相对优先级。如果在同一时刻对于一个进程产生了两个信号,则它们将可能以任意顺序到达进程并进行处理。

Linux进程间通信程序设计

计算机与信息技术学院设计性实验报告 一、实验目的 (1)理解进程概念; (2)理解并掌握多进程开发模式; (3)理解并掌握Linux平台进程间数据的传送方法。 二、总体设计 (1)实验内容:编写程序实现进程的管道通信。用系统调用pipe( )建立一管道,创建两个二个子进程P1和P2分别向管道各写一句话: Message from child P1! Message from child P2! 父进程从管道中读出二个来自子进程的信息并显示。 (2)设计原理: 所谓管道,是指能够连接一个写进程和一个读进程、并允许它们进行通信的一个共享文件,又称为pipe文件。由写进程从管道的写入端(句柄1)将数据写入管道,而读进程则从管道的读出端(句柄0)读出数据。 通过管道的信息流 三、实验步骤: #include

#include #include #include # include #define BUFSIZE 100 int pid1,pid2; int main() { int fd[2]; char buf_out [BUFSIZE], buf_in [BUFSIZE]; if (pipe(fd) < 0) { printf("pipe error\n"); exit(1); } /*创建一个管道*/ if ((pid1 = fork()) < 0) /*创建子进程1*/ { printf("fork1 failure\n"); exit(1); } else if (pid1 == 0) { lockf(fd[1],1,0); strcpy(buf_out," Message from child P1!"); /*把串放入数组buf_out中*/ write(fd[1],buf_out,BUFSIZE); /*向管道写长为BUFSIZE字节的串*/ sleep(5); /*自我阻塞5秒*/ lockf(fd[1],0,0); exit(0); } else { while((pid2=fork( ))==-1); /*创建子进程2*/ if(pid2==0) { lockf(fd[1],1,0); /*互斥*/ sprintf(buf_out,"Message from child %d!",getpid()); write(fd[1], buf_out,BUFSIZE); sleep(5); lockf(fd[1],0,0); exit(0); } else { wait(0); /*同步*/ read(fd[0], buf_in,BUFSIZE); /*从管道中读长为BUFSIZE字节的串*/ printf("%s\n", buf_in); wait(0); read(fd[0], buf_in,BUFSIZE); printf("%s\n", buf_in); exit(0); } }

实验六 信号和pipe管道通信

第四章 Linux 进程通信(6学时) 实验六信号和pipe管道通信 实验目的 1.了解和掌握Linux信号的原理 2.掌握Linux信号相关函数的使用 实验内容 1.运行以下程序,分析程序的运行结果及运行过程。 #include #include #include static void sig_quit(int); int main(void) { sigset_t newmask, oldmask, pendingmask; //设置对信号SIGQUIT的处理函数 if (signal(SIGQUIT, sig_quit) == SIG_ERR) { fprintf(stderr, "can't catch SIGQUIT\n"); exit(1); } //设置一个空的信号集 sigemptyset(&newmask); sigaddset(&newmask, SIGQUIT); // 在这个信号集中增加SIGQUIT信号 //在当前进程中增加newmask信号集作为屏蔽信号集,oldmask返回当前进程的信号集 if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) { fprintf(stderr, "SIG_BLOCK error\n"); exit(1); } for(i=1;i<6;i++){ printf("SIGQUIT信号现在被阻塞!\n");sleep(2);} //恢复进程的信号集 if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) { fprintf(stderr, "SIG_SETMASK error\n"); exit(1); } printf("SIGQUIT unblocked\n");

相关主题