一 消息队列方式
Linux消息队列是链式队列,链队上每个结点都是一个消息。一个进程可以将某一消息加入消息队列中,另一个进程可以从此消息队列中读取消息。一个典型的案例如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define SIZE 1024
const long id = 1000;
/*
* a example to send and receive message
* */
int main() {
key_t unique_key;
int msgid;
int status;
char str[SIZE];
struct msgbuf {
long msgtype;
char msgtext[SIZE];
}sndmsg, rcvmsg;
/*
* get the identifier of a message queue
* if the new message queue is created then msgget return the id
* else return -1
* */
if((msgid = msgget(unique_key, IPC_PRIVATE | 0666)) == -1) {
fprintf(stderr, "msgget error!\n");
exit(1);
}
int pid = fork();
if(pid == 0) {
sndmsg.msgtype = id;
strcpy(str, "Hello World, I am wangzhicheng!\n");
sprintf(sndmsg.msgtext, str);
/*
* send message to message queue
* */
if(msgsnd(msgid, (struct msgbuf *)&sndmsg, sizeof(str) + 1, 0) == -1) {
fprintf(stderr, "msgsnd error! \n");
exit(2);
}
return 0;
}
else if(pid > 0) {
sleep(3);
if((status = msgrcv(msgid, (struct msgbuf *)&rcvmsg, sizeof(str) + 1, id, IPC_NOWAIT)) == -1) {
fprintf(stderr, "msgrcv error!\n");
exit(4);
}
printf("The received message is:%s\n", rcvmsg.msgtext);
msgctl(msgid, IPC_RMID, 0); // delete the message queue
return 0;
}
else {
fprintf(stderr, "fork error!\n");
msgctl(msgid, IPC_RMID, 0);
exit(5);
}
}
要点分析:
1. msgget(long key, int msgflag)函数用于创建消息队列,key可由用户设定,msgflag指明了消息队列的操作权限和控制命令。0666表示此消息队列可读写,IPC_PRIVATE表示创建自己的消息队列。
2. 当父进程fork出子进程后,子进程使用msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)函数来将消息挂载到消息队列中,其中msgp指向消息缓存,msgfla表明进程在消息队列满或空应采取的行动,IPC_NOWAIT表示当消息队列为空时,函数返回错误信息。
3. 当子进程执行完毕后,父进程使用msgrcv函数从消息队列中取出消息。之所以要让父进程休眠3秒,就是要等子进程把消息挂载到消息队列上。
4. 消息队列可通过函数msgctl和关键字IPC_RM(remove)ID回收。
执行结果:
二 共享内存方式
共享内存是进程间通信最快的一种方式,还是以上述案例为基础,修改后代码如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
#include <error.h>
#define SIZE 1024 // the size of shm
/*
* the father process fork the child process and shm
* the child process put data into the shm
* the father process read data from shm
* */
int main() {
int shmid;
char *shmaddr;
struct shmid_ds buf; // the structure of shm
int flag = 0;
int pid;
// 0600 means permission
shmid = shmget(IPC_PRIVATE, SIZE, IPC_CREAT | 0600);
if(shmid < 0) {
perror("get shm id error!\n");
exit(1);
}
pid = fork();
if(pid == 0) {
shmaddr = (char *)shmat(shmid, NULL, 0);
if(shmaddr == (void*)(-1)) {
perror("shmat addr error!\n");
exit(2);
}
strcpy(shmaddr, "Hello World, I am wangzhicheng!\n");
shmdt(shmaddr); // to forbid process to access the shm
return 0;
}
else if(pid > 0) {
sleep(3);
flag = shmctl(shmid, IPC_STAT, &buf); // to copy the shm state to buf
if(flag == -1) {
perror("shmctl error!\n");
exit(3);
}
printf("shm size =%d bytes \n", buf.shm_segsz);
// getpid() pointer to the current program
printf("parent id = %d, shm_cpid =%d\n", getpid(), buf.shm_cpid); // shm_cpid means the id of created the shm
printf("child pid = %d, shm_lpid = %d\n", pid, buf.shm_lpid); // shm_lpid meansns the last process id
shmaddr = (char *)shmat(shmid, NULL, 0);
if(shmaddr == (void *)(-1)) {
perror("shmat error!\n");
exit(4);
}
puts(shmaddr);
shmdt(shmaddr);
shmctl(shmid, IPC_RMID, NULL); // remove the shm
}
else {
perror("fork error!\n");
shmctl(shmid, IPC_RMID, NULL);
}
return 0;
}
要点分析:
1. shmget用于获取共享内存,IPC_CREAT和0600指明了创建一个可读写的共享内存。
2. shmat用于将共享内存映射到进程内存空间,让进程可以像在自己的地址空间里访问共享内存。
3. 子进程被创建后将字符串写入shmaddr所指向的共享内存中。
4. 父进程同样使用shmat进行地址映射,然后从共享内存中读取数据。
5. shmdt表明进程断开与共享内存的地址映射。
6. shmctl和IPC_RMID用于共享内存的回收。
执行结果: