IPC:inter-process communication
三种IPC:共享内存Shared memory,信号灯semophore、消息队列message queue。 有名管道可以无亲缘关系的进程间通信,是因为用户空间有管道这种文件类型。 而这三种IPC,用户空间是没有对应的文件类型的。ftok key可以保证用户空间的二个进程对同一个IPC对象进程操作。
这三中IPC对象的实现模型:
与文件IO操作的对应关系:
| 文件I/O | IPC |
|---|---|
| open | Msg_get Shm_get Sem_get |
| read | msgrecv shmat semop |
| write | msgsnd shmat semop |
| close | msgctrl shmctrl semctrl |
共享内存
共享内存创建后一直存在于内核中,直到被删除或系统关闭;
shmget函数
创建出的共享内存,一块缓存,类似于用户空间的数组或malloc 函数分配的空间一样。共享内存的创建shmget:
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
函数原型: int shmget(key_t key, int size, int shmflg)
函数参数: key:IPC_PRIVATE 或 ftok的返回值
size:共享内存区大小
shmflg:同open函数的权限位,也可以用8进制表示
返回: 成功:共享内存段标识符,ID,文件描述符, 失败:-1
查看IPC 共享内存对象命令:ipcs -m 删除IPC 共享内存对象命令:ipcrm -m id
#include "sys/types.h"
#include "sys/shm.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
int main()
{
int shmid;
shmid=shmget(IPC_PRIVATE,128,0777);
if(shmid <0)
{
printf("creat share memory failure\n");
return -1;
}
printf("creat share memory sucess shmid=%d\n",shmid);
system("ipcs -m");
// system("ipcrm -m shmid");
return 0;
}
ftok函数
使用shmget函数创建共享内存时,参数如果是IPC_PRIVATE,则共享内存的key值都一样为0。只能用于有亲缘关系进程之间的通信。 如果要使key非0,则需要借助函数ftok。 只要key值一样,用户空间的进程通过函数打开,就会对内核的同一个IPC对象进行操作。
ftok函数:
原型: char ftok(const char *path, char key)
参数: path:文件路劲和文件名
key:一个字符
返回值:成功返回一个key值,失败-1
shmat函数
创建了共享内存后需要将内存映射到用户空间,减少内核访问,映射函数为shmat
原型:void *shmat(int shmid, const void *shmaddr, int shmflg) //类似于malloc
参数: 第一个参数,ID号
第二个参数,映射到的地址,NULL为系统自动完成的映射
第三个参数,shmflg, SHM_RDONLY共享内存只读, 默认是0,表示可读写
返回: 成功映射后的地址, 失败NULL
#include "sys/types.h"
#include "sys/shm.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
int main()
{
int shmid;
int key;
char *p;
key=ftok("./a.c",'b');//通过a.c生成key值,a.c文件必须存在
if(key <0 )
{
printf("creat key fialure\n");
return -2;
}
printf("creat key sucess key=%X\n",key);
shmid=shmget(key,128,IPC_CREAT | 0777);
if(shmid <0)
{
printf("creat share memory failure\n");
return -1;
}
printf("creat share memory sucess shmid=%d\n",shmid);
system("ipcs -m");
p=(char *)shmat(shmid,NULL,0);
if(p == NULL)
{
printf("shmat function failure\n");
return -3;
}
//write share memory
fgets(p,128,stdin);
//start read share memory
printf("share memory data:%s",p);
printf("second read share memory data:%s",p);
// system("ipcrm -m shmid");
return 0;
}
shmdt函数
删除用户空间的进程中的地址映射shmdt
原型: int shmdt(const void *shmaddr)
参数: shmaddr共享内存映射后的地址
返回: 成功0,出错-1
shmctl函数
删除内核中的共享内存对象shmctl
原型: int shmctl(int shmid, int cmd, struct shmid_ds * buf)
参数: shmid,要操作的共享内存标识符
cmd: IPC_STAT,获取对象属性值 //实现了命令ipcs -m
IPC_SET,设置对象属性
IPC_RMID,删除对象 //实现了命令ipcrm -m
返回: 成功0,出错-1
#include "sys/types.h"
#include "sys/shm.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
int main()
{
int shmid;
int key;
char *p;
key=ftok("./a.c",'b');
if(key <0 )
{
printf("creat key fialure\n");
return -2;
}
printf("creat key sucess key=%X\n",key);
shmid=shmget(key,128,IPC_CREAT | 0777);
if(shmid <0)
{
printf("creat share memory failure\n");
return -1;
}
printf("creat share memory sucess shmid=%d\n",shmid);
system("ipcs -m");
p=(char *)shmat(shmid,NULL,0);
if(p == NULL)
{
printf("shmat function failure\n");
return -3;
}
//write share memory
fgets(p,128,stdin);
//start read share memory
printf("share memory data:%s",p);
printf("second read share memory data:%s",p);
shmdt(p);
shmctl(shmid,IPC_RMID,NULL);
system("ipcs -m ");
return 0;
}
亲缘关系进程间通信示例
a. 父进程完成内存映射后,等待读取用户输入信息;当输入完成后,通过kill向子进程发送SIGUSR1信号告知有消息可读,同时父进程进入睡眠状态;
b. 子进程完成内存映射后,就进入休眠状态;当收到信号后,进程唤醒,并从共享内存中读取信息;读取完成后向父进程发送SIGUSR2信号,告知,信息被读取,你可以继续输入了。
#include "sys/shm.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
void myfun(int signum)
{
return ;
}
int main()
{
int shmid;
int key;
char *p;
int pid;
shmid=shmget(IPC_PRIVATE,128,IPC_CREAT | 0777);
if(shmid <0)
{
printf("creat share memory failure\n");
return -1;
}
printf("creat share memory sucess shmid=%d\n",shmid);
pid=fork();
if(pid > 0)//parent process
{
signal(SIGUSR2,myfun);
p=(char *)shmat(shmid,NULL,0);
if(p == NULL)
{
printf("parent process:shmat function failure\n");
return -3;
}
while(1)
{
//write share memory
printf("parent process start write share memory:\n");
fgets(p,128,stdin);
kill(pid,SIGUSR1);// child process read data
pause();// wait child process read
}
}
if(pid == 0)//child process
{
signal(SIGUSR1,myfun);
p=(char *)shmat(shmid,NULL,0);
if(p == NULL)
{
printf("child process shmat function failure\n");
return -3;
}
while(1)
{
pause();// wait parent process write
//start read share memory
printf("share memory data:%s",p);
kill(getppid(),SIGUSR2);
}
}
shmdt(p);
shmctl(shmid,IPC_RMID,NULL);
system("ipcs -m ");
return 0;
}
非亲缘关系进程
server.c
#include "sys/types.h"
#include "sys/shm.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
struct mybuf
{
int pid;
char buf[124];
};
void myfun(int signum)
{
return ;
}
int main()
{
int shmid;
int key;
struct mybuf *p;
int pid;
key=ftok("./a.c",'a');
if(key < 0)
{
printf("creat key failure\n");
return -1;
}
printf("creat key sucess\n");
shmid=shmget(key,128,IPC_CREAT | 0777);
if(shmid <0)
{
printf("creat share memory failure\n");
return -1;
}
printf("creat share memory sucess shmid=%d\n",shmid);
signal(SIGUSR2,myfun);
p=(struct mybuf *)shmat(shmid,NULL,0);
if(p == NULL)
{
printf("parent process:shmat function failure\n");
return -3;
}
//get client pid
p->pid=getpid();//write server pid to share memory
pause();//wait client read server pid;
pid=p->pid;
//write
while(1)
{
//write share memory
printf("parent process start write share memory:\n");
fgets(p->buf,128,stdin);
kill(pid,SIGUSR1);// client process read data
pause();// wait client process read
}
shmdt(p);
shmctl(shmid,IPC_RMID,NULL);
system("ipcs -m ");
return 0;
}
client.c
#include "sys/types.h"
#include "sys/shm.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
struct mybuf
{
int pid;
char buf[124];
};
void myfun(int signum)
{
return ;
}
int main()
{
int shmid;
int key;
struct mybuf *p;
int pid;
key=ftok("./a.c",'a');
if(key < 0)
{
printf("creat key failure\n");
return -1;
}
printf("creat key sucess\n");
shmid=shmget(key,128,IPC_CREAT | 0777);
if(shmid <0)
{
printf("creat share memory failure\n");
return -1;
}
printf("creat share memory sucess shmid=%d\n",shmid);
signal(SIGUSR1,myfun);
p=(struct mybuf *)shmat(shmid,NULL,0);
if(p == NULL)
{
printf("parent process:shmat function failure\n");
return -3;
}
//get server pid
//read share memory
pid=p->pid;
//write client pid to share memory
p->pid=getpid();
//kill signal
kill(pid,SIGUSR2);
//client start read data from share memory
while(1)
{
pause();//wait server write data to share memory;
printf("client process receve data from share memory:%s",p->buf);//read data
kill(pid,SIGUSR2);//server may write share memory
}
shmdt(p);
shmctl(shmid,IPC_RMID,NULL);
system("ipcs -m ");
return 0;
}
消息队列
消息队列是链式的,消息的类型可以不一致。
消息队列中维护队列的结构体msqid_ds,其中的第一个消息指针msg_first,最后一个指针msg_last..
每个消息中包含数据data,数据的长度length,数据的类型type。
常用消息结构msgbuf如下:
struct msgbuf{
long mtype; //消息类型
car mtext[N]; //消息正文
}
msgget函数
创建了消息队列后可以通过system("ipcs -q")查看队列信息 消息队列创建函数msgget:
头文件: #include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数原型: int msgget(key_t key, int flag);
参数: key,和消息队列关联的key值
flag,消息队列的访问权限
返回: 成功,消息队列ID,出错:-1
msgsnd
函数原型: int msgsnd(int msqid, const void *msgp, size_t size, int flag)
参数: msqid,消息队列ID
msgp,指向消息的指针
size,发送的消息正文字节数
flag,IPC_NOWAIT,消息没有发送完函数也会立即返回0,直到发送完函数才返回
返回:成功0,出错-1
接收消息函数msgrcv
函数原型: int msgrcv(int msgid, void *msgp, size_t size, long msgtype, int flag)
参数: msgid,消息队列号
msgp,接收消息的缓冲区
size,要接收的消息字节数
msgtype,0:接收消息队列中第一个消息
>0,接收消息队列中第一个类型为msgtype的消息
<0,接收消息队列中类型值不大于msgtype的绝对值切类型值又最小的消息
flag, 0:若无消息函数一直阻塞
IPC_NOWAIT,若没消息,进程立即返回ENOMSG
返回: 成功,接收到的消息长度,出错-1
消息队列控制函数msgctl
原型: int msgctl(int msgqid, int cmd, struct msgqid_ds *buf)
参数: msgid,消息队列ID
cmd,IPC_STAT,读取消息队列的属性,并将其保存在buf指向的缓冲区
IPC_SET,设置消息队列属性,这个值取自buf参数
IPC_RMID,从系统中删除消息队列
buf,消息队列缓冲区
返回: 成功0,,出错-1
示例
#include "sys/types.h"
#include "sys/msg.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
struct msgbuf
{
long type;
char voltage[124];
char ID[4];
};
int main()
{
int msgid;
int readret;
struct msgbuf sendbuf,recvbuf;
msgid=msgget(IPC_PRIVATE,0777);
if(msgid <0)
{
printf("creat message queue failure\n");
return -1;
}
printf("creat message queue sucess msgid=%d\n",msgid);
system("ipcs -q");
//init sendbuf
sendbuf.type=100;
printf("please input message:\n");
fgets(sendbuf.voltage,124,stdin);
//start write message to message queue
msgsnd(msgid,(void *)&sendbuf,strlen(sendbuf.voltage),0);
//start read message from message queue
memset(recvbuf.voltage,0,124);
readret=msgrcv(msgid,(void *)&recvbuf,124,100,0);
printf("recv:%s",recvbuf.voltage);
printf("readret=%d\n",readret);
//second read message queue
msgrcv(msgid,(void *)&recvbuf,124,100,0);
printf("second read after\n");
//delete message queue
msgctl(msgid,IPC_RMID,NULL);
system("ipcs -q");
return 0;
}
信号灯
信号灯的IPC对象是一个信号灯集合,包含了多个信号量,多有的函数都是对一个集合的操作。
POSIX 信号量 与 IPC 信号灯之间比较
| 功能 | POSIX信号量 | IPC信号灯 |
|---|---|---|
| 定义 | sem_t sem1 | semget |
| 初始化 | sem_init | semctl |
| P操作 | sem_wait | semop |
| V操作 | sem_post | semop |
semget函数
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
函数原型:int semget(key_t key, int nsems, int semflg)
参数:
key:和信号灯关联的key值
nsems:信号灯集合包含的信号灯数量
semflg:信号灯集合访问权限
返回:成功:信号灯集ID,出错-1
semop函数
函数原型:int semop(int semid, struct sembuf *opsptr, size_t nops)
函数参数:
semid:信号灯集ID
struct sembuf{
short sem_num;//要操作的信号灯编号
short sem_op;//0,,等待,直到信号灯的值变成0. //1:释放资源,V操作。 //-1:分配资源,P操作
short sem_flg;// 0,IPC_NOWAIT, SEM_UNDO
};
nops:要操作的信号灯个数
返回:成功0,出错-1
semctl函数
原型:int semctl(int semid, int semnum, int cmd, ...union semun arg)
参数:semid 信号灯集合ID
semnum:要修改的信号灯编号
cmd:GETVAL获取信号灯的值
SETVAL设置信号灯的值
IPC_RMID从系统中删除信号灯集合
返回:成功0,出错-1
示例
线程使用信号灯同步
#include "stdio.h"
#include "stdlib.h"
#include "pthread.h"
//#include "semaphore.h"
#include "sys/ipc.h"
#include "sys/sem.h"
//sem_t sem;
union semun
{
int val; /* Value for SETVAL*/
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO(Linux-specific) */
};
int semid;
union semun mysemun;
struct sembuf mysembuf;
void *fun(void *var)//child thread code
{
int j;
//p wait
// sem_wait(&sem); //sleep
mysembuf.sem_op=-1;
semop(semid,&mysembuf,1);
for(j=0;j<10;j++)//second
{
usleep(100);
printf("this is fun j=%d\n",j);
}
}
int main()//main thread code
{
int i;
char str[]="hello linux\n";
pthread_t tid;
int ret;
semid=semget(IPC_PRIVATE,3,0777);
if(semid < 0)
{
printf("creat semaphore failure\n");
return -1;
}
printf("creat semaphore sucess,semid=%d\n",semid);
system("ipcs -s");
mysemun.val=0;
semctl(semid,0,SETVAL,mysemun);
//sem_init(&sem,0,0);//sem=0
mysembuf.sem_num=0;
mysembuf.sem_flg=0;
ret=pthread_create(&tid,NULL,fun,(void *)str);
if(ret<0)
{
printf("creat thread failure\n");
return -1;
}
for(i=0;i<10;i++)//first
{
usleep(100);
printf("this is main fun i=%d\n",i);
}
//v
// sem_post(&sem);
mysembuf.sem_op=1;
semop(semid,&mysembuf,1);
while(1);
return 0;
}