Linux下进程间通信方式:
# 有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
# 消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
# 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
System V 与 POSIX的区别:
IPC存在时间比较老,许多系统都支持,但是接口复杂,并且可能各平台上实现略有区别(如ftok的实现及限制)。
POSIX是新标准,现在多数也已实现,我觉得如果只是开发的话,那么还是好,因为语法简单,并且各平台上实现都一样。
数据结构
msgque[MSGMNI]是一个msqid_ds结构的指针数组,每个msqid_ds结构指针代表一个系统消息队列,msgque[MSGMNI]的大小为MSGMNI=128,也就是说系统最多有MSGMNI=128个消息队列。
3、消息队列Key的获取:
数据结构,因此需要一个消息队列的标识,让应用进程知道当前操作的是哪个消息队列,同时也要保证每个消息队列key值的唯一性
- int msgget(key_t, key, int msgflg);
与其他的IPC机制一样,程序必须提供一个键来命名某个特定的消息队列。msgflg是一个权限标志,表示消息队列的访问权限,它与文件的访问权限一样。msgflg可以与IPC_CREAT做或操作,表示当key所命名的消息队列不存在时创建一个消息队列,如果key所命名的消息队列存在时,IPC_CREAT标志会被忽略,而只返回一个标识符。
在程序中若要使用消息队列,必须要能知道消息队列key,因为应用进程无法直接访问内核消息队列中的数据结构,因此需要一个消息队列的标识,让应用进程知道当前操作的是哪个消息队列,同时也要保证每个消息队列key值的唯一性。
申请一块内存,创建一个新的消息队列(数据结构msqid_ds),将其初始化后加入到msgque向量表中的某个空位置处,返回标示符。或者在msgque向量表中找键值为key的消息队列。
2、将消息添加到消息队列中:
- int msgsend(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);
msgid是由msgget函数返回的消息队列标识符。
- struct my_message{
- long int message_type;
- /* The data you wish to transfer*/
- };
- int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg);
- int msgctl(int msgid, int command, struct msgid_ds *buf);
- struct msgid_ds
- {
- uid_t shm_perm.uid;
- uid_t shm_perm.gid;
- mode_t shm_perm.mode;
- };
- /*send.c*/
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/msg.h>
- #include <errno.h>
- #define MSGKEY 1024
- struct msgstru
- {
- long msgtype;
- char msgtext[2048];
- };
- main()
- {
- struct msgstru msgs;
- int msg_type;
- char str[256];
- int ret_value;
- int msqid;
- msqid=msgget(MSGKEY,IPC_EXCL); /*检查消息队列是否存在*/
- if(msqid < 0){
- msqid = msgget(MSGKEY,IPC_CREAT|0666);/*创建消息队列*/
- if(msqid <0){
- printf("failed to create msq | errno=%d [%s]\n",errno,strerror(errno));
- exit(-1);
- }
- }
- while (1){
- printf("input message type(end:0):");
- scanf("%d",&msg_type);
- if (msg_type == 0)
- break;
- printf("input message to be sent:");
- scanf ("%s",str);
- msgs.msgtype = msg_type;
- strcpy(msgs.msgtext, str);
- /* 发送消息队列 */
- ret_value = msgsnd(msqid,&msgs,sizeof(struct msgstru),IPC_NOWAIT);
- if ( ret_value < 0 ) {
- printf("msgsnd() write msg failed,errno=%d[%s]\n",errno,strerror(errno));
- exit(-1);
- }
- }
- msgctl(msqid,IPC_RMID,0); //删除消息队列
- }
- /*receive.c */
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/msg.h>
- #include <errno.h>
- #define MSGKEY 1024
- struct msgstru
- {
- long msgtype;
- char msgtext[2048];
- };
- /*子进程,监听消息队列*/
- void childproc(){
- struct msgstru msgs;
- int msgid,ret_value;
- char str[512];
- while(1){
- msgid = msgget(MSGKEY,IPC_EXCL );/*检查消息队列是否存在 */
- if(msgid < 0){
- printf("msq not existed! errno=%d [%s]\n",errno,strerror(errno));
- sleep(2);
- continue;
- }
- /*接收消息队列*/
- ret_value = msgrcv(msgid,&msgs,sizeof(struct msgstru),0,0);
- printf("text=[%s] pid=[%d]\n",msgs.msgtext,getpid());
- }
- return;
- }
- void main()
- {
- int i,cpid;
- /* create 5 child process */
- for (i=0;i<5;i++){
- cpid = fork();
- if (cpid < 0)
- printf("fork failed\n");
- else if (cpid ==0) /*child process*/
- childproc();
- }
- }
若父进程退出,子进程尚未结束,则子进程会被init进程领养,也就是说init进程将成为该子进程的父进程。
若希望父进程退出,子进程也退出的话,可以使用线程,因为若进程结束,则还没结束的线程一定会立刻结束。
另外:以上是system v标准下的消息队列接口,如果要使用POSIX标准来创建消息队列,1、
mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr);
第4个参数为 mq_attr 指针
struct mq_attr{
long mq_flags;
long mq_maxmsg;
long mq_msgsize;
long mq_curmsgs;};
当第四个参数为空指针时,就使用默认属性。
(1)attr.mq_maxmsg 不能超过文件 /proc/sys/fs/mqueue/msg_max 中的数值;
(2)attr.mq_msgsize不能超过 /proc/sys/fs/mqueue/msgsize_max 的数值;
(3)消息队列名称前面必须加上斜杆。
在POSIX消息队列中 msg_max 默认为 10 ,msgsize_max 默认为8192 ,可以在 /proc/sys/fs/mqueue# cat msg_max /proc/sys/fs/mqueue# cat msgsize_max 查看
修改的话,要使用:echo 1000 > /proc/sys/fs/mqueue/msg_max往里面写。
2、获取消息队列的属性 一个进程在发送和接收消息之前,需要了解消息对象的属性,如消息的最大长度。以便设定接收和发送的buffer大小。 mqd_t mq_getattr(mqd_t mqdes, struct mq_attr *attr);参数: Mqdes:打开消息队列时获取的描述符。 Attr:指向结构struct mq_attr的指针,用来获取消息队列的四个属性 struct mq_attr { long mq_flags; // 0或者O_NONBLOCK long mq_maxmsg; //队列中包含的消息数的最大限制数
long mq_msgsize; //每个消息大小的最大限制数
long mq_curmsgs; //当前队列中的消息数
} 3、设置消息队列属性 我们可以设置消息队列的属性,实际只能设置flag标志,说明队列中没有消息时,接收消息的进程是否在队列上继续等待。 mqd_t mq_setattr(mqd_t mqdes, struct mq_attr *newattr, struct mq_attr *oldattr); 参数: Mqdes:打开消息队列时获取的描述符。 Attr:指向结构struct mq_attr的指针,用来获取消息队列的最大消息个数和最大消息长度。放到数据结构的mq_maxmsg和mq_msgsize中。
struct mq_attr { long mq_flags; // 0或者O_NONBLOCK,只能设置这个
long mq_maxmsg; //队列中包含的消息数的最大限制数
long mq_msgsize; //每个消息大小的最大限制数
long mq_curmsgs; //当前队列中的消息数
} oldattr:用来保存设置之前的attr值,可以为NULL.
4、发送消息 进程在打开消息队列后,可以使用下面的函数发送消息 int mq_send(mqd_t mqdes, const char *ptr, size_t len, unsigned int prio); 参数: mqdes: 打开消息队列时获得的描述符。 ptr: 指向发送缓冲区的指针,发送缓冲区存放了要发送的数据。 Len: 要发送的数据的长度。 prio :消息的优先级;它是一个小于 MQ_PRIO_MAX 的数,数值越大,优先级越高。 POSIX 消息队列在调用 mq_receive 时总是返回队列中 最高优先级的最早消息 。如果消息不需要设定优先级,那么可以在 mq_send 是置 prio 为 0 , mq_receive 的 prio 置为 NULL 。 返回值:发送成功,返回0,失败,返回-1. 5、接收消息 进程在打开消息队列后,可以使用下面的函数接收消息。 ssize_t mq_receive(mqd_t mqdes, char *ptr, size_t len, unsigned int *prio); 参数: mqdes: 打开消息队列时获得的描述符。 ptr: 指向接收缓冲区的指针。接收缓冲区用来存放收到的消息。 Len: 接收缓冲区的长度。 len不能小于mq_msgsize,否则会返回EMSGSIZE prio :消息的优先级;它是一个小于 MQ_PRIO_MAX 的数,数值越大,优先级越高。 POSIX 消息队列在调用 mq_receive 时总是返回队列中 最高优先级的最早消息 。如果消息不需要设定优先级,那么可以在 mq_send 是置 prio 为 0 , mq_receive 的 prio 置为 NULL 。 返回值: 接收成功,返回0,失败,返回-1.
6、消息队列的关闭 mqd_t mq_close(mqd_t mqdes); 关闭消息队列,但不能删除它 成功返回0,失败返回-1
7、删除消息队列 mqd_t mq_unlink(const char *name); 成功返回0,失败返回-1 当某个进程还没有关闭此消息队列时,调用mq_unlink时,不会马上删除队列,当最后一个进程关闭队列时,该队列被删除
- 编译:
- gcc -o consumer consumer.c -lrt
- gcc -o producer producer.c -lrt
- /*
- *
- * Filename: producer.c
- *
- * Description: 生产者进程
- *
- * Version: 1.0
- * Created: 09/30/2011 04:52:23 PM
- * Revision: none
- * Compiler: gcc(g++)
- *
- * Author: |Zhenghe Zhang|, |zhenghe.zhang@gmail.com|
- * Company: |Shenzhen XXX Technology Co., Ltd.|
- *
- */
- #include <stdio.h>
- #include <mqueue.h>
- #include <sys/stat.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <time.h>
- #include <string.h>
- #define MAXSIZE 10 //定义buf大小
- #define BUFFER 8192 //定义Msg大小
- struct MsgType{
- int len;
- char buf[MAXSIZE];
- char x;
- short y;
- };
- int main()
- {
- /*消息队列*/
- mqd_t msgq_id;
- struct MsgType msg;
- unsigned int prio = 1;
- unsigned int send_size = BUFFER;
- struct mq_attr msgq_attr;
- const char *file = "/posix";
- /*mq_open() for creating a new queue (using default attributes) */
- /*mq_open() 创建一个新的 POSIX 消息队列或打开一个存在的队列*/
- msgq_id = mq_open(file, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG, NULL);
- if(msgq_id == (mqd_t)-1)
- {
- perror("mq_open");
- exit(1);
- }
- /* getting the attributes from the queue -- mq_getattr() */
- if(mq_getattr(msgq_id, &msgq_attr) == -1)
- {
- perror("mq_getattr");
- exit(1);
- }
- printf("Queue \"%s\":\n\t- stores at most %ld messages\n\t- \
- large at most %ld bytes each\n\t- currently holds %ld messages\n",
- file, msgq_attr.mq_maxmsg, msgq_attr.mq_msgsize, msgq_attr.mq_curmsgs);
- /*setting the attributes of the queue -- mq_setattr() */
- /*mq_setattr() 设置消息队列的属性,设置时使用由 newattr 指针指向的 mq_attr 结构的信息。*/
- /*属性中只有标志 mq_flasgs 里的 O_NONBLOCK 标志可以更改,其它在 newattr 内的域都被忽略 */
- if(mq_setattr(msgq_id, &msgq_attr, NULL) == -1)
- {
- perror("mq_setattr");
- exit(1);
- }
- int i = 0;
- while(i < 10)
- {
- msg.len = i;
- memset(msg.buf, 0, MAXSIZE);
- sprintf(msg.buf, "0x%x", i);
- msg.x = (char)(i + 'a');
- msg.y = (short)(i + 100);
- printf("msg.len = %d, msg.buf = %s, msg.x = %c, msg.y = %d\n", msg.len, msg.buf, msg.x, msg.y);
- /*sending the message -- mq_send() */
- /*mq_send() 把 msg_ptr 指向的消息加入由 mqdes 引用的消息队列里。*/
- /*参数 msg_len 指定消息 msg_ptr 的长度:这个长度必须小于或等于队列 mq_msgsize 属性的值。零长度的消息是允许。*/
- if(mq_send(msgq_id, (char*)&msg, sizeof(struct MsgType), prio) == -1)
- {
- perror("mq_send");
- exit(1);
- }
- i++;
- sleep(1);
- }
- sleep(30); //等待消费者进程退出
- /*closing the queue -- mq_close() */
- /*mq_close() 关闭消息队列描述符 mqdes。如果调用进程在消息队列 mqdes 绑定了通知请求,*/
- /*那么这个请求被删除,此后其它进程就可以绑定通知请求到此消息队列。*/
- if(mq_close(msgq_id) == -1)
- {
- perror("mq_close");
- exit(1);
- }
- /*mq_unlink() 删除名为 name 的消息队列。消息队列名将被直接删除。*/
- /*消息队列本身在所有引用这个队列的描述符被关闭时销毁。*/
- if(mq_unlink(file) == -1)
- {
- perror("mq_unlink");
- exit(1);
- }
- return 0;
- }
- /*
- *
- * Filename: consumer.c
- *
- * Description: 消费者进程
- *
- * Version: 1.0
- * Created: 09/30/2011 04:52:23 PM
- * Revision: none
- * Compiler: gcc(g++)
- *
- * Author: |Zhenghe Zhang|, |zhenghe.zhang@gmail.com|
- * Company: |Shenzhen XXX Technology Co., Ltd.|
- *
- */
- #include <stdio.h>
- #include <mqueue.h>
- #include <sys/stat.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <time.h>
- #include <string.h>
- #define MAXSIZE 10 //定义buf大小
- #define BUFFER 8192 //定义Msg大小
- struct MsgType{
- int len;
- char buf[MAXSIZE];
- char x;
- short y;
- };
- int main()
- {
- /*消息队列*/
- mqd_t msgq_id;
- struct MsgType msg;
- unsigned int sender;
- struct mq_attr msgq_attr;
- unsigned int recv_size = BUFFER;
- const char *file = "/posix";
- /* mq_open() for opening an existing queue */
- msgq_id = mq_open(file, O_RDWR);
- if(msgq_id == (mqd_t)-1)
- {
- perror("mq_open");
- exit(1);
- }
- /* getting the attributes from the queue -- mq_getattr() */
- if(mq_getattr(msgq_id, &msgq_attr) == -1)
- {
- perror("mq_getattr");
- exit(1);
- }
- printf("Queue \"%s\":\n\t- stores at most %ld messages\n\t- \
- large at most %ld bytes each\n\t- currently holds %ld messages\n",
- file, msgq_attr.mq_maxmsg, msgq_attr.mq_msgsize, msgq_attr.mq_curmsgs);
- if(recv_size < msgq_attr.mq_msgsize)
- recv_size = msgq_attr.mq_msgsize;
- int i = 0;
- while(i < 10) //运行一个consumenr,为 10 ,同时运行两个consumer进程,为 5
- {
- msg.len = -1;
- memset(msg.buf, 0, MAXSIZE);
- msg.x = ' ';
- msg.y = -1;
- /* getting a message */
- /*mq_receive() 从由描述符 mqdes 引用的队列时删除优先级最高的最老的消息,并把放置到 msg_ptr 的缓存区内。*/
- /*参数 msg_len 指定缓冲区 msg_ptr 的大小:它必须大于队列的 mq_msgsize 属性(参数 mq_getattr)。*/
- /*如果 prio 不是 NULL,那么它指向的内存用于返回收到消息相关的优先级。*/
- if (mq_receive(msgq_id, (char*)&msg, recv_size, &sender) == -1)
- {
- perror("mq_receive");
- exit(1);
- }
- printf("msg.len = %d, msg.buf = %s, msg.x = %c, msg.y = %d\n", msg.len, msg.buf, msg.x, msg.y);
- i++;
- sleep(2);
- }
- if(mq_close(msgq_id) == -1)
- {
- perror("mq_close");
- exit(1);
- }
- return 0;
- }
1、僵尸进程;2、共享内存;3、延时函数;4、kill信号;5、fork子进程;6信号量;