Linux -进程通信

一.概念

消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。  每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值。我们可以通过发送消息 来避免命名管道的同步和阻塞问题。消息队列与管道不同的是,消息队列是基于消息的, 而管道是基于字节流的,且消息队列的读取不?定是先入先出。消息队列与命名管道有一样的不足,就是每个消息的最大长度是有上限的(MSGMAX),每个消息队列的总的字节数是有上限的(MSGMNB),系统上消息队列的总数也有?个上限(MSGMNI)。

查看机器的三个上限值:

二. IPC对象数据结构

内核为每个IPC对象维护?一个数据结构(/usr/include/linux/ipc.h)

[cpp] view
plain
 copy

  1. struct ipc_perm
  2. {
  3. key_t  __key;  /* Key supplied to xxxget(2) */
  4. uid_t  uid;         /* Effective UID of owner */
  5. gid_t  gid;         /* Effective GID of owner */
  6. uid_t  cuid;        /* Effective UID of creator */
  7. gid_t  cgid;        /* Effective GID of creator */
  8. unsigned short  mode;        /* Permissions */
  9. unsigned short  __seq;       /* Sequence number */ };

消息队列,共享内存和信号量都有这样?一个共同的数据结构。

三、消息队列结构(/usr/include/linux/msg.h)

[cpp] view
plain
 copy

  1. /* Obsolete, used only for backwards compatibility and libc5 compiles */
  2. struct msqid_ds {
  3. <span style="color:#FF0000;">struct ipc_perm msg_perm;</span>
  4. struct msg *msg_first;      /* first message on queue,unused  */
  5. struct msg *msg_last;       /* last message in queue,unused */
  6. __kernel_time_t msg_stime;  /* last msgsnd time */
  7. __kernel_time_t msg_rtime;  /* last msgrcv time */
  8. __kernel_time_t msg_ctime;  /* last change time */
  9. unsigned long  msg_lcbytes; /* Reuse junk fields for 32 bit */
  10. unsigned long  msg_lqbytes; /* ditto */
  11. unsigned short msg_cbytes;  /* current number of bytes on queue */
  12. unsigned short msg_qnum;    /* number of messages in queue */
  13. unsigned short msg_qbytes;  /* max number of bytes on queue */
  14. __kernel_ipc_pid_t msg_lspid;   /* pid of last msgsnd */
  15. __kernel_ipc_pid_t msg_lrpid;   /* last receive pid */
  16. };

可以看到第?个红色条目就是IPC结构体,即是共有的,后面的都是消息队列所私有的成员。消息队列是用链表实现的。

四.消息队列的函数

实现消息队列的相关函数:

1.生成新消息队列或者取得已存在的消息队列

[cpp] view
plain
 copy

  1. int msgget(ket_t key,int msgflg);

key:可以认为是一个端口号,也可以由函数ftok生成。

参数: msg?g

IPC_CREAT:如果IPC不存在,则创建一个IPC资源,否则打开操作。

IPC_EXCL:只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。 如果单独使用IPC_CREAT   XXXget()函数要么返回一个已经存在的共享内存的操作符,要 么返回一个新建的共享内存的标识符。 如果将IPC_CREAT和IPC_EXCL标志一起使用,XXXget()将返回一个新建的IPC标识符 ;如果该IPC资源已存在,或者返回-1。      IPC_EXEL标志本身并没有太大的意义,但是和IPC_CREAT标志一起使用可以用来保证所得的对象是新建的,而不是打开已有的对象。

返回值:返回msgid。

2.向队列读/写消息

msgrcv取消息:

[cpp] view
plain
 copy

  1. ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msg?g);

msgsnd读消息:

[cpp] view
plain
 copy

  1. int msgsnd(int msqid, const void *msgp, size_t msgsz, int msg?g);

msqid:消息队列的标识码。

msgp:指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,形态如下:

[cpp] view
plain
 copy

  1. struct msgstru
  2. {
  3. long mtype;//大于0
  4. char mtext[_SIZE_]; //_SIZE_ 用户指定大小
  5. }

