坚志者,功名之主也。不惰者,众善之师也。——《抱朴子》
前面介绍的创建进程,且进程之间交换信息的方法是经过fork,exec传送打开文件,或者通过文件系统进行通信,有点繁杂和残缺。现在我们学习一种进程间相互通信的其他技术 –LINUX IPC继承了(System V IPC 和 基于 Socket IPC)
IPC的几种主要手段有:管道(无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等、其中后二者支持在不同主机上的两个进程IPC。
1.半双工,有固定的读、写端
2.只能在具有亲缘关系的进程间进行通信
3.管道中不储存数据,读取后消失
4.可以把它看成是一个特殊的文件,对他的读写可以使用read,write等函数,但它不是普通文件,并且不属于任何文件系统,只存在于内存之中。
#include
int pipe(int fd[2]); //返回值;若是成功返回0,失败返回1
当一个管道建立的时候,会返回俩个描述符: fd[0] 读打开
fd[1] 写打开
关闭管道即关闭两个文件描述符
在父进程创建子进程,两个进程之间IPC通信
#include
#include
#include
#include
#include
#include
int main()
{
int pid;
int fd[2];
char readBuf[200];
if(pipe(fd) == -1)
printf("creat pipe fail\r\n");
pid = fork();
if(pid < 0)
printf("creat fork fail\r\n");
else if (pid > 0){ //进入父进程
sleep(3); //让子进程先运行
printf("This is father fork\r\n");
close(fd[0]);
write(fd[1],"Hello this is test",strlen("Hello this is test"));
wait(NULL);
}
else{ //进入子进程
printf("This is child fork\r\n");
close(fd[1]); //关闭写通道准备读取数据
read(fd[0],readBuf,200);
printf("The rec is %s\r\n",readBuf);
exit(0);
}
return 0;
}
除了无名管道的功能外,他还允许无亲缘关系进程间的通信
#include
int mkfifo(const char *pathname,mode_t mode); //成功返回0,失败返回-1
mode参数和open参数相同,创建一个fifo可以一般文件I/O函数操作它
当打开一个FIFO时候
若没有指定O_NONBLIOCK(默认),只读open会阻塞到某个进程为写而打开此FIFO,类似只写open会阻塞到某个进程为读而打开
若指定了O_NONBLOCK,则只读open立即返回,而只写open将出错返回-1,如果没有进程已经为读而打开该FIFO,其errno置ENXIO
write.c
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
int fd;
char writeBuf[]={"This is a test\r\n"};
fd = open("./test",O_WRONLY);
if(fd < 0 ){
printf("Open test fail\r\n");
exit(-1);
}
int nums =5;
while(nums --)
{
write(fd,writeBuf,strlen(writeBuf));
sleep(1);
}
close(fd);
return 0;
}
read.c
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
char readBuf[100]={0};
if(mkfifo("./test",0600) < 0&& errno != EEXIST){
printf("Open pipe fail\r\n");
perror("Why: ");
}
int fd = open("test",O_RDONLY);
if(fd < 0){
printf("Opne fail\r\n");
exit(-1);
}
while(1){
int read_n = read(fd,readBuf,100);
if(read_n <= 0)
exit(-1);
printf("Read num is %d,the sec is %s\r\n",read_n,readBuf);
}
close(fd);
return 0;
}
消息队列是消息的链接表,存放在内核中,一个队列由一个标识符(即队列ID)来标识
消息队列克服了信号承载信号量少、管道只能承载无格式字节流以及缓冲区大小受限的缺点
#include
#include
#include
key_t ftok(char *pathname,char proj); //获取键值
pathname 为文件名 proj为项目名
int msgget(key_t key int msgflg); //打开/创建一个消息队列
key为键值 msgflg为标志位
msgflg: IPC_CREAT :创建一个新的消息队列
IPC_EXCL和IPC_CREAT一同使用,表示如果要创建的消息队列已经存在,返回错误
IPC_NOWAIT表示读写消息队列要求无法满足的时候,不阻塞
int msgsnd(int msqid,struct msgbuf*msgp,int msgsz,int msgflg); //向消息队列发送一条消息
msqid:为打开的消息队列ID;
msgp:为存放消息的结构;
msgflg:为发送标志,
int msgrcv(int msqid,struct msgbuf*msgp,int msgsz,long msgtyp,int msgflg); //接收消息,成功返回0,失败返回-1
打开msqid的消息队列,把消息存放在msgp指向的msgbuf结构中,读取成功后这条消息会被删除
int msgctl(int msqid, int cmd, struct msqid_ds *buf); //消息控制
msgwrite.c
#include
#include
#include
#include
#include
#include
struct msgbuf{
long mtype;
char mtext[128];
};
int main()
{
struct msgbuf readbuf;
struct msgbuf sendbuf = {
888,
"This is a test from msgwrite"
};
key_t key;
key = ftok(".",'z');
if(key < 0){
printf("ftok error \r\n" );
exit(-1);
}
printf("The key num is %d\r\n",key);
int msgid = msgget(key,IPC_CREAT|0777);
if(msgid < 0){
printf("get error\r\n");
exit(-1);
}
msgsnd(msgid,&sendbuf,strlen(sendbuf.mtext),0);
msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),988,0);
printf("The rec is %s\n",readbuf.mtext);
return 0;
}
msgread.c
#include
#include
#include
#include
struct msgbuf{
long mtype;
char mtext[256];
};
int main()
{
int msqid;
key_t key;
struct msgbuf readbuf;
struct msgbuf sendbuf = {
988,
"Have read your send"
};
key = ftok(".",'z');
if(key < 0){
printf("ftok error\r\n");
exit(-1);
}
printf("Message Queue - Server key is: %d.\n", key);
if ((msqid = msgget(key, IPC_CREAT|0777)) == -1)
{
perror("msgget error");
exit(1);
}
msgrcv(msqid,&readbuf,sizeof(readbuf.mtext),888,0);
msgsnd(msqid,&sendbuf,strlen(sendbuf.mtext),0);
printf("The sec is %s\r\n",readbuf.mtext);
return 0;
}
共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区
共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
因为多个进程可以同时操作,所以需要进行同步。
信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。
#include
int shmget(key_t key,size_t size,int flag); //共享内存的创建,成功时返回内存的ID,失败返回-1
int shmctl(int shm_id,int cmd,struct shmid_ds*buf); //对内存的操作
void *shmat(int shm_id,const void*addr,int flag); //共享内存段连接到本进程空间 成功返回指向内存的指针
int shmdt(void *addr); //解除内存
shmwrite.c
#include
#include
#include
#include
#include
#include
#include
int main()
{
key_t key;
key = ftok(".",'a');
if(key < 0){
printf("Ftok creat error\r\n");
exit(-1);
}
printf("key num is %d\n",key);
int shm_id = shmget(key,1024*4,IPC_CREAT|0666);
if(shm_id < 0){
printf("creat shmid fail\r\n");
exit(-1);
}
printf("shm_id is %d\r\n",shm_id);
char *shmaddr = shmat(shm_id,0,0);
if(shmaddr < 0){
printf("shmat creat fial\r\n");
exit(-1);
}
printf("shmat success!!!\r\n");
strcpy(shmaddr ,"This is a project");
sleep(10);
shmdt(shmaddr);
shmctl(shm_id,IPC_RMID,0);
printf("quit success !!!\r\n");
return 0;
}
shmread.c
#include
#include
#include
#include
#include
#include
#include
int main()
{
key_t key;
key = ftok(".",'a');
if(key < 0){
printf("Ftok creat error\r\n");
exit(-1);
}
printf("key num is %d\n",key);
int shm_id = shmget(key,1024*4,0);
if(shm_id < 0){
printf("creat shmid fail\r\n");
exit(-1);
}
printf("shm_id is %d\r\n",shm_id);
char *shmaddr = shmat(shm_id,0,0);
if(shmaddr < 0){
printf("shmat creat fial\r\n");
exit(-1);
}
printf("shmat success!!!\r\n");
printf("The net is %s\r\n",shmaddr);
shmdt(shmaddr);
printf("quit success !!!\r\n");
return 0;
}
将shmwrite.c中释放共享内存注释,利用 ipcs -m 指令观察当前的共享内存段
利用 ipcrm -m 共享内存的id号码 可以关闭共享内存
信号是进程在运行的过程中,有自身产生或进程外部发过来的,
用来通知进程发生了异步事件的通信机制,是硬件中断的软件模拟(软中断),是进程间通信机制中唯一的异步通信机制。
kill -l显示所有信号
信号处理方法: 忽略、捕捉、和默认动作
将传入的ctrl+c信号进行修改,让其执行设定好的函数
int sigaction(int signum,const struct sigaction*act,struct sigaction *oldact)
signum 是信号
act是struct sgaction指针
包括:1,sa_sigaction:指定要处理的函数
2,sa_flags:指定处理信号的方式
3,sa_mask:阻塞信号集
sigproread.c
#include
#include
#include
void handler (int signum ,siginfo_t *info,void *ucontext)
{
if(ucontext == NULL){
printf("NOT recevie sig\r\n");
exit(-1);
}
printf("Have rec is %d\n",signum);
printf("Send pid is %d\n",info->si_pid);
printf("The value is %d\n",info->si_value.sival_int);
printf("The data is %d\n",info->si_int);
}
int main()
{
struct sigaction act;
printf("Now forkpid is %d\r\n",getpid());
act.sa_sigaction = handler;
act.sa_flags = SA_SIGINFO;//可以发送数据
sigaction(SIGUSR1,&act,NULL);
while(1);
return 0;
}
sigprowrite.c
#include
#include
#include
#include
#include
int main(int argc ,char **argv)
{
int signum = atoi(argv[1]);
int pid = atoi(argv[2]);
printf("argv[1] = %s\n",argv[1]);
union sigval value;
value.sival_int = 666;
printf("The signum is %d\n",signum);
printf("Now forkpid is %d\n",getpid());
sigqueue(pid,signum,value);
printf("send success\r\n");
return 0;
}
信号量是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间的通信数据
用于进程间的永不,若在进程间要传递数据要结合共享内存实现
信号量基于操作系统的PV操作,程序对信号量的操作都是原子操作
支持信号量组
#include
int semget(key_t key,int num_sems,int sem_flags);//创建/获取一个信号量组,成功返回信号量集的ID,失败返回-1
int semop(int semid,struct sembuf semoparray[].size_t numops);//对信号量组进行操作,改变信号量的值;成功返回0,失败返回-1
int semctl(int semid ,int sem_num,itn cmd, ...); // 控制信号量的相关信息
先让子进程释放信号量,再让父进程运行
#include
#include
#include
#include
#include
#include
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *_buf;
};
void Getkey(int id)
{
struct sembuf so;
so.sem_num =0;
so.sem_op = -1;
so.sem_flg = SEM_UNDO;
if(semop(id,&so,1) < 0){
printf("Get error\r\n");
exit(-1);
}
printf("Get succes\r\n");
}
void Putkey(int id)
{
struct sembuf so;
so.sem_num =0;
so.sem_op = 1;
so.sem_flg = SEM_UNDO;
if(semop(id,&so,1) < 0){
printf("Put error\r\n");
exit(-1);
}
printf("Put succes\r\n");
}
int main(int argc,char **argv)
{
int semid;
key_t key ;
key = ftok(".",1);
if(key < 0){
printf("ftok create fail \r\n");
exit(-1);
}
semid = semget(key,1,IPC_CREAT|0666);
if(semid < 0)
{
printf("smeid creat error\r\n");
exit(-1);
}
union semun initsem;
initsem.val = 0;
semctl(semid,0,SETVAL,initsem);
int pid;
pid = fork();
if(pid > 0){
Getkey(semid);
printf("This is father fork\r\n");
Putkey(semid);
}
else if(pid == 0){
printf("This is child fork\r\n");
Putkey(semid);
}
else
{
printf("fork creat error\r\n");
exit(-1);
}
return 0;
}