Linux进程间通讯----管道,消息队列,信号量,共享内存
Linux进程间通讯手段主要有:管道,消息队列,信号量,共享内存,不同网络之间之间通讯会使用socket。
管道:
管道主要通过文件系统的辅助来实现通讯,有两种方式:有名管道与无名管道。
有名管道在磁盘上有一个管道文件标识,但是其进行数据交互时,并不占用磁盘空间,仅在磁盘上存在一个文件i-node节点信息。
有名管道的创建:
mafifo filename或
int mkfifo(char* filename ,int modes)
有名管道的通讯:与普通文件操作相同
int fd=open(char* file ,int flag)
close() read() write().
管道与普通文件的不同:
管道文件open()会阻塞,至少有一对读写操作同时打开才会结束阻塞,
write()时,没有正在读取数据的操作也会向管道文件写数据,直到管道满阻塞;
read()时,会发生阻塞,直到有数据可读或所有写端关闭。
无名管道同样借助文件系统,但是不需要管道文件,依赖父子进程共享fork()之间打开的文件描述符。
无名管道的使用:
int pipe(int fds[2]) fds[0] :读操作 fds[1]:写操作(fork()之前创建)
操作和有名管道一样。
消息队列,信号量,共享内存
消息队列,信号量,共享内存三者的实现原理相同,都是借助于4G虚拟地址空间上1G的内核空间上的内核对象。
内核对象:存在于1G内核空间中,可以看成一种实体 对象 结构体变量 ,纪录着内核对象的属性。
不同进程通过指定某一个相同的用户标识,来访问同一个内核对象,获取一个独一无二的内核对象标识,用于之后的进程通讯。
消息队列:
消息:类型+数据(定向(消息类型)发送数据)
队列:先进先出 优先级队列
消息被进程读取,则队列中便不再保存此条信息
消息队列的使用:
int msgget((key_t)key,int flag)//获取或创建内核对象 成功返回内核对象的id,失败返回-1
int msgsnd(int msgid ,void *ptr,size-t size,int flag)//发送消息
其中 ptr指向一个结构体变量
struct MsgData
{
long mType;//数据
char mText[];//类型
}
*int msgrcv(int msgid,void ptr,size-t size,long type ,int flag)//接收消息
释放内核对象
*int msgctl(int msgid,intcmd,struct msgid-ds buff)
cmd:IPC_RMID 从系统中删除消息队列的内核对象,并且删除还在队列中的数据 (立即删除)
消息队列 的内核对象管理命令:
ipcs -q 查看消息队列
ipcrm -q id 删除消息队列
消息队列的使用:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/msg.h>
typedef struct MsgData
{
long mType;
char mText[128];
}MsgData;
//消息队列的发送
int main()
{
int msgid = msgget((key_t) 1234, 0664 | IPC_CREAT);
assert(-1 != msgid);
MsgData data;
data.mType = 100;
strcpy(data.mText, "hello");
msgsnd(msgid, &data, strlen(data.mText), 0);
data.mType = 200;
strcpy(data.mText, "world");
msgsnd(msgid, &data, strlen(data.mText), 0);
strcpy(data.mText, "hello");
msgsnd(msgid, &data, strlen(data.mText), 0);
//不能直接调用msgctl()删除消息队列 删除是立即删除
exit(0);
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/msg.h>
typedef struct MsgData
{
long mType;
char mText[128];
}MsgData;
int main()
{
int msgid = msgget((key_t) 1234, 0664 | IPC_CREAT);
assert(-1 != msgid);
MsgData data;
memset(&data, 0, sizeof(data));
msgrcv(msgid, &data, 127, 100, 0);
printf("data.type = %d\n", data.mType);
printf("data.text = %s\n", data.mText);
exit(0);
}
信号量
信号量:类似于一个计数器,>0时,纪录临界资源的个数,=0时 临界资源用完,信号量实现进程间访问临界资源控制,来实现进程的同步(协同工作)控制。
一个内核对象维护的是一个信号量集
信号量的获取或创建:
int semget((key_t) key ,int nsems ,int flag)
nsms只有在创建时才有效,用来指定信号量集中包含信号量的个数
信号量必须在创建时完成初始化
int semctl(int semid,int semnum,int cmd ,…)
cmd:SETVAL
信号量的PV操作
int semop(int semid,struct sembuf *buf,int len)
buf :数组 len:数组长度
struct sembuf
{
int sem_num;
short sem_op;操作方式 P -1 V+1
short sem_flag;
};
信号量集的删除(立即删除)
int semctl(int semid ,int semnum,int cmd)
cmd:IPC_RMID
semnum无效,即可传任意值。
信号量集的内核对象管理命令:
ipcs -s 查看消息队列
ipcrm -s d 删除消息队列
#ifndef __SEM_H
#define __SEM_H
typedef union SemUn
{
int val;
}SemUn;
int SemGet(int key, int semVal[], int nsems);
int SemP(int semid, int index[], int sems);
int SemV(int semid, int index[], int sems);
int SemDel(int semid);
#endif
#include "sem.h"
#include <sys/sem.h>
#include <malloc.h>
#include <string.h>
#include <assert.h>
int SemGet(int key, int semVal[], int nsems)
{
int semid = semget((key_t)key, nsems, 0664);
if(semid == -1)
{
semid = semget((key_t)key, nsems, 0664|IPC_CREAT);
assert(semid != -1);
int i = 0;
for(; i < nsems; ++i)
{
SemUn un;
un.val = semVal[i];
semctl(semid, i, SETVAL, un);
}
}
return semid;
}
int SemP(int semid, int index[], int sems)
{
struct sembuf *buf = (struct sembuf*)malloc(sizeof(struct sembuf) * sems);
assert(buf != NULL);
int i = 0;
for(; i < sems; ++i)
{
buf[i].sem_num = index[i];
buf[i].sem_op = -1; // P
buf[i].sem_flg = SEM_UNDO;
}
if(-1 == semop(semid, buf, sems))
{
free(buf);
return -1;
}
free(buf);
return 0;
}
int SemV(int semid, int index[], int sems)
{
struct sembuf *buf = (struct sembuf*)malloc(sizeof(struct sembuf) * sems);
assert(buf != NULL);
int i = 0;
for(; i < sems; ++i)
{
buf[i].sem_num = index[i];
buf[i].sem_op = 1; // P
buf[i].sem_flg = SEM_UNDO;
}
if(-1 == semop(semid, buf, sems))
{
free(buf);
return -1;
}
free(buf);
return 0;
}
int SemDel(int semid)
{
return semctl(semid, 0, IPC_RMID);
}
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include "sem.h"
int main()
{
int semVal = 1;
int semid = SemGet(1234, &semVal, 1);
printf("semid = %d\n", semid);
pid_t pid = fork();
assert(pid != -1);
if(pid == 0)
{
int i = 0;
for(; i < 10; ++i)
{
int index = 0;
SemP(semid, &index, 1);
printf("A\n");
sleep(1);
SemV(semid, &index, 1);
}
}
else
{
int i = 0;
for(; i < 10; ++i)
{
int index = 0;
SemP(semid, &index, 1);
printf("B\n");
sleep(1);
SemV(semid, &index, 1);
}
}
}
**共享内存:*最快的进程间通讯方式,共享内存是临界资源,所以必须使用信号量来控制进程同步
int shmget((key_t key)key,size_t size,int flag)size:共享内存的大小
使用shmget创建内核对象,内核对象创建完成后,内核对象在内存区域创建一块共享内存区域(物理内存);失败 返回-1 成功返回内核对象的id;
void shmat(int shmid,voidaddr,int flag) addr常为null
int shmdt(void shmptr)//断开链接,仅仅断开本进程指针与共享存储区域的连接
int shmctl(int shmid,int cmd ,struct shmid ds * buf)cmd:IPC_STAT IPC_SET IPC_RMID
对共享内存的内核对象的管理命令:
ipcs -m
ipcs -m id