msgsz:消息的大小。

msgtyp:从消息队列内读取的消息形态。如果值为零,则表示消息队列中的所有消息都会被读取。   

msg?g:用来指明核心程序在队列没有数据的情况下所应采取的行动。如果msg?g和常数IPC_NOWAIT合用,则在msgsnd()执行时若是消息队列已满,则msgsnd()将不会阻塞,而会立即返回-1,如果执行的是msgrcv(),则在消息队列呈空时,不做等待马上返回-1,并设定 错误码为ENOMSG。当msg?g为0时,msgsnd()及msgrcv()在队列呈满或呈空的情形时,采取
阻塞等待的处理模式。

.应用

          ———使用消息队列实现一个简单的客户端--服务端的通信

实现代码如下给出:

comm.h

[cpp] view
plain
 copy

  1. #pragma once
  2. #include<stdio.h>
  3. #include<errno.h>
  4. #include<string.h>
  5. #include<sys/types.h>
  6. #include<sys/ipc.h>
  7. #include<sys/msg.h>
  8. #define _PATH_NAME_ "/tmp"
  9. #define _PROJ_ID_ 0x6666
  10. #define _SIZE_ 1024
  11. extern int server_type;
  12. extern int client_type;
  13. struct msgbuf
  14. {
  15. long mtype;
  16. char mtext[_SIZE_];
  17. };
  18. int creat_msg_queue();
  19. int get_msg_queue();
  20. //int creat_msg_queue(int msg_id);
  21. int send_msg(int msg_id,int send_type,const char* msg);
  22. int recv_msg(int msg_id,int recv_type,char* msg_out);
  23. int destroy_queue(int msg_id);

comm.c:

[cpp] view
plain
 copy

  1. #include"comm.h"
  2. int server_type = 1;
  3. int client_type = 2;
  4. static int comm_msg_queue(int flags)
  5. {
  6. key_t _key = ftok(_PATH_NAME_,_PROJ_ID_);
  7. if(_key < 0)
  8. {
  9. printf("%d : %s",errno,strerror(errno));
  10. return -1;
  11. }
  12. int msg_id = msgget(_key,flags);
  13. //int msg_id = msgget(_key,IPC_CREAT | IPC_EXCL | 0666);
  14. return msg_id;
  15. }
  16. int creat_msg_queue()
  17. {
  18. int flags = IPC_CREAT | IPC_EXCL | 0666;
  19. return comm_msg_queue(flags);
  20. }
  21. int get_msg_queue()
  22. {
  23. int flags = IPC_CREAT;
  24. return comm_msg_queue(flags);
  25. }
  26. int destroy_queue(int msg_id)
  27. {
  28. if(msgctl(msg_id,IPC_RMID,NULL) != 0)
  29. {
  30. printf("%d : %s",errno,strerror(errno));
  31. return -1;
  32. }
  33. return 0;
  34. }
  35. int send_msg(int msg_id,int send_type,const char* msg)
  36. {
  37. struct msgbuf _buf;
  38. _buf.mtype = send_type;
  39. strncpy(_buf.mtext,msg,strlen(msg)+1);
  40. if(msgsnd(msg_id,&_buf,sizeof(_buf.mtext),0) < 0)
  41. {
  42. printf("%d : %s",errno,strerror(errno));
  43. return -1;
  44. }
  45. return 0;
  46. }
  47. int recv_msg(int msg_id,int recv_type,char* msg_out)
  48. {
  49. struct msgbuf _buf;
  50. _buf.mtype = 0;
  51. memset(_buf.mtext,‘\0‘,sizeof(_buf.mtext));
  52. if(msgrcv(msg_id,&_buf,sizeof(_buf.mtext),recv_type,0) < 0)
  53. {
  54. printf("%d : %s",errno,strerror(errno));
  55. return -1;
  56. }
  57. strcpy(msg_out,_buf.mtext);
  58. return 0;
  59. }

server.c:

[cpp] view
plain
 copy

  1. #include"comm.h"
  2. int main()
  3. {
  4. int msg_id = creat_msg_queue();
  5. if(msg_id <0)
  6. {
  7. printf("%d : %s\n",errno,strerror(errno));
  8. return 1;
  9. }
  10. char buf[_SIZE_];
  11. while(1)
  12. {
  13. memset(buf,‘\0‘,sizeof(buf));
  14. recv_msg(msg_id,client_type,buf);
  15. printf("client:%s\n",buf);
  16. if(strcasecmp(buf,"quit") == 0)
  17. {
  18. break;
  19. }
  20. printf("client say done,Please Enter# ");
  21. fflush(stdout);
  22. ssize_t _s = read(0,buf,sizeof(buf)-1);
  23. if(_s > 0)
  24. {
  25. buf[_s - 1] = ‘\0‘;
  26. }
  27. send_msg(msg_id,server_type,buf);
  28. }
  29. destroy_queue(msg_id);
  30. return 0;
  31. }

client.c:

[cpp] view
plain
 copy

  1. #include"comm.h"
  2. int main()
  3. {
  4. //int msg_id = creat_msg_queue();
  5. //if(msg_id <0)
  6. //{
  7. //  printf("%d : %s\n",errno,strerror(errno));
  8. //  return 1;
  9. //}
  10. //
  11. //
  12. int msg_id = get_msg_queue();
  13. char buf[_SIZE_];
  14. while(1)
  15. {
  16. printf("Please Enter:");
  17. fflush(stdout);
  18. ssize_t _s = read(0,buf,sizeof(buf)-1);
  19. if(_s >0 )
  20. {
  21. buf[_s - 1] = ‘\0‘;
  22. }
  23. send_msg(msg_id,client_type,buf);
  24. if(strcasecmp(buf,"quit") == 0)
  25. {
  26. break;
  27. }
  28. memset(buf,‘\0‘,sizeof(buf));
  29. recv_msg(msg_id,server_type,buf);
  30. printf("server# %s\n",buf);
  31. }
  32. return 0;
  33. }

六.测试过程及结果

头文件:

各个接口:

[cpp] view
plain
 copy

  1. int creat_msg_queue();  //创建一个消息队列
  2. int get_msg_queue();   //获得消息队列的msg_id

[cpp] view
plain
 copy

  1. int send_msg(int msg_id,int send_type,const char* msg);  //发送消息

[cpp] view
plain
 copy

  1. int recv_msg(int msg_id,int recv_type,char* msg_out);  //接收消息

[cpp] view
plain
 copy

  1. int destroy_queue(int msg_id);  //销毁队列

server.c:创建消息队列,接收客户端发送的消息,同时给客户端发送消息

client.c:获取msg_id,接收服务端发送的消息,同时给客户端反馈

Makefile:

要生成两个可执行程序,所以使用伪目标all。

[email protected](依赖关系中代表目标文件) $^(依赖关系中:右边的所有内容)

测试结果:

时间: 2024-11-03 05:41:41

Linux -进程通信的相关文章

Linux 进程通信之 ——信号和信号量总结

如今最经常使用的进程间通信的方式有:信号,信号量,消息队列,共享内存.       所谓进程通信,就是不同进程之间进行一些"接触",这种接触有简单,也有复杂.机制不同,复杂度也不一样.通信是一个广义上的意义,不仅仅指传递一些massege.他们的用法是基本相同的,所以仅仅要掌握了一种的用法,然后记住其他的用法就能够了. 1. 信号       在我学习的内容中,主要接触了信号来实现同步的机制,据说信号也能够用来做其他的事      情,可是我还不知道做什么.       信号和信号量是

浅析linux进程通信的方式

求职笔试中,考察进程通信方式是一个老生长谈的问题,每次都让我答得一头雾水,于是我总结了一下 这些必须了解的知识点. 实现linux进程通信的方式有6种: --内存共享 --信号(Singal) --管道(Pipe) --消息队列(Message) --信号量(Semaphore) --socket 消息队列通信 请关注:http://blog.csdn.net/ljianhui/article/details/10287879 内存共享通信 请关注:http://blog.csdn.net/lj

