进程通信PIC部分知识汇总

进程间通信IPC

  • 多个进程借助一个内核传递数据。
进程间通信的方法
  1. 管道:最简单
  2. 信号:开销小
  3. mmap映射:速度快、非血缘关系间。

管道:

  • 实现原理:Linux内核使用环形队列机制,借助缓冲区实现。
  • 特质:
    1. 本质:伪文件(实为内核缓冲区)
    2. 用于进程通信,右;两个文件描述符引用,一个读端,一个写端。
    3. 规定,数据从管道写端流入,从读端流出。
  • 局限性:
    1. 自己写,不能自己读
    2. 管道中的数据,读走没!不能反复读取。
    3. 半双工通信。
使用的函数
#include               /* Obtain O_* constant definitions */
#include 

int pipe2(int pipefd[2], int flags);
fd[0]:管道读端
fd[1]:管道写端
返回值:
    成功:0
    失败-1 errno
  • 父子进程管道通信实例
#include
#include
#include
#include
#include
#include
#include
void sys_err(const char*str)
{
    perror(str);
    exit(1);
}


int main(int argc,char*argv[])
{
    int fd[2];
    int ret = pipe(fd);
    if(ret == -1)
    {
        sys_err("pipe error");
    }

    
    pid_t pid = fork();
    if(pid==0)
    {
        // sleep(2);
        char buf[4096] = {0};
        read(fd[0],buf,4096);
        printf("child read: %s\n",buf);
    }
    else if(pid > 0)
    {
        sleep(2);
        write(fd[1],"hello\n",6);
    }
    return 0;
}

管道的读写行为

  • 读管道

    1. 管道有数据,read返回实际读到的字节数

    2. 无数据 1)无写端,返回0(类似读到文件末尾)

      ​ 2)有写端,阻塞等待。

  • 写管道

    1. 无读端,异常终止(SIGPIPE信号)

    2. 有读端,1)管道已满,阻塞等待

      ​ 2)管道未满,返回实际写出的字节数

父子进程ls | wc -l

#include
#include
#include
#include
#include
#include
#include
void sys_err(const char*str)
{
    perror(str);
    exit(1);
}

int main(int argc,char*argv)
{
    pid_t pid;
    int fd[2];
    //先创建管道
    pipe(fd);
    pid = fork();

    if(pid==0) //子进程实现wc -l
    {
        close(fd[1]); //子进程读管道关闭写端
        dup2(fd[0],STDIN_FILENO);   //让wc从管道的读端,读数据
        execlp("wc","wc","-l",NULL);
    }
    else if(pid > 0)
    {
        close(fd[0]);  //父进程写关闭读端。
        // 讲写出到屏幕的ls结果,写入到管道写端
        dup2(fd[1],STDOUT_FILENO);
        execlp("ls","ls",NULL);
    }
    return 0;
}

兄弟进程 ls | wc -l

#include
#include
#include
#include
#include
#include
#include
#include

void sys_err(const char* str) {
    perror(str);
    exit(1);
}

int main(int argc, char* argv[]) {
    int i = 0;
    pid_t pid;
    int fd[2] = {0};
    pipe(fd);

    for(i = 0; i < 2; i++) {
        if ((pid = fork()) == 0) {
            break;
        }
    }

    if (i == 0) { // 兄
        close(fd[0]);
        dup2(fd[1], STDOUT_FILENO);
        execlp("ls", "ls", NULL);
    } else if (i == 1) { // 弟
        close(fd[1]);
        dup2(fd[0], STDIN_FILENO);
        execlp("wc", "wc", "-l", NULL);
    } else { // 父进程
        close(fd[0]);
        close(fd[1]);
        for (i = 0; i < 2; i++)
            wait(NULL);
    }

    return 0;
}

管道缓存区
#include 

long fpathconf(int fd, int name);
long pathconf(const char *path, int name);
_PC_PIPE_BUF 宏
管道的优劣
  • 优点:简单、比信号套接字、mmap简单
  • 缺点:
    • 只能单向通信,双向通信,两个管道。
    • 只能应用于父子兄弟(有公共属性)用fifo替代(无血缘的)
命名管道fifo
  • 命令创建:mkfifo
  • 函数创建:mkfifo
int mkfifo(const char*pathname,mode_t mode)
  • 可以用于无血缘关系进程间通信。

  • 读端和写端打开fifo管道方式不同。

  • 管道中的数据一次性读走。

mmap
文件进程间通信
  • ​ 有血缘关系、无血缘关系的进程,都可以使用同一个文件来实现进程通信。
建立映射
  • mmap借助文件映射,创建共享内存映射区
#include 

