消息队列
消息队列提供了进程间发送数据块的方法,每个数据块都可以被认为是有一个类型,接受者接受的数据块可以有不同的类型;我们可以通过发送消息来避免命名管道的同步和阻塞问题;消息队列与命名管道一样,每个数据块都有一个最大长度的限制;我们可以将每个数据块当作是一中消息类型(频道),发送和接收的内容就是这个类型(频道)对应的消息(节目),每个类型(频道)相当于一个独立的管道,相互之间互不影响。
例子:子进程发送10次消息,父进程接收7次。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#define MAX 1024
typedef struct ___msgbuf{
long mtype; //消息类型/通道号
pid_t pid; //进程ID
char mtext[MAX];//消息正文
}MSGBUF;
int main()
{
key_t key = ftok(".", 123);
if( 0 > key)
{
perror("ftok");
exit(-1);
}
int msgid = msgget(key, IPC_CREAT|IPC_EXCL|0666);
if(0 > msgid)
{
if(EEXIST != errno)
{
perror("msgget");
exit(-1);
}
msgid = msgget(key, IPC_EXCL|0666);
}
pid_t pid = fork();
if(0 > pid)
{
perror("fork");
exit(-1);
}
else if(0 == pid)
{
sleep(3);
printf("-------1-------\n");
int k = 10;
while(k--)
{
MSGBUF msgbuf;
memset(&msgbuf, 0, sizeof(msgbuf));
msgbuf.mtype = rand()%10+1;
msgbuf.pid = getpid();
strcpy(msgbuf.mtext, "hello world");
printf("------NO.%d-%ld, %d------\n", k, msgbuf.mtype, msgbuf.pid);
int ret = msgsnd(msgid, &msgbuf, sizeof(msgbuf.pid)+strlen(msgbuf.mtext), 0);
if(0 > ret)
{
perror("msgsnd");
break;
}
sleep(1);
}
printf("-------2-------\n");
}
else
{
int k = 7;
while(k--)
{
MSGBUF msgbuf;
memset(&msgbuf, 0, sizeof(msgbuf));
int ret = msgrcv(msgid, &msgbuf, sizeof(msgbuf.pid)+sizeof(msgbuf.mtext), 0, 0);
if(0 > ret)
{
perror("msgrcv");
break;
}
printf("RECV: %d, %s\n", msgbuf.pid, msgbuf.mtext);
}
wait(NULL);
if(0 > msgctl(msgid, IPC_RMID, NULL))
{
perror("msgctl");
}
}
exit(0);
}
信号灯(信号量)
信号灯提供这样的一种访问机制,让一个临界区同一时间只有一个进程访问它,也就是说信号灯是用来调协进程对共享资源的访问的。信号灯是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。最简单的信号灯是只能取0和1的变量,这也是信号灯量最常见的一种形式,叫做二进制信号灯。
工作原理:
P操作,如果值大于0就减1,并执行后面程序;如果它的值为零,就挂起该进程的执行;
V操作,如果有其他进程因等待而被挂起,就让它恢复运行,如果没有进程因等待而挂起,就给它加1.
使用流程:
key_t key = ftok(".", 1);
int semget(key_t key, int nsems, int semflg); nsems:信号灯数量为1;semflg标志:IPC_CREAT|IPC_EXCL
int semctl(int semid, int semnum, int cmd, ...); 控制信号灯,SETVAL:用来把信号量初始化为一个已知的值,一般初始化为1;IPC_RMID:用于删除一个已经无需继续使用的信号量标识符;
int semop(int semid, struct sembuf *sops, unsigned nsops); 改变信号量的值,sembuf 定义如下:
struct sembuf{
unsigned short sem_num; /* semaphore number */一般为0
short sem_op; /* semaphore operation */信号灯操作,P为-1,V为+1
short sem_flg; /* operation flags */一般为SEM_UNDO
};
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <wait.h>
int P(int sid)
{
struct sembuf lock={0, -1, SEM_UNDO};
return semop(sid, &lock, 1);
}
int V(int sid)
{
struct sembuf lock={0, 1, SEM_UNDO};
return semop(sid, &lock, 1);
}
int Wait(int sid)
{
struct sembuf lock={0, 0, SEM_UNDO};
return semop(sid, &lock, 1);
}
int setsem(int sid, int val)
{
return semctl(sid, 0, SETVAL, val);
}
int rmsem(int sid)
{
return semctl(sid, 0, IPC_RMID, 0);
}
int main()
{
key_t key = ftok(".", 1);
if(0 > key)
{
perror("ftok");
exit(-1);
}
int sid = semget(key, 1, IPC_CREAT|IPC_EXCL);
if(0 > sid)
{
if(EEXIST != errno)
{
perror("semget");
exit(-1);
}
sid = semget(key, 1, IPC_EXCL);
}
setsem(sid, 1);
pid_t pid = fork();
if(0 > pid)
{
perror("fork");
exit(-1);
}
else if(0 == pid)
{
P(sid);
printf("------3-------\n");
sleep(1);
printf("------4-------\n");
V(sid);
}
else
{
P(sid);
printf("------1-------\n");
sleep(1);
printf("------2-------\n");
V(sid);
wait(NULL);
rmsem(sid);
}
exit(0);
}
共享内存
System V进程间的通信方式共享内存,就是允许两个不相关的进程访问同一个物理内存,在系统建立IPC通信时,通过ftok()函数得到一个key值,可用于非血缘关系进程间通信;
共享内存的操作步骤分为四步:
<1>创建共享内存
int id = shmget(key, MAXSZ, IPC_CREAT|IPC_EXCL); key值为IPC_PRIVATE=0时为血缘关系进程间通信;MAXSZ为申请物理内存空间大小,以PAGE_SIZE=4KB为基本单位;IPC_CREAT表示如果共享内存不存在则创建一个共享内存,否则直接打开已存在的,返回其ID;IPC_EXCL只有在共享内存不存在的时候,新的共享内存才建立,否则若是存在,shmget调用失败,并设置EEXITST错误码;
<2>映射共享内存,即把指定的共享内存映射到多个进程的地址空间,方便进程的访问;
char *shmp=shmat(id, NULL, 0); 后续就可以直接操作shmp这个指针,可用其他数据类型设置指针空间数据类型;
<3>撤销共享内存的映射
shmdt(shmp)
<4>删除共享内存
shmctl(id, IPC_RMID, NULL)
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/shm.h>
#define MAXSZ 4096
int main()
{
key_t key = ftok("./test.c", 10);
if(key<0)
{
perror("ftok");
exit(-1);
}
int id = shmget(key, MAXSZ, IPC_CREAT|IPC_EXCL);
if(id<0)
{
if(EEXIST != errno)
{
perror("shmget");
exit(-1);
}
}
char *shmp=shmat(id, NULL, 0);
if((void*)-1 == shmp)
{
perror("shmat");
exit(-1);
}
memset(shmp, 0, MAXSZ);
strcpy(shmp,"hello world");
printf("shmp=%s\n", shmp);
pid_t pid = fork();
if(0 > pid)
{
perror("fork");
exit(-1);
}
else if(0 == pid)
{
int k = 10;
while(k--)
{
sprintf(shmp,"hello world %d",k);
sleep(1);
}
}
else
{
sleep(1);
int k = 10;
while(k--)
{
printf("shmp %d=%s\n",k, shmp);
sleep(1);
}
wait(NULL);
if(-1 == shmdt(shmp))
{
perror("shmdt");
}
if(0 > shmctl(id, IPC_RMID, NULL))
{
perror("shmctl");
}
}
exit(0);
}
查看共享内存命令:ipcs