linux进程通信之SYSTEM V信号量

信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有.信号量的值为正的时候,说明它空闲.所测试的线程可以锁定而使用它.若为0,说明它被占用,测试的线程要进入睡眠队列中,等待被唤醒. 一.信号量的分类: 在学习信号量之前,我们必须先知道--Linux提供两种信号量: (1) 内核信号量,由内核控制路径使用. (2) 用户态进程使用的信号量,这种信号量又分为POSIX信号量和SYSTEM V信号量. POSIX信号量又分为有名信号量和无名信号量.有名信号量,其值保存在文件

Linux进程通信----匿名管道

Linux进程通信中最为简单的方式是匿名管道 匿名管道的创建需要用到pipe函数,pipe函数参数为一个数组 表示的文件描述字.这个数组有两个文件描述字,第一个是用 于读数据的文件描述符第二个是用于写数据的文件描述符. 不能将用于写的文件描述符进行读操作或者进行读的文件描述 符进写操作,这样都会导致错误. 关于匿名管道的几点说明: 1.匿名管道是半双工的,即一个进程只能读,一个进程只能写    要实现全双工,需要两个匿名管道. 2.只能在父子进程或者兄弟进程进行通信. 3.在读的时候关闭写文件描

Linux进程通信——管道

进程间通信(IPC:Inner Proceeding Communication) 进程是操作系统实现程序独占系统运行的假象的方法,是对处理器.主存.I/O设备的抽象表示.每个进程都是一个独立的资源管理单元,每个进程所看到的是自己独占使用系统的假象,因此各个进程之间是不能够直接的访问对方进程的资源的,不同的进程之间进行信息交互需要借助操作系统提供的特殊的进程通信机制. 进程之间的通信,从物理上分,可以分为同主机的进程之间的通信和不同主机间的进程之间的通信.从通信内容方式上分,可以分为数据交互.同

Linux笔记--Linux进程通信

Linux进程间通信 文章来源: http://www.cnblogs.com/linshui91/archive/2010/09/29/1838770.html 一.进程间通信概述进程通信有如下一些目的:A.数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间B.共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到.C.通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程).D.

Linux进程通信的几种方式总结

进程通信的目的 数据传输 一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间 共享数据 多个进程想要操作共享数据,一个进程对共享数据 通知事 一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程). 资源共享 多个进程之间共享同样的资源.为了作到这一点,需要内核提供锁和同步机制. 进程控制 有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变.

linux 进程通信之 共享内存

共享内存是被多个进程共享的一部分物理内存.共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容. 关于共享内存使用的API key_t ftok(const char *pathname, int proj_id); #在IPC中,我们经常用一个 key_t 的值来创建或者打开 信号量,共享内存和消息队列.这个 key_t 就是由ftok函数产生的. pathname:指定的文件名,该文件必须是存在而且可以访问 proj_

linux进程通信之管道

1.介绍: 1)同一主机: unix进程通信方式:无名管道,有名管道,信号 system v方式:信号量,消息队列,共享内存 2)网络通信:Socket,RPC 2.管道: 无名管道(PIPE):使用一个进程的标准输出作为另一个进程的标准输入建立的一个单向管道,执行完成后消失.主要用于父进程与子进程之间,或者两个兄弟进程之间.采用的是单向 1)创建无名管道:(#include(unistd.h)) extern int pipe(int pipes[2]),pipes[0]完成读操作,pipes

linux进程通信

e14: 进程间通信(进程之间发送/接收字符串/结构体): 传统的通信方式: 管道(有名管道 fifo,无名管道 pipe) 信号 signal System V(基于IPC的对象):                             IPC对象:                                                    ipcrm [ -M key | -m id | -Q key | -q id | -S key | -s id ] ... 消息队列 mes