Linux的进程编程-之二-进程间通信(管道)

来源:互联网 发布:看肚脐知健康 编辑:程序博客网 时间:2024/05/19 03:46

1.1         管道

管道可用于具有亲缘关系(父子进程或者兄弟进程)之间的通信,管道具有以下特点:

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

2.管道对于两端的进程而言,就是一个文件,但它不属于文件系统,只存在于内存中;

3.每次写入的内容都添加在管道缓冲区的末尾,每次都是从管道缓冲区的头部读出数据;

4.管道没有名字;

5.管道缓冲区是有限的(PIPE_BUF);

6.文件的I/O函数都可以用于管道,如open( )、close( )、read( )、write( ) 、fcntl( )。

1.1.1       管道的创建

#include<unistd.h>

intpipe( int fd[2] )

fd[0]是管道读端;fd[1]是管道写端。父进程用pipe( )创建管道后,一般再fork()出一个子进程,然后通过管道实现父子进程间的通信。

成功返回0,失败返回-1。

1.1.2       有名管道的创建

#include<sys/types.h>

#include<sys/stat.h>

int mkfifo( const char * name, mode_t mode )

参数name指定与FIFO相关联的名字;成功返回0,失败返回-1。

有名管道(named pipe或FIFO)以FIFO文件的形式存在于文件系统中,因此即使没有亲缘关系的进程也能通过FIFO进行通信。

1.1.3       管道的打开

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

intopen( const char *name, int flags )

int open( const char *name, int flags, mode_t mode )

参数name指定了文件名(包含路径),当前进程必须能够访问该文件。

成功返回文件描述符fd,失败返回-1。

参数flags可以取:

O_RDONLY     O_WRONLY      O_RDWR(三选一)

O_CREAT

单独使用O_CREAT,如果name指定的文件不存在,就创建该文件,并返回其描述符;如果name指定的文件已经存在,就返回该文件的描述符。如果指定了O_CREAT,需要使用open( )的第二种形式,其中参数mode指定了新创建文件的访问权限。

用户

 

组成员

 

其他成员

 

S_IXUSR

100

S_IXGRP

010

S_IXOTH

001

S_IWUSR

200

S_IWGRP

020

S_IWOTH

002

S_IRUSR

400

S_IRGRP

040

S_IROTH

004

S_IRWXU

700

S_IRWXG

070

S_IRWXO

007

O_EXCL

单独使用O_EXCL是没有意义的,如果O_EXCL和O_CREAT一起使用,当name指定的文件已经存在时,就失败返回。

O_APPEND      :确保每次对文件进行写操作之前,文件偏移指针都会被重新定位到文件末尾

O_NOCTTY      :如果打开的文件是终端设备,确保该终端设备不会成为当前进程的控制终端

O_TRUNC        :清空文件内容,如果同时满足以下条件:文件已经存在&&文件是普通文件&&文件能够成功open&&open时指定支持写操作(O_WRONLYor O_RDWR)

O_NONBLOCK:

1.如果open的是FIFO,而且open时指定支持单一读写操作(O_RDONLYor O_WRONLY):

1.1.指定O_NONBLOCK:

单一读打开的open,立即返回;

单一写打开的open,如果还没有其它进程读打开该FIFO,返回-1。

1.2.未指定O_NONBLOCK:

单一读打开的open会挂起当前进程,直到有其它进程写打开FIFO;

单一写打开的open会挂起当前进程,直到有其它进程读打开FIFO。

2.如果open的文件或设备支持非阻塞打开:

2.1.指定O_NONBLOCK:open会立即返回,不用等待打开的文件或设备ready or available

2.2.未指定O_NONBLOCK:open会挂起当前进程,直到打开的文件或设备ready or available

1.1.4       管道的关闭

#include<unistd.h>

intclose( int fd )

成功返回0,失败返回-1。

1.1.5       从管道中读取数据

1.1.5.1     read( )

#include<unistd.h>

ssize_tread( int fd, void * buf, size_t nbyte )

从fd指定的文件中读取nbyte字节到buf中,返回实际读取到的字节数(可能小于nbyte)。

返回0表明文件为空(读到EOF),读取失败返回-1。

当试图从一个空的FIFO中读取数据时:

1.如果没有其它进程写打开FIFO,read( )返回0;

2.如果有其它进程写打开FIFO:

2.1.当前进程读打开时指定了O_NONBLOCK,read( )返回-1;

2.2.当前进程读打开时未指定O_NONBLOCK,read( )挂起当前进程,直到有数据写入该FIFO,或者是所有写打开该FIFO的进程都关闭了它。

当试图从一个支持非阻塞读操作,但却没有数据available的文件或设备中读取数据时:

1.如果在读打开时指定了O_NONBLOCK,read( )返回-1;

2.如果在读打开时未指定O_NONBLOCK,read( )将挂起当前进程,直到有数据写入。

1.1.5.2     readv

#include<sys/uio.h>

ssize_treadv( int fd, const struct iovec *iov, int iovcnt )

readv( )将读取出来的数据依次存放到一系列内存单元中(iov[0], iov[1], ..., iov[iovcnt-1])。

struct iovec

{

void *    iov_base;      // baseaddress of the memory

size_t     iov_len;        // size ofthe memory

}

1.1.6       向管道中写入数据

1.1.6.1     write( )

#include<unistd.h>

ssize_twrite( int fd, const void * buf, size_t nbyte )

向fd指定的文件中写入nbyte字节的数据,数据来源为buf,返回实际写入的字节数(可能小于nbyte)。写入失败返回-1。

当向FIFO写入时:

1.如果写打开时未指定O_NONBLOCK,write挂起当前进程,直到全部nbyte写入完毕;

2.如果写打开时指定了O_NONBLOCK,write执行以下写入操作后,立即返回:

2.1.nbyte≤PIPE_BUF,但FIFO中的剩余空间不足,write不写入任何数据,立即返回-1;

2.2.nbyte>PIPE_BUF,write会尽可能多的向FIFO中写入数据,立即返回写入的字节数;如果之前存在于FIFO中的数据被读取了,write最终能够向FIFO中写入PIPE_BUF个字节;

2.3.nbyte>PIPE_BUF,但FIFO中确实一个字节也写不进去了,立即返回-1。

所以总的来看,只要nbyte≤PIPE_BUF,不管有没有指定O_NONBLOCK,都可以保证FIFO写入操作的原子性。

当试图向一个支持非阻塞写操作的文件或设备中写入数据时,如果该文件或设备不能马上接收写入的数据,则:

2.如果在写打开时未指定O_NONBLOCK,write( )将挂起当前进程,直到数据能够写入。

1.如果在写打开时指定了O_NONBLOCK,write( )会尽可能多的向文件或设备中写入数据,并返回成功写入的字节数;如果确实一个字节也写不进去,write( )就返回-1。

1.1.6.2     writev( )

#include<sys/uio.h>

ssize_twritev( int fd, const struct iovec *iov, int iovcnt )

writev( )将需要写入的数据依次存放到一系列内存单元中(iov[0], iov[1], ..., iov[iovcnt-1])。

如果所有内存单元的长度之和大于SSIZE_MAX,writev失败返回-1,不进行任何数据写入。