Linux 进程间通信(一)

前言

进程间通信(简称IPC即InterProcess Communication),简单来说,就是两个进程之间的数据交换。
在进程这一小节,讲过通过exit函数,使当前进程退出并向父进程传递退出状态,父进程再通过wait函数收集终止状态。不过这只能局限于父、子进程,且不能实现子进程运行过程中的数据交换。我们要用到IPC来实现任何两个进程之间的数据交换。
进程间通信方式一般有以下几种:
1、匿名管道(仅支持同一主机的IPC)
2、命名管道:FIFO(仅支持同一主机的IPC)
3、消息队列(仅支持同一主机的IPC)
4、共享内存(仅支持同一主机的IPC)
5、信号(仅支持同一主机的IPC)
6、信号量(仅支持同一主机的IPC)
7、Scocket(支持同一主机和不同主机的IPC)
8、Stream(支持同一主机和不同主机的IPC)

API介绍

一、管道
管道,通常是指无名管道,是UNIX系统IPC最古老的形式。
1、特点
(1)它是半双工(即数据只能在一个方向上流动),具有固定的读端和写端。
(2)它只能用于具有亲缘关系的进程之间的通信。(父子进程或者兄弟进程)
(3)它可以看成一种特殊的文件,对于它的读写也可以使用普通的read、write等函数,但是它不是普通的文件,并不属于其他任何的文件系统,并且只存在于内存中。
功能:创建一个管道,同时会创建两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。
函数原型:

#include <unistd.h>
int pipe(int fd[2]);
参数:
fd[0]为读端,fd[1]为写端。
返回值:
若成功返回0,失败返回-1。

关闭管道只需将这两个文件描述关闭即可。
在这里插入图片描述

例程:创建父进程与子进程之间的管道,父进程打开写端fd[1],关闭读端fd[0],往管道里写入数据;子进程打开读端fd[0],关闭写端fd[1],从管道里读取数据。
在这里插入图片描述

#include <unistd.h>
#include <stdio.h>
int main()
{
        int fd[2];
        pid_t pid;
        char ret[1024];
        char buf[1024];
		//函数原型:int pipe(int fd[2]);
        if(pipe(fd)==-1)
        {
                printf("create pipe fail!\n");
                return -1;
        }
        if((pid=fork())<0)
        {
                printf("new process creat fail!\n");
        }
        else if(pid>0)
        {
                while(1)
                {
                        close(fd[0]);
                        printf("please input str to pipe:\n");
                        scanf("%s",ret);
                        write(fd[1],ret,sizeof(ret)/sizeof(char));
                }
        }
        else
        {
                while(1)
                {
                        close(fd[1]);
                        read(fd[0],buf,sizeof(buf)/sizeof(char));
                        printf("get str from pipe buf:%s\n",buf);
                }
        }
        return 0;
}

二、FIFO
FIFO,也称命名管道,它是一种文件类型。
1、特点
(1)FIFO可以在无关的进程之间交换数据,与无名管道不同。
(2)FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

函数原型:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
参数:
pathname:路径名
mode:权限
返回值:
若成功返回0,失败返回-1并设置参数errno。

其中的mode参数与open函数中的mode相同。一旦创建了一个FIFO,就可以用一般文件I/O函数操作它。
当open一个FIFO时,是否设置非阻塞标志(O_NONBLOCK)的区别:
1、若没有指定O_NONBLOCK(默认),只读open要阻塞到某个其他进程为写而打开此FIFO。类似的,只写open要阻塞到某个进程为读而打开它。
2、若指定了O_NONBLOCK,则只读open立即返回。而只写open将出错返回-1。如果没有进程已经为读而打开该FIFO,其errno置ENXIO。

例程:演示使用设置非阻塞标志(O_NONBLOCK)的FIFO进行IPC的过程

//文件read_fifo.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h> 
#include <errno.h>
int main()
{
        int fd; 
        char buf[1024]="";
        int nread=0;
        if((mkfifo("./file",0600)==-1)&&(errno!=EEXIST))
        {
                printf("mkfifo fail!\n");
                perror("why");
        }
        if((fd=open("./file",O_RDONLY))==-1)
        {
                printf("open fifo fail!\n");
        }
        printf("start read str!\n");
        while(1)
        { 
                nread=read(fd,buf,1024);
                sleep(1);
                if(nread==-1)
                {
                        printf("read fail!\n");
                }
                else
                {
                        printf("nread:%d bytes context:%s\n",nread,buf);
                }
                memset(buf,'\0',1024);
        }
        return 0;
}
//文件write_fifo.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
        int fd;
        char buf[1024]="hello linux!\n";
        int nwrite=0;
        if((mkfifo("./file",0600)==-1)&&(errno!=EEXIST))
        {
                printf("mkfifo fail!\n");
                perror("why");
        }
        if((fd=open("./file",O_WRONLY))==-1)
        {
                printf("open fifo fail!\n");
        }
        printf("start write str!\n");
        while(1)
        {
                nwrite=write(fd,buf,strlen(buf));
                sleep(1);
                if(nwrite==-1)
                {
                        printf("write fail!\n");
                }
        }
        return 0;
}

