对于本地进程间通信,普通管道提供了相关进程(都有想相同的祖先)之间通信的一种方法,但是普通管道存在两种局限性。
首先它只能在相关进程间使用,其次它是半双工的,即使一些系统提供全双工的管道但为了可移植性,我们不应作此假设。
命名管道解决了第一个局限性,他能在不相关的进程之间进行数据通信,但是同样具有第二个局限性,即通常他们是半双工的。
下面介绍两种没有上面所说的局限性的同性方法。消息队列和unix域套接字。
对于消息队列这里说的只是行为意义上的全双工,即消息队列可以实现a进程b进程之间的双向数据传送,但是这是通过其中含有的 消息类型特性来实现的,后面
会有具体的例子
关于消息队列,简单说明一下几个用到的函数
int msgget(key_t key,int flag);
调用成功则返回非负消息队列id,该id可以供后续的函数使用。出错返回-1
可以在两个进程中使用相同的 key,使他们使用同一个消息队列从而可以进行无关进程的通信,flag参数指定权限 和 IPC_CREATE 做或运算用来指定关键一个
新的消息队列,如果消息队列已存在则忽略IPC_CREATE作用,但是并不是一个错误。
int msgsnd(int msqid,const void *ptr,size_t nbytes,int flag);
成功返回0,出错返回-1
msgid为调用msgget返回的消息队列id
ptr参数指向一个长整型,它包含的正的整型消息,在其后跟随着消息。例如如果要发送最长为100字节的消息则定义如下结构传入ptr参数中
struct data{
long type;
char text[100];
}
那么ptr参数就是指向data的指针。
ssize_t msgrcv(int msgid,void *ptr,size_t nbytes,long type,int flag);
成功返回消息的数据部分长度,出错返回-1
和msgsnd一样 ptr指向一个长整型,其后跟随存放消息数据的缓冲区。
nbytes说明的存放数据的缓冲区的大小。按照上面的我们的定义 nbytes就是 100.他不包括长整型占用的字节
type指定 想要的消息。 type=0 返回队列中的第一个消息。
type>0 返回队列中消息类型为type的第一个消息(这里的type指的是上面定义的结构体中的type)
type<0 返回队列中消息类型值小于或等于type绝对值的消息。如果这种消息有若干个,则去类型值最小的消息
消息队列通常是以先进先出的顺去取走消息的,但是我们在调用 msgsnd 时指定 data中的type字段来传送不同类型的消息,然后调用msgrcv时指定type
从而可以取得特定的消息,实现非先进先出的顺序取走消息。
int msgctl(int msgid,int cmd,struct msqid_ds *buf);
成功返回0,出错返回-1
cmd说明又msgid指定的队列要执行的命令,通常使用的是IPC_RMID来从系统中删除消息队列以及其中的消息。这种删除会立即生效。(与文件不同,文件会等到
最后一个打开他的进程关闭它时才会真正删除数据)
下面是两个 进程间互传消息的例子
希望实现的是 a进程向b 进程传送A字符 b进程向a进程传送B字符。
一种不正确的实现如下:
a进程代码
7 #define KEY 1234
8 #define SIZE 100
9
10 #define SEND_TYPE 'a'
11 #define RECE_TYPE 'b'
12
13 struct message{
14 long type;
15 char text[SIZE];
16 };
17
18 int main(void){
19 int id;
20 if((id=msgget((key_t)KEY,0666 | IPC_CREAT))==-1){
21 perror("msgget error");
22 exit(1);
23 }
24
25 struct message data,receive;
26 strcpy(data.text,"A");
27 data.type=SEND_TYPE;
28
29 printf("int process a: send data(%s) to process b\n",data.text);
30 msgsnd(id,&data,SIZE,0);
31 msgrcv(id,&receive,SIZE,RECE_TYPE,0);
32 printf("in process a: receive data(%s) from process b\n",receive.text);
33 exit(0);
34 }
b进程代码:
6 #define KEY 1234
7 #define SIZE 100
8
9 #define SEND_TYPE 'b'
10 #define RECE_TYPE 'a'
11 struct message{
12 long type;
13 char text[SIZE];
14 };
15
16 int main(void){
17 int id;
18 if((id=msgget((key_t)KEY,0666 | IPC_CREAT))==-1){
19 perror("msgget error");
20 exit(1);
21 }
22
23 struct message data,receive;
24 strcpy(data.text,"B");
25 data.type=SEND_TYPE;
26
27 printf("int process b: send data(%s) to process a\n",data.text);
28 msgsnd(id,&data,SIZE,0);
29 msgrcv(id,&receive,SIZE,RECE_TYPE,0);
30 printf("in process b: receive data(%s) from a\n",receive.text);
31 exit(0);
32 }
a进程和b 进程互相传递数据后再试图获取对方发送过来的数据
运行程序
feng@ubuntu:~/learn_linux_c_second/chapter_15$ ./a & ./b
[1] 3775
int process a: send data(A) to process b
in process a: receive data(B) from process b
int process b: send data(B) to process a
in process b: receive data(A) from a
[1]+ Done ./a
从输出中看到,a 进程b进程都正确的获得了 对方传递过来的数据。这就是通过对 消息meaasge中类型type的设置来实现的,注意两个程序中最后我们都未
删除消息队列,那么两个进程结束后消息队列仍旧是在系统中的。
unix域套接字也能实现本地的双向通信.对于unix域的的函数调用这里不做详细介绍。直接看使用unix域套接字实现的两个无关进程间的双向通信
a进程创建一个服务器以供b进程链接,当 b 进程链接后, a进程发送字符A 给进程b ,b进程发送字符B 给进程a
a进程代码:
7 int main(void){
8 int serverfd;
9
10 serverfd=socket(AF_UNIX,SOCK_STREAM,0);
11 if(serverfd==-1){
12 perror("socket error");
13 exit(1);
14 }
15
16 struct sockaddr_un server;
17 server.sun_family=AF_UNIX;
18 strcpy(server.sun_path,"SERVER");
19 if(bind(serverfd,(struct sockaddr *)&server,sizeof(server))==-1){
20 perror("bind error");
21 exit(1);
22 }
23 listen(serverfd,5);
24
25 int data='A',receive;
26 int client;
27 if((client=accept(serverfd,NULL,NULL))==-1){
28 perror("connect error");
29 exit(1);
30 }
31 printf("int process a: send data(%c) to process b\n",data);
32 write(client,&data,1);
33 read(client,&receive,1);
34 printf("int process a:receive data(%c) from process b\n",receive);
35
36 exit(0);
37 }
b进程代码:
7 int main(void){
8 int client;
9
10 client=socket(AF_UNIX,SOCK_STREAM,0);
11 if(client==-1){
12 perror("socket error");
13 exit(1);
14 }
15
16 struct sockaddr_un server;
17 server.sun_family=AF_UNIX;
18 strcpy(server.sun_path,"SERVER");
19
20 int data='B',receive;
21 if(connect(client,(struct sockaddr *)&server,sizeof(server))==-1){
22 perror("connect error");
23 exit(1);
24 }
25
26 printf("int process b: send data(%c) to process a\n",data);
27 write(client,&data,1);
28 read(client,&receive,1);
29 printf("int process b:receive data(%c) from process a\n",receive);
30
31 exit(0);
32 }
程序运行结果如下:
feng@ubuntu:~/learn_linux_c_second/chapter_15$ ./a & ./b
[3] 4574
int process b: send data(B) to process a
int process a: send data(A) to process b
int process b:receive data(A) from process a
int process a:receive data(B) from process b
[3]+ Done ./a
a进程和b进程都正确获得了对方发送的数据
阅读(5013) | 评论(0) | 转发(2) |