void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);

 	addr:指定映射首地址,设置为NULL,表示让系统自动分配。
    length:共享内存映射区的大小  <= 文件实际的大小
    prot:共享内存映射区的读写属性。PORT_READ|PORT_WRITE
    flags:标注共享内存映射区的共享属性:
        MAP_SHARED,对共享内存所作的修改,会反应到物理磁盘文件上。
        MAP_PRIVATE,不会反映到物理磁盘文件上。
    fd: 用来创建共享内存映射区的那个文件的文件描述符
    offset:默认0,表示映射文件全部!偏移位置必须是4k整数倍。
返回值:
        成功:映射区的首地址
        失败:MAP_FAILED(void*(-1)) errno
munmap 释放共享内存映射
int munmap(void *addr, size_t length);1mmap()函数的返回值
参2:共享内存映射区大小
返回值:
	  成功:0
      失败:-1
mmap使用注意事项
  1. 用于创建映射区的大小,必须是非0,映射区的大小<=文件大小.
  2. 创建映射区,需要read权限,指定访问权限为MAP_SHARED,mmap需要读写权限,应该<= 文件打开权限。
  3. 文件描述符fd,在mmap创建映射区完成就可以立即关闭。
  4. offset必须是4096的整数倍(MMU映射最小单位4k)
  5. 映射区访问的权限设置为MAP_PRIVATE,,对内存做的所有修改只在内存上生效,不能应用于IPC。
mmap函数保险调用方式
1、fd = open("文件名",O_RDWR);
2、mmap(NULL,有效文件大小,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
#include
#include
#include
#include
#include
#include
#include
#include 
void sys_err(const char*str)
{
    perror(str);
    exit(1);
}

int main(int argc,char*argv[])
{
    int fd = open("test.mmap",O_RDWR|O_CREAT|O_TRUNC,0664);

    ftruncate(fd,4);
    char* memp;
    memp = mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(memp == MAP_FAILED)
    {
        sys_err("mmap err");
    }
    strcpy(memp,"xxx"); //写操作 
    printf("%s\n",memp);

    if(munmap(memp,4) == -1)
    {
        sys_err("munmap err");
    }
    close(fd);
    return 0;
}

mmap_w.c

#include
#include
#include
#include
#include
#include
#include
#include 
void sys_err(const char*str)
{
    perror(str);
    exit(1);
}

struct student
{
    int id;
    char name[256];
    int age;
};
int main(int argc,char*argv[])
{
    struct student stu = {1,"xiaowang",18};
    int fd = open("testmap",O_RDWR|O_CREAT|O_TRUNC,0664);
    ftruncate(fd,sizeof(stu));
    struct student* p = mmap(NULL,sizeof(stu),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(p == MAP_FAILED)
    {
        sys_err("mmap error");
    }
    close(fd);

    while(1)
    {
        memcpy(p,&stu,sizeof(stu));
        stu.id++;
        sleep(2);
    }
    munmap(p,sizeof(stu));
    return 0;
}

mmap_r.c

#include
#include
#include
#include
#include
#include
#include
#include 
void sys_err(const char*str)
{
    perror(str);
    exit(1);
}

struct student
{
    int id;
    char name[256];
    int age;
};
int main(int argc,char*argv[])
{
    struct student stu;
    int fd = open("testmap",O_RDONLY);
    struct student* p = mmap(NULL,sizeof(stu),PROT_READ,MAP_SHARED,fd,0);
    if(p == MAP_FAILED)
    {
        sys_err("mmap error");
    }
    close(fd);

    while(1)
    {
        printf("id = %d, name = %s, age = %d\n",p->id,p->name,p->age);
        sleep(2);
    }
    munmap(p,sizeof(stu));
    return 0;
}

父子进程间通信
  1. 父进程,先创建 映射区。open(O_RDWR); mmap(MAP_SHARED);
  2. fork()创建子进程.
  3. 一个进程使用映射区读,另一个写

无血缘关系进程间通信:

  1. ​ 两个进程,打开同一个文件,创建映射区
  2. 指定flags为MAP_SHARED
  3. 一个进程使用映射区读,另一个写
mmap特性
  • fifo、mmap都可以应用于非血缘的。区别
    • mmap:数据可以重复读写
    • fifo:只能一次读写
  • 直接操作内存,执行速度快。
匿名映射
  • 只能应用有血缘关系的进程通信
p =(int*)mmap(NULL,400,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);
  • MAP_ANONYMOUS、 MAP_ANOPN只在Linux系统中有效。
  • /dev/null: 设备文件。黑洞文件。特性:无限写数据,写入没!
  • /dev/zero: 设备文件。无限读都是"\0";
  • 类unix系统借助/dev/zero实现
fd = open("/dev/zero",O_RDWR);
p = mmap(NULL,size.PROT_READ|PROT_WRITE,MMAP_SHARED,fd,0);

你可能感兴趣的:(linux,运维,服务器,c++)