Linux下进程间通信方式:
# 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
# 有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
# 信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
# 消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
# 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
# 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
# 套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
System V 与 POSIX的区别:
System V IPC存在时间比较老,许多系统都支持,但是接口复杂,并且可能各平台上实现略有区别(如ftok的实现及限制)。
POSIX是新标准,现在多数UNIX也已实现,我觉得如果只是开发的话,那么还是POSIX好,因为语法简单,并且各平台上实现都一样。
2、消息队列的数据结构
struct msqid_ds *msgque[MSGMNI]向量:
msgque[MSGMNI]是一个msqid_ds结构的指针数组,每个msqid_ds结构指针代表一个系统消息队列,msgque[MSGMNI]的大小为MSGMNI=128,也就是说系统最多有MSGMNI=128个消息队列。
3、消息队列Key的获取:
在程序中若要使用消息队列,必须要能知道消息队列key,因为应用进程无法直接访问内核消息队列中的数据结构,因此需要一个消息队列的标识,让应用进程知道当前操作的是哪个消息队列,同时也要保证每个消息队列key值的唯一性
http://blog.csdn.net/stonecao/article/details/10364287
与其他的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、mq_open来创建非默认个数大小消息队列:
函数原型
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; };
当第四个参数为空指针时,就使用默认属性。
当指向mq_attr结构的指针作为参数时,允许我们在该函数的实际操作时创建一个新队列时,给它指定mq_maxmsg和mq_msgsize属性.mq_open忽略该结构的另外两个成员.
(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.
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|, |[email protected]| * 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|, |[email protected]| * 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信号量;