进程间通信(IPC)详解

目录

进程间通信的目的:

进程间通信发展

进程间通信分类 

管道:

命名管道

system V 共享内存


前言:

什么是进程间通信?顾名思义,就是进程之间的通信。

进程间通信介绍:

进程间通信的目的:

  1. 数据传输:一个进程需要将它的数据发送给另一个进程
  2. 资源共享:多个进程之间共享同样的资源。
  3. 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
  4. 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

进程间通信发展

管道
System V进程间通信
POSIX进程间通信

进程间通信分类 

管道
        匿名管道pipe
        命名管道
System V IPC
        System V 消息队列
        System V 共享内存(本文主讲)
        System V 信号量
POSIX IPC(本文不讲)
        消息队列
        共享内存
        信号量
        互斥量
        条件变量
        读写锁

管道:

单通道,将一个进程与另外一个进程进行链接

 匿名管道:

#include <unistd.h>
功能:创建一无名管道
原型
int pipe(int fd[2]);
参数
fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
返回值:成功返回0,失败返回错误代码

从父子进程间来理解管道通信:

父子进程都具有文件描述符数组。在通信的时候,只需要关闭其对应不要的文件描述符即可!

实例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>

int main()
{
    int pipefd[2] = {0};  //文件描述符数组
    int n = pipe(pipefd);  //创建匿名管道
    assert(n != -1) //保险起见,加一个断言。
    (void)n;
}

此时,匿名管道就已经创建好了。不过匿名管道的通信,只适用于父子间的通信

此时再创建子进程,两者就可以相互通信了。

pid_t id = fork();
if(id == 0) //子进程
{
    //关闭写文件
    close(pipefd[1]);
    while(true)
    {
        //设立缓冲区
        char buffer[128];
        //读取信息
        size_t size = read(pipefd[0],buffer,sizeof(buffer));
        buffer[size] = 0; //以 /0结尾
        //将读取到的信息 进行打印
        printf("%s\n",buffer);
    }
}

//父进程
close(pipefd[0]);
while(true)
{
    char buffer[1024];
    cin>>buffer;
    write(pipefd[1],buffer,strlen(buffer));
}

运行结果:

命名管道

匿名管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信

如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。

命名管道可以从命令行上创建,命令行方法是使用下面这个命令:

mkfifo filename

命名管道也可以从程序里创建,相关函数有:

int mkfifo(const char *filename,mode_t mode);

匿名管道与命名管道的区别:

匿名管道由pipe函数创建并打开。
命名管道由mkfifo函数创建,打开用open
FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义。

实例:

进程1:

int main()
{
    // 1.创建管道文件
    if(mkfifo(ipcPath.c_str(),MODE))
    {
        perror("mkfifo");
        exit(1);
    }
    //2.创建缓冲区
    char buffer[1024];
    
    //3.打开管道文件
    int fd = open(filename,O_RDONLY);
    //4.进行进程间通信
    while(true)
    {
        read(fd,buffer,sizeof(buffer)-1);
        printf("%s\n",buffer);
    }
    close(fd)
    return 0;
}

进程2:

int main()
{
    //因为上一个进程已经创建好了管道文件,故这个进程不需要再创建管道文件。
    int fd = open(filename,mode);
    //上一个进程是选择读,那么这个进程选择写
    char buffer[1024];
    while(true)
    {    
        cin>>buffer;
        write(fd,buffer,sizeof(buffer));
    }
    close(fd);
    return 0;
}

进程1和进程2之间可以进行通信了。

system V 共享内存

 

不同进程使用同一块共享内存~

共享内存的几个函数讲解:

1、shmget

 #include <sys/ipc.h>
 #include <sys/shm.h>

 //获取共享内存
 int shmget(key_t key, size_t size, int shmflg);

参数分析:
key 是作为分配共享内存的唯一标识! 每一块共享内存都有唯一的key值
而这个key值 可以通过函数 ftok(const char *pathname, int proj_id)进行产生
size:共享内存的大小
shmflg: IPC_CREAT IPC_EXCL
当只有IPC_CREAT选项打开时,不管是否已存在该块共享内存,则都返回该共享内存的ID,若不存在则创建共享内存

当只有IPC_EXCL选项打开时,不管有没有该快共享内存,shmget()都返回-1

所以当IPC_CREAT | IPC_EXCL时, 如果没有该块共享内存,则创建,并返回共享内存ID。
                            若已有该块共享内存,则返回-1;

2.shmat

void *shmat(int shmid,const void *shmaddr,int shmflg);
功能:将共享内存段连接到进程地址空间
参数分析:
shmid 是shmget返回的标识符!
shmaddr 有三种情况:
1.如果shmaddr 是NULL,系统将自动选择一个合适的地址!   
2.如果shmaddr 不是NULL 并且没有指定SHM_RND则此段连接到addr所指定的地址上。 
3.如果shmaddr非0 并且指定了SHM_RND 则此段连接到shmaddr -(shmaddr mod SHMLAB)所表示的地址上。这里解释一下SHM_RND命令,它的意思是取整,而SHMLAB的意思是低边界地址的倍数,它总是2的乘方,该算式是将地址向下取最近一个SHMLAB的倍数。 除非只计划在一种硬件上运行应用程序(在现在是不太可能的),否则不用指定共享段所连接到的地址。
!!!!所以一般指定shmaddr为0,以便由内核选择地址。

shmflg:
如果在flag中指定了SHM_RDONLY位,则以只读方式连接此段,否则以读写的方式连接此段。

shmat 如果挂起失败,则返回-1.

3.shmdt函数

int shmdt(const void *shmaddr);
功能:将共享内存段与当前进程脱离
shmaddr:shmat的返回值
返回值:成功返回0;失败返回-1。
将共享内存段与当前进程脱离不等于删除共享内存段

4.shmctl函数

功能:用于控制共享内存
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
IPC_STAT 把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET  在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值
IPC_RMID 删除共享内存段

buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1

实战:

进程1:

int main()
{
    int key  = ftok(filename,proj_id);
    int shmid = shmget(key,2048,IPC_CREAT|IPC_EXCL|0666);
    char* shmaddr = shmat(shmid,nullptr,0);
    //shmaddr 其实相当于就是共享内存段的首地址。我们可以直接通过shmaddr进行操作共享内存的内容
    while(i<26)
    {
        addr[i] = 'A'+i;
        i++;
        addr[i] = 0;
        sleep(1);
    }
    return 0;
}
进程2
int main()
{
    int key  = ftok(filename,proj_id);
    int shmid = shmget(key,2048,0);//这里将不再创建共享内存,而是直接通过Key来找到共享内存块
    char* shmaddr = shmat(shmid,nullptr,0);
    //shmaddr 其实相当于就是共享内存段的首地址。我们可以直接通过shmaddr进行操作共享内存的内容
    printf("%s\n",shmaddr);
    shmdt(shmaddr);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值