简介
进程之间的通信包括多种:管道(有名管道,无名管道),
信号,文件IO,共享内存,消息队列,信号量,socket。
以下介绍的是共享内存,消息队列和信号量,这是三种
常用的进程间的通信方式。
共享内存
共享内存,多个进程同时使用一片内存
1.共享内存的创建,或者获取一片共享内存
int shmget(key_t key, size_t size, int shmflg);
shmflg可以有多种宏值,可以实现不同的操作,但是通常只需要两个操作
创建一个共享内存 或者获取一片共享内存。
使用示例
shmid = shmget(0x12345678, 1000, IPC_CREAT | 0666);
如果共享内存存在,则会返回共享内存的id,如果不存在就会创建一片新的共享内存,并返回ID
shmget(0x12345678, 0, 0);
获得key为0x12345678的共享内存ID, 如果不存在就会出错返回,同时设置errno值
2.共享内存属性设置
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
struct shmid_ds结构体
1 | struct shmid_ds { |
通过cmd的值来决定不同的操作类型
IPC_STAT
获得原共享内存的额信息
IPC_SET
设置共享内存的信息,通过传入shmid_ds解构体。
一般来说,先要获取原来的共享内存的信息,然后再更改需要更改的部分
IPC_RMID
删除共享内存
3.将ID转为成地址
将ID转换成进程空间内的地址,不同的进程调用的值一般不会一样
void shmat(int shmid, void shmaddr, int shmflg);
使用示例
shmaddr = shmat(shmid, NULL, 0);
使用id将共享内存映射到本进程空间,并返回地址
4.结束使用
int shmdt(void *shmddr);
使用示例
shmdt(shmaddr);
消息队列
消息队列属于一种异步的通信方式,发送方不用管消息是否被正确接受
同时,发送的消息只能被一个进程接受,类似管道,接受一份就没了。
无论是消息的发送还是消息的接收都是原子操作。
1.可能存在多个进程向同一个消息队列发送消息,防止队列满
2.可能存在多个进程同时接收一个消息队列的消息。
消息队列并不是单单一个队列,它有多个队列,无论是发送还是接收都要指定type值
1 消息队列的创建,或者获得一个已经存在的消息队列
int msgget(key_t key, int msgflg);
使用示例
msgid = msgget(key, IPC_CREAT | 0666);
如果消息队列存在就返回其ID,如果不存在就创建一个新的消息队列
msgid = msgget(key, 0);
2 消息队列的设置
int msgctl(int msgid, int cmd, struct msgqid_ds *buf);
从函数可以看出,如果设置消息队列的属性,主要是通过结构体来完成。
结构体的构成
1 | struct msqid_ds { |
根据cmd的值来决定哪种操作
以下是几种常见操作:
IPC_STAT
IPC_STAT很明显是用来获取消息队列的信息。
IPC_RMID
IPC_RMID是删除一个消息队列
IPC_SET
IPC_SET是用来设置消息队列的信息,但是一般,只需要更改部分信息,
而并不需要改动其他信息,所以应该先执行STAT,来获得原来的信息,
然后更改需要更改的部分,这样才合理。
3 消息队列的发送和接收
int msgsnd(int msqid, const void msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void msgp, size_t msgsz, long msgtyp, int msgflg);
使用示例
发送:1
2
3
4
5
6
7
8
9typedef struct msg_buf{
long type;
char buf[1024];
}_msg_buf;
_msg_buf msg_buf;
memset(&msg_buf, 0x00, sizeof(msg_buf));
msg_buf.type = 1;
strcpy(msg_buf.buf, "hello,world");
msgsnd(msgqid, &msg_buf, strlen(msg_buf.buf), 0);
接收1
2
3
4
5
6
7
8typedef struct msg_buf{
long type;
char buf[1024];
}_msg_buf;
_msg_buf msg_buf;
memset(&msg_buf, 0x00, sizeof(msg_buf));
msg_buf.type = 1;
msgrcv(msgqid, &buf, 1024, 1, 0);
有上可以看出,无论是发送还是接收,都要指定type值,只有使用了相同
的type值才能正确接收
同时,接收的时候指定需要传入type值而不是在接收的结构体里面。
但是,type值可以为0或者负的,有特殊含义
由于接收放并不知道消息具体的大小,所以必须要使用最大值。如果接收的消息指定的大小比发送的消息要小
消息会截短,剩余的部分会丢失。
接收方的type值不同,意义不同
当type值为正,接收指定对列的消息
当type值为0,接收消息队列的第一条消息
当type值为负,接收消息队列的小于或等于type的绝对值的第一条
信号量
信号量代表的是一种信号的容量。
1 信号量的创建,或者获取一个已经存在的信号量
int semget(key_t key, int nsem, int semflg);
根据semflg来决定操作。
使用示例
semid = semget(0x12345678, 3, IPC_CREAT|0666);
如果信号量存在,返回ID,否则
创建信号量,改信号量有3个种信号量。
信号权限为666,并返回ID
semid = semget(0x12345678, 0, 0);
获得信号量的ID。
2 设置信号量的信息。
int semctl(int semid, int semnum, int cmd, …);
根据cmd的值来判定不同的操作类型。
主要使用两种参数
IPC_RMID
semctl(semid, 0, IPC_RMID);
SETVAL
semctl(semid, 2, SETVAL, 1);
将semid的信号量的第2号信号队列的值设置为1
3 信号量操作
int semop(int semid, struct sembuf *sops, unsigned nsops);
struct sembuf{
unsigned short sem_num;
short sem_op;
short sem_flg;
};
sem_num 指定哪号信号队列
sem_op 指定信号的操作
sem_flg 指定操作类型,一般为0
使用示例
1 | struct sembuf sem[2]; |
上面操作是同时对一个信号量的两个信号队列进行操作。
信号队列0的sem_op值为-1,所以是加锁操作。
信号队列1的sem_op的为1, 所以是解锁操作。