进程间通信 IPC对象
System V IPC :
这个东西真的要首先理解,因为好多书或者资料就直接说IPC就是 共享内存、消息队列、信号灯集,其实
IPC是一种机制,这种机制提供了进程间通信的通道,那么为什么加个System V呢,那是因为在System V
系统的四个版本中提出的进程通信的IPC这种机制。所以叫做System V IPC。
目前Linux也支持这三种机制:共享内存、消息队列、信号灯集
linux内核中定义了一个结构体:
struct ipc_perm
{
key_t key; 关键字
uid_t uid; /*共享内存所有者的有效用户ID */
gid_t gid; /* 共享内存所有者所属组的有效组ID*/
uid_t cuid; /* 共享内存创建 者的有效用户ID*/
gid_t cgid; /* 共享内存创建者所属组的有效组ID*/
unsigned short mode; /* Permissions + SHM_DEST和SHM_LOCKED标志*/
unsignedshort seq; /* 序列号*/
};
key比较关键,是识别这个共享通道的钥匙。
在这三种机制中都会用到这个创建的key;
一般用 ftok这个函数来创建钥匙。
key_t ftok( char * fname, int id )
例如:
if ((key = ftok(".", 's')) < 0)
{
perror("fail to ftok");
exit(-1);
}
这样就产生了一个key,其实也可以自己定义key,但是这样定义的key不安全。所以尽量用ftok来产生key。
下面具体讲解一下三种机制:
首先要提到的是共享内存,它是一种最为高效的进程间通信方式,进程可以直接读写内存,这个很猛,不需要任何数据的拷贝
为了在多个进程间交换信息,内核专门留出了一块内存区,可以有需要访问的进程将其映射到自己的私有地址空间,这种机制要
建立在绝对信任的基础上,否则,如果系统的一些重要信息放在这块内存区的话,可能会被利用。
进程不需要拷贝,那么句大大提高了效率,由于多个进程共享一段内存,所以也需要依靠某种同步机制,如互斥和信号量等。
函数所需头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
共享内存的实现步骤:
1.创建/打开共享内存
int shmget(key_t key,int size,int shmflg);
函数参数:
key:IPC_PRIVATE或ftok的返回值
size:共享内存的大小
shmflg:权限位
返回值:成功:共享内存段标识符
失败 -1;
2.映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
void * shmat(int shmid,const void * shmaddr,int shmflg);
函数参数: shmid: 要映射的共享内存去标识符
shmaddr:将共享内存映射到指定的地址(若为NULL,则表示由系统自动完成映射)
shmflg:SHM_RDONLY 共享内存只读
默认0:共享内存可读写
返回值: 成功,映射后的地址
失败 -1;
3.撤销共享内存映射
int shmdt(const void * shmaddr);
函数参数:shmaddr:共享内存映射后的地址
函数返回值: 成功 0,失败 -1
4.删除共享内存对象
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
函数参数: shmid:要操作的共享内存标识符
cmd: IPC_STAT(获取对象属性)
IPC_SET (设置对象属性)
IPC_RMID(删除对象)
buf:指定IPC_STAT/IPC_SET时用以保存/设置属性
返回值;成功 0,出错 -1;
/*****************************************write写端***************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define N 64
typedef struct
{
pid_t pid;
char buf[N];
} SHM;
void handler(int signo)
{
return;
}
int main()
{
pid_t pid;
key_t key;
int shmid;
SHM *shmadd;
if ((key = ftok(".", 's')) < 0)
{
perror("fail to ftok");
exit(-1);
}
signal(SIGUSR1, handler);
if ((shmid = shmget(key, sizeof(SHM), 0666|IPC_CREAT|IPC_EXCL)) < 0) // not first process
{
if (errno == EEXIST)
{
shmid = shmget(key, sizeof(SHM), 0666);
shmadd = (SHM *)shmat(shmid, NULL, 0);
pid = shmadd->pid;
shmadd->pid = getpid();
kill(pid, SIGUSR1);
}
else
{
perror("fail to shmget");
exit(-1);
}
}
else // first process
{
shmadd = (SHM *)shmat(shmid, NULL, 0);
shmadd->pid = getpid();
pause();
pid = shmadd->pid;
}
while ( 1 )
{
printf("write to shm : ");
fgets(shmadd->buf, N, stdin);
kill(pid, SIGUSR1);
if (strncmp(shmadd->buf, "quit\n", 5) == 0) break;
pause();
}
shmdt(shmadd);
if (shmctl(shmid, IPC_RMID, NULL) < 0)
{
perror("fail to shmctl");
}
return 0;
}
/******************************************read读端***************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define N 64
typedef struct
{
pid_t pid;
char buf[N];
} SHM;
void handler(int signo)
{
return;
}
int main()
{
pid_t pid;
key_t key;
int shmid;
SHM *shmadd;
if ((key = ftok(".", 's')) < 0)
{
perror("fail to ftok");
exit(-1);
}
signal(SIGUSR1, handler);
if ((shmid = shmget(key, sizeof(SHM), 0666|IPC_CREAT|IPC_EXCL)) < 0) // not first process
{
if (errno == EEXIST)
{
shmid = shmget(key, sizeof(SHM), 0666);
shmadd = (SHM *)shmat(shmid, NULL, 0);
pid = shmadd->pid;
shmadd->pid = getpid();
kill(pid, SIGUSR1);
}
else
{
perror("fail to shmget");
exit(-1);
}
}
else // first process
{
shmadd = (SHM *)shmat(shmid, NULL, 0);
shmadd->pid = getpid();
pause();
pid = shmadd->pid;
}
while ( 1 )
{
pause();
if (strncmp(shmadd->buf, "quit\n", 5) == 0) break;
printf("read from shm : ");
printf("%s", shmadd->buf);
kill(pid, SIGUSR1);
}
shmdt(shmadd);
return 0;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
消息队列:
消息队列是IPC对象的一种,由消息队列ID来唯一标识,消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息。
消息队列可以安装类型来发送/接收消息。
消息队列的操作包括:创建或者打开消息队列、添加消息、读取消息、控制消息
1.创建或者打开消息队列:
int msgget(key_t key,int flag);
函数参数: key key值
flag 消息队列访问权限
函数返回值: 成功 消息队列 ID,出错 -1;
创建的消息队列的数量会受到系统消息队列数量的限制
2.添加消息函数
int msgsnd(int msqid,const void * msgp,size_t size,int flag);
函数参数: msqid: 消息队列的ID
msgp: 指向消息的指针。常用的消息结构msgbuf如下:
struct msgbuf{
long mtype; //消息类型
char mtext[N];//消息正文
};
size : 发送的消息正文的字节数
flag : IPC_NOWAIT 消息没有发送完成函数也会立即返回。
0,直到发送完成函数才返回。
函数返回值: 成功 0,出错 -1;
3.读取消息函数
int msgrcv(int msgid,void *msgp,size_t size,long msgtype,int flag)
函数参数:
msqid: 消息队列的ID号
msgp :接收消息的缓冲区
size :要接收消息的字节数
msgtype:0:接收消息队列中第一个消息
大于0,接收消息队列中第一个类型为 msgtyp的消息
小于0,接收消息队列中类型值不小于msgtyp的绝对值且类型值又最小的消息
flag:0,若无消息,函数会一直阻塞
IPC_NOWAIT,若无消息,进程会立即返回ENOMSG
函数返回值: 成功,接收到的消息的长度。
失败 -1;
4.控制消息使用函数:
int msgctl(int msgqid,int cmd,struct msqid_ds *buf)
函数参数 :
msgqid 消息队列ID
cmd: IPC_STAT 读取消息队列的属性,并将其保存在buf执行的缓冲区中
IPC_SET 设置消息队列的属性,这个值取自buf参数
IPC_RMID 从系统中删除消息队列
buf: 消息队列缓冲区
函数返回值:成功 0,失败 -1;
例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define BFUSZ 512
#define TYPE 100
struct msgbuf{
long mtype;
char mtext[BFUSZ];
};
int main()
{
int qid,len;
key_t key;
struct msgbuf msg;
if( (key = ftok(".",'a'))== -1)
{
perror("ftok");
exit(-1);
}
if( (qid = msgget(key,IPC_CREAT|0666)) == -1)
{
perror("msgget");
exit(-1);
}
printf("opened queue%d\n",qid);
if( (fgets((&msg)->mtext,BFUSZ,stdin))==NULL)
{
puts("no message");
exit(-1);
}
msg.mtype = TYPE;
len = strlen(msg.mtext)+1;
if( msgsnd(qid,&msg,len,0)<0)
{
perror("msgsnd");
exit(-1);
}
if( msgrcv(qid,&msg,BFUSZ,0,0)<0)
{
perror("msgrcv");
exit(-1);
}
printf("message is: %s\n",(&msg)->mtext);
if(msgctl(qid,IPC_RMID,NULL)<0)
{
perror("msgctl");
exit(1);
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
信号灯 (semaphore) 也叫做信号量。它是不同进程间或同一进程内部不同线程间同步的机制。
信号灯种类:
posix有名信号灯
posix基于内存的信号灯(无名信号灯)
System V信号灯(IPC对象)
System V信号灯是一个或者多个信号灯的集合。其中的每一个都是耽误的计数信号灯。而Posix信号灯指的是单个信号灯
System V信号灯由内核维护。
主要函数semget,semop,semctl
int semget(key_t key , int nsems,int semflg);
函数参数: key:和信号灯集相关联的key值
nsems:信号灯集中包含的信号灯数目
semflg 信号灯集的访问权限 通常为IPC_CREAT|0666
函数返回值:成功 信号灯集ID
失败 -1;
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;
int semctl(int semid,int semnum,int cmd .../*union semun arg*/)
函数参数:
semid: 信号灯集 ID
semnum:要修改的信号灯编号
cmd:
GETVAL:获取信号灯的值
SETVAL:设置信号灯的值
IPC_RMID:从系统中删除信号灯集
函数返回值:成功 0,失败 -1;
例子:
/*********************************read**********************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
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) */
};
#define N 64
#define READ 0
#define WRITE 1
void sem_init(int semid, int array[], int n)
{
union semun myun;
int i;
for (i=0; i<n; i++)
{
myun.val = array[i];
semctl(semid, i, SETVAL, myun);
}
return;
}
void pv(int semid, int num, int op)
{
struct sembuf buf;
buf.sem_num = num;
buf.sem_op = op;
buf.sem_flg = 0;
if (semop(semid, &buf, 1) < 0)
{
exit(0);
}
return;
}
int main()
{
key_t key;
int shmid, semid;
char *shmaddr;
int array[] = {0, 1};
if ((key = ftok(".", 's')) < 0)
{
perror("fail to ftok");
exit(-1);
}
if ((semid = semget(key, 2, 0666|IPC_CREAT|IPC_EXCL)) < 0)
{
if (EEXIST == errno)
{
semid = semget(key, 2, 0666);
}
else
{
perror("fail to semget");
exit(-1);
}
}
else
{
sem_init(semid, array, 2);
}
if ((shmid = shmget(key, N, 0666|IPC_CREAT)) < 0)
{
perror("fail to shmget");
exit(-1);
}
shmaddr = (char *)shmat(shmid, NULL, 0);
while ( 1 )
{
pv(semid, READ, -1);
printf("read from shm : %s", shmaddr);
pv(semid, WRITE, 1);
}
return 0;
}
/********************************************write******************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
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) */
};
#define N 64
#define READ 0
#define WRITE 1
void sem_init(int semid, int array[], int n)
{
union semun myun;
int i;
for (i=0; i<n; i++)
{
myun.val = array[i];
semctl(semid, i, SETVAL, myun);
}
return;
}
void pv(int semid, int num, int op)
{
struct sembuf buf;
buf.sem_num = num;
buf.sem_op = op;
buf.sem_flg = 0;
semop(semid, &buf, 1);
return;
}
int main()
{
key_t key;
int shmid, semid;
char *shmaddr;
int array[] = {0, 1};
if ((key = ftok(".", 's')) < 0)
{
perror("fail to ftok");
exit(-1);
}
if ((semid = semget(key, 2, 0666|IPC_CREAT|IPC_EXCL)) < 0)
{
if (EEXIST == errno)
{
semid = semget(key, 2, 0666);
}
else
{
perror("fail to semget");
exit(-1);
}
}
else
{
sem_init(semid, array, 2);
}
if ((shmid = shmget(key, N, 0666|IPC_CREAT)) < 0)
{
perror("fail to shmget");
exit(-1);
}
shmaddr = (char *)shmat(shmid, NULL, 0);
while ( 1 )
{
pv(semid, WRITE, -1);
printf("write to shm : ");
fgets(shmaddr, N, stdin);
if (strncmp(shmaddr, "quit\n", 5) == 0) break;
pv(semid, READ, 1);
}
shmdt(shmaddr);
shmctl(shmid, IPC_RMID, NULL);
semctl(semid, 0, IPC_RMID);
return 0;
}