Chinaunix首页 | 论坛 | 博客
  • 博客访问: 807978
  • 博文数量: 247
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 501
  • 用 户 组: 普通用户
  • 注册时间: 2013-07-12 21:53
个人简介

系统未建立

文章分类

全部博文(247)

文章存档

2021年(1)

2020年(3)

2019年(5)

2018年(3)

2017年(44)

2016年(75)

2015年(52)

2014年(63)

2013年(1)

我的朋友

分类: LINUX

2016-07-26 19:53:56

对于本地进程间通信,普通管道提供了相关进程(都有想相同的祖先)之间通信的一种方法,但是普通管道存在两种局限性。
首先它只能在相关进程间使用,其次它是半双工的,即使一些系统提供全双工的管道但为了可移植性,我们不应作此假设。
命名管道解决了第一个局限性,他能在不相关的进程之间进行数据通信,但是同样具有第二个局限性,即通常他们是半双工的。


下面介绍两种没有上面所说的局限性的同性方法。消息队列和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进程都正确获得了对方发送的数据
阅读(822) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~