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
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
#include
#include
#include
#include
#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
#include
#include
#include
#include
#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
#include
#include
#include
#include
#include
#include
#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
#include
#include
#include
#include
#include
#include
#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信号量;