Linux进程通信之一:管道与消息队列

一. 无名管道与有名管道


1.无名管道是半双工,就是对于一个管道来讲,只能读,或者写。无名管道只能在相关的,有共同祖先的进程间使用(即一般用户父子进程)。 

int pipe(int fda[2]);
如果成功建立了管道,则会打开两个文件描述符,并把他们的值保存在一个整数数组中。
第一个文件描述符用于读取数据,第二个文件描述符用于写入数据。
管道的两个文件描述符相当于管道的两端,一端只负责读数据,一端只负责写数据
如果出错返回-1,同时设置errno
关闭一个文件描述符用close()函数
关闭一个管道的所有文件描述符等于关闭这个管道(不能读不能写)
示例代码
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

//进程读函数
void read_data(int *);
//进程写函数 
void write_data(int *);

int main(int argc,char *argv[])
{
	int pipes[2],rc;
	pid_t pid;
		
	rc = pipe(pipes);	//创建管道                 
	if(rc == -1){
		perror("\npipes\n");
		exit(1);
	}
		
	pid = fork();	//创建进程 

	switch(pid){
		case -1:
			perror("\nfork\n");
			exit(1);
		case 0:
			read_data(pipes);	//相同的pipes
		default:
			write_data(pipes);	//相同的pipes
	}	
	return 0;
}

//进程读函数
void read_data(int pipes[])
{
	int c,rc;
	
	//由于此函数只负责读,因此将写描述关闭(资源宝贵)
	close(pipes[1]);
	
	//阻塞,等待从管道读取数据
	//int 转为 unsiged char 输出到终端
	while( (rc = read(pipes[0],&c,1)) > 0 ){  		
		putchar(c);       		                       
	}

	exit(0);
}

//进程写函数
void write_data(int pipes[])
{
	int c,rc;

	//关闭读描述字
	close(pipes[0]);                          

	while( (c=getchar()) > 0 ){
		rc = write( pipes[1], &c, 1);	//写入管道
		if( rc == -1 ){
			perror("Parent: write");
			close(pipes[1]);
			exit(1);
		}
	}

	close( pipes[1] );
	exit(0);
}

2.有名管道

命名管道也被称为FIFO文件,是一种特殊的文件。由于linux所有的事物都可以被视为文件,所以对命名管道的使用也就变得与文件操作非常统一。能用于没有亲缘关系和有亲缘关系的进程间的通信。

int mkfifo(const char *filename, mode_t mode);

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

第一个参数 :存放的有名管道的路径,包含了有名管道文件的名字,例如:"/tmp/myfifo"的意思就是,在tmp目录下创建了一个名为myfifo的管道文件。但是,如果你写成"/mnt/hgfs/share/myfifo"就会失败,这个是共享目录,链接到windows所以会失败。

第二个参数:读写权限,为0777(可读可写可执行)

创建成功的管道文件,就可以把他当做是普通文件一样进行操作(但是本质上两者不同)可操作如下:

open()   read()   write()   close()

注意:

1、就是程序不能以O_RDWR(读写)模式打开FIFO文件进行读写操作,而其行为也未明确定义,因为如一个管道以读/写方式打开,就代表,一个进程自己把东西写进管道,再读出来给自己用,这个意义就不大了,管道是用来给两个进程的通信,不是这样用的。网上的说法说“只能以读或写的方式打开“是对的,同时我们通常使用FIFO只是为了单向的数据传递。

2、就是传递给open调用的是FIFO的路径名,而不是正常的文件。(如:const char *fifo_name = "/tmp/my_fifo"; )

3、第二个参数中的选项O_NONBLOCK,选项O_NONBLOCK表示非阻塞,加上这个选项后,表示open调用是非阻塞的,如果没有这个选项,则表示open调用是阻塞的。

二. 消息队列

1.msgget函数原型

msgget(得到消息队列标识符或创建一个消息队列对象)
所需头文件

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

函数说明得到消息队列标识符或创建一个消息队列对象并返回消息队列标识符
函数原型int msgget(key_t key, int msgflg)
参数说明key0(IPC_PRIVATE):会建立新的消息队列
大于0的32位整数:视参数msgflg来确定操作。通常要求此值来源于ftok返回的IPC键值
msgflg0:取消息队列标识符,若不存在则函数会报错
IPC_CREAT:当msgflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的消息队列,则新建一个消息队列;如果存在这样的消息队列,返回此消息队列的标识符
IPC_CREAT|IPC_EXCL:如果内核中不存在键值与key相等的消息队列,则新建一个消息队列;如果存在这样的消息队列则报错
函数返回值成功:返回消息队列的标识符
出错:-1,错误原因存于error中

 

2.msgsnd函数原型

msgsnd (将消息写入到消息队列)
函数说明将msgp消息写入到标识符为msqid的消息队列
函数原型int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
函数传入值msqid消息队列标识符
msgp发送给队列的消息。msgp可以是任何类型的结构体,但第一个字段必须为long类型,即表明此发送消息的类型,msgrcv根据此接收消息。msgp定义的参照格式如下:
 struct s_msg{                        /*msgp定义的参照格式*/
   long type;                            /* 必须大于0,消息类型 */
   char mtext[256];                  /*消息正文,可以是其他任何类型*/
 } msgp;   
msgsz要发送消息的大小,不含消息类型占用的4个字节,即mtext的长度
msgflg0:当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列
IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回
IPC_NOERROR:若发送的消息大于size字节,则把该消息截断,截断部分将被丢弃,且不通知发送进程。
函数返回值成功:0
出错:-1,错误原因存于error中

3.   msgrcv函数原型

msgrcv (从消息队列读取消息)
函数说明从标识符为msqid的消息队列读取消息并存于msgp中,读取后把此消息从消息队列中删除
函数原型

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

函数传入值msqid消息队列标识符
msgp存放消息的结构体,结构体类型要与msgsnd函数发送的类型相同
msgsz要接收消息的大小,不含消息类型占用的4个字节
msgtyp

0:接收第一个消息

>0:接收类型等于msgtyp的第一个消息

<0:接收类型等于或者小于msgtyp绝对值的第一个消息

msgflg

0: 阻塞式接收消息,没有该类型的消息msgrcv函数一直阻塞等待

IPC_NOWAIT:如果没有返回条件的消息调用立即返回,此时错误码为ENOMSG

IPC_EXCEPT:与msgtype配合使用返回队列中第一个类型不为msgtype的消息

IPC_NOERROR:如果队列中满足条件的消息内容大于所请求的size字节,则把该消息截断,截断部分将被丢弃

函数返回值成功:实际读取到的消息数据长度
出错:-1,错误原因存于error中


 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值