三、消息队列
消息队列是消息的链接表,存放在内核中,一个消息队列一个标识符(即队列ID)来标识。
1、特点
(1)消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
(2)消息队列独立于发送接收过程。进程终止时,消息队列及其内容并不会被删除。
(3)消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
2、函数原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
//创建或打开消息队列,成功返回队列ID,失败返回-1。
int msgget(key_t key, int msgflg);
//添加消息,成功返回0,失败返回-1
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
//读取消息,成功返回0,失败返回-1
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
//控制消息队列:成功返回0,失败返回-1
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

在以下两种情况,msgget将创建一个新的消息队列:
1、如果没有与键值key相对应的消息队列,并且flag中包含了IPC_CREAT标志位。
2、key参数为IPC_PRIVATE。

获取key值函数
函数原型:
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
参数:
pathname:指定的文件名,一般使用当前路径
proj_id:子序号
返回:
根据路径名和子序号生成的key值

例程:演示使用消息队列进行IPC的过程

//文件msgsnd.c
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
//消息结构
struct msgbuf { 
        long mtype;                  
        char mtext[1024];
};

int main()
{
        struct msgbuf Rcvmsg;
        memset(Rcvmsg.mtext,'\0',1024);
        struct msgbuf Sndmsg={888,"hello linux!"}; 
        //设置接收的消息类型和内容
        int msgid; 
        int size_rcv=0;
		//获取key值
        key_t key=ftok(".",'z');
		//打印key值
        printf("msg queue key is %d\n",key);
		//创建消息队列
        if((msgid=msgget(key,IPC_CREAT|0777))==-1)
        {
                printf("msg create fail!\n");
        }
        msgsnd(msgid,&Sndmsg,strlen(Sndmsg.mtext),0);
		//返回类型为887的第一个消息
        if((size_rcv=msgrcv(msgid,&Rcvmsg,sizeof(Rcvmsg.mtext),887,0))==-1)
        {
                perror("why");
                printf("msg rcv fail!\n");
        }
        printf("rcv bytes:%d context:%s\n",size_rcv,Rcvmsg.mtext);
		//移除消息队列
        msgctl(msgid, IPC_RMID, NULL);
        return 0;
}
//文件msgrcv.c
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
//消息结构
struct msgbuf { 
        long mtype;                  
        char mtext[1024];
};

int main()
{
        struct msgbuf Rcvmsg;
        memset(Rcvmsg.mtext,'\0',1024);
        struct msgbuf Sndmsg={887,"thank you!"};
        //设置接收的消息类型和内容
        int size_rcv=0;
        int msgid;
		//获取key值
        key_t key=ftok(".",'z');
		//打印key值
        printf("msg queue key is %d\n",key);
		//创建消息队列
        if((msgid=msgget(key,IPC_CREAT|0777))==-1)
        {
                printf("msg create fail!\n");
        }
		//返回类型为888的第一个消息
        if((size_rcv=msgrcv(msgid,&Rcvmsg,sizeof(Rcvmsg.mtext),888,0))==-1)
        {
                printf("msg rcv fail!\n");
                perror("why");
        }
        printf("rcv bytes:%d context:%s\n",size_rcv,Rcvmsg.mtext);
        msgsnd(msgid,&Sndmsg,strlen(Sndmsg.mtext),0);
		//移除消息队列
        msgctl(msgid, IPC_RMID, NULL);
        return 0;
}

四、共享内存
共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区。
1、特点
(1)共享内存是最快的一种IPC,因为进程是直接对内存进行存取。
(2)因为多个进程可以同时操作,所以需要进行同步。
2、函数原型

#include <sys/ipc.h>
#include <sys/shm.h>
//创建或获取一个共享内存,成功返回共享内存ID,失败返回-1
//size是以4KB为最小单位
int shmget(key_t key, size_t size, int shmflg);
//连接共享内存到当前进程的地址空间,成功返回指向内存的指针,失败则返回-1
//shmaddr为0时,内存自动分配共享空间,shmflg为0时,共享内存可读可写
void *shmat(int shmid,const void *shmaddr, int shmflg);
//断开与共享内存的连接,成功返回0,失败返回-1
int shmdt(const void *shmaddr);
//控制共享内存的相关信息,成功返回0,失败返回-1
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

指令:
ipcs -m 查看进程申请的共享内存
ipcrm -m 共享内存ID号 断开并释放与进程连接的共享存储

例程:演示使用共享内存进行IPC的过程
步骤:
1、创建或打开共享内存(shmget)
2、映射(shmat)
3、数据操作
4、释放共享内存(shmdt)
5、移除共享内存(shmctl)

//文件shmwrite.c
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
        int shmid;
        char* shmaddr;
        key_t key=ftok(".",'z');
        shmid=shmget(key,4*1024,IPC_CREAT|0666);
        if(shmid==-1)
        {
                printf("shmget fail\n");
                exit(-1);
        }
        shmaddr=shmat(shmid, 0, 0);
        strcpy(shmaddr,"hello linux!\n");
        sleep(5);
        shmdt(shmaddr);
        shmctl(shmid,IPC_RMID,0);
        return 0;
}   
//文件shmread.c
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
        int shmid;
        char* shmaddr;
        key_t key=ftok(".",'z');
        shmid=shmget(key,4*1024,IPC_CREAT|0666);
        if(shmid==-1)
        {
                printf("shmget fail\n");
                exit(-1);
        }
        shmaddr=shmat(shmid, 0, 0);
        printf("shmaddr:%s",shmaddr);
        shmdt(shmaddr);
        return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一盆电子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值