pipewrite(无名)管道特点:
?
read
管道是一个单向信道,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;
? ?
只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程); 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。
?
数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,且每次都是从缓冲区的头部读出数据。(先进先出)
Ex:
#include
fileds[0] 用于读,fileds[1] 用于写。 读/写 —— 0/1
#include
int main() {
int pfds[2]; char buf[30]; pipe(pfds); if(fork()==0) {
close(pfds[0]); sleep(2);
write(pfds[1],\ exit(0); } else {
close(pfds[1]);
read(pfds[0],buf,30); wait(NULL); exit(0); } } note:
1.向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。
2. 写端对读端存在依赖性。只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIGPIPE信号,应用程序可以处理该信号,也可以忽略(默认动作则是应用程序终止)。
3.严格遵循先进先出(first in first out),不支持诸如lseek()等文件定位操作。
有名管道(named pipe或FIFO), 与管道不同之处在于,它与一个路径名关联,
以FIFO的文件形式存在于文件系统中。这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信。
系统调用mkfifo ( ) 创建有名管道
int mkfifo(const char * pathname, mode_t mode)
该函数的第一个参数是一个普通的路径名,也就是创建后FIFO的名字。第二个参数与打开普通文件的open()函数中的mode 参数相同。如果mkfifo的第一个参数是一个已经存在的路径名时,会返回EEXIST错误,所以一般典型的调用代码首先会检查是否返回该错误,如果确实返回该错误,那么只要调用打开FIFO的函数就可以了。生成了有名管道后,就可以使用一般的文件I/O函数如
open、close、read、write等来对它进行操作。
Ex: 写:
#include
#define FIFO_SERVER \
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(\
if(fd==-1)
if(errno==ENXIO)
printf(\
//设置非阻塞标志
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(\ }
else
printf(\ }
读:
#include
#define FIFO_SERVER \
main(int argc,char** argv) {
char r_buf[4096*2]; int fd;
int r_size; int ret_size;
r_size=atoi(argv[1]);
printf(\ memset(r_buf,0,sizeof(r_buf));
fd=open(FIFO_SERVER,O_RDONLY|O_NONBLOCK,0); //fd=open(FIFO_SERVER,O_RDONLY,0);
//在此处可以把读程序编译成两个不同版本:阻塞版本及非阻塞版本
if(fd==-1) {
printf(\ exit(); }
while(1) {
memset(r_buf,0,sizeof(r_buf)); ret_size=read(fd,r_buf,r_size); if(ret_size==-1)
if(errno==EAGAIN)
printf(\
printf(\
sleep(1); }
pause();
unlink(FIFO_SERVER); } note:
管道是最古老的方式,具有通用性。但Pipe_buf有限,通常几百到几千字节。
4.3. 消息队列
系统 V IPC引入了三种进程间通信机制:消息、信号灯、共享内存。内核为每种机制维护一个表,在表中存储所有相关实例,每个表项由一个关键字(用户选择的名字)来标志。
将一个路径名和项目标识 转换为一个关键字: #include
key_t ftok(char* pathname,char proj);
Linux系统维护着一个msgque消息队列链表,其中每个元素指向一个描述消息队列的msqid_ds结构。当创建新的消息队列时,系统将从系统内存中分配一个msqid_ds结构,同时将其插入到数组中。
每个msqid_ds结构包含一个ipc_perm结构和指向已经进入此队列消息的指针,以及有关队列修改时间信息,如上次系统向队列中写入的时间等。 struct kern_ipc_perm {
key_t key; //该键值则唯一对应一个消息队列 uid_t uid; gid_t gid;
uid_t cuid; gid_t cgid; mode_t mode;
unsigned long seq; }
msqid_ds包含两个等待队列:一个为队列写入进程使用而另一个由队列读取进程使用。
每次进程试图向写入队列写入消息时,系统将把其有效用户和组标志符与此队列的ipc_perm结构中的模式进行比较。如果允许写入操作,则把此消息从此进程的地址空间拷贝到msg数据结构中,并放置到此消息队列尾部。由于 Linux严格限制可写入消息的个数和长度,队列中可能容纳不下这个消息。此时,此写入进程将被添加到这个消息队列的等待队列中,同时调用调度管理器选择新进程运行。当有消息从此队列中释放时,该进程将被唤醒。
从队列中读的过程与之类似。进程对这个写入队列的访问权限将被再次检验。读取进程将选择队列中第一个消息(不管是什么类型)或者第一个某特定类型的消息。如果没有消息可以满足此要求,读取进程将被添加 到消息队列的读取等待队列中,然后系统运行调度管理器。当有新消息写入队列时,进程将被唤