• LInux多进程开发II


    1.进程间通信。
    • 进程是独立的资源分配单元,不同进程之间的资源是独立的,没有关联,不能在一个进程中直接访问另一个进程的资源。
    • 进程之间可以进行信息的交互和状态的传递,称为进程间通信(IPC:Inter Processes Communication)。
    • 进程间通信:数据传输、通知时间、资源共享、进程控制。
    2.进程通信方式。
    • 同一主机进程通信。匿名管道、有名管道、信号、消息队列、共享内存、信号量。
    • 不同主机(网络)通信。Socket。
    3.匿名管道。
    • Unix系统进程间通信。
    • 管道是在内核内存中维护的缓冲区,不同的操作系统大小不同。
    • 管道拥有文件的特质:读写操作。匿名管道没有文件实体;有名管道有文件实体,但不存储数据。管道的两端可以分别用两个文件描述符表示。可以用操作文件的方式对管道进行操作。
    • 一个管道是一个字节流,使用管道不存在消息或消息边界的概念,从管道读取数据的进程可以读取任意大小的数据块,而不管写入进程写入管道的数据块大小是多少。
    • 通过管道传递的数据是顺序的,从管道读取出来的字节的顺序和被写入管道的顺序完全一致。
    • 管道的数据传递方向是单向的,一端用于写入,一端用于读取,管道是半双工的。
    • 从管道读数据是一次性操作,数据一旦被读走就被从管道中抛弃并释放空间可以写入更多的数据。在管道中无法使用lseek()来随机访问数据。
    • 匿名管道只能在具有公共祖先的进程之间使用。子进程通过fork()创建出来与父进程共享文件描述符,因此可以根据相同的文件描述符进行通信。
    • 实现:循环数组。由两个文件描述符标志分别读写位置。
    4.实现匿名管道。
    #include <unistd.h>
    int pipe(int pipedfd[2]);
        - 作用:创建一个匿名管道,用来进程间通信。
        - pipefd[2]: 传出参数。pipefd[0],对应管道的读端;pipefd[1],对应管道的写端。
        - 返回值:成功返回0,失败返回1。
     
    注意:
    1.匿名管道只用于具有关系的进程之间的通信。
    2.管道默认是阻塞的,如果管道中没有数据,read阻塞;如果管道满了,write阻塞。
     
    # 查看管道缓冲大小命令
    $ ulimit -a
     
    # 查看管道缓冲大小函数
    #include <unistd.h>
    long fpathconf(int fd, int name);
    long size = fpathcong(pipefd[0], _PC_PIPE_BUF);
     
    注意:实现管道通信,一般只会有一个方向的通信,从进程a传数据到进程b,或者从进程b传数据到进程a。因为如果进程a,b互相传数据的话,可能会造成后果:a写入管道的数据又被a自己读去了,b写入管道的数据也可能被b进程读去了。所以在实现管道时,往管道写入数据的进程会关闭读端close(pipefd[0]);而往管道读数据的进程会关闭管道写端close(pipefd[1])。
     
    5.管道的读写特点:使用管道时,有以下几种特殊情况(假设都是阻塞IO操作)
    • 所有指向管道写端的文件描述符都关闭了(管道写端引用计数为0),有进程从管道的读端读取数据,那么管道中剩余的数据被读取之后,再次read会返回0,就像读到文件末尾一样。
    • 如果有指向管道写端的文件描述符没有关闭(管道写端引用计数大于0),而持有管道写端的进程没有往管道中写数据,这个时候若有进程从管道中读取数据,那么管道中剩余的数据被读取之后,再次read会阻塞,直到管道中有数据可以读了才读取数据并返回。
    • 所有指向管道读端的文件描述符都关闭了(管道读端引用计数为0),这个时候有进程往管道中写数据,那么该进程会受到一个信号SIGPIPE,通常会导致进程终止。
    • 如果有指向管道读端的文件描述符没有关闭(管道读端引用技术大于0),而持有管道读端的进程也没有从管道读数据,这时有进程向管道写数据,那么管道在被写满的时候再次write阻塞,直到管道中有空位置能再次写入数据并返回。
    总结:
        读管道:
            1. 管道中有数据,read返回实际读到的字节数。
            2. 管道中无数据:a. 写端全关闭,read返回0;b. 写端没有完全关闭,read阻塞等待。
        写管道:
            1. 管道读端全部关闭,进程异常终止(进程受到SIGPIPE信号)。
            2. 管道读端没有全部关闭:a. 管道已满,write阻塞;b. 管道没有满,write写入数据,并返回实际写入的字节数。
     
    6.设置管道非阻塞。
    int flags = fcntl(fd[0], F_GETFL);       # 获取原来的flag
    flags |= O_NONBLOCK;                     # 修改flag的值
    fcntl(fd[0], F_SETFL, flags);            # 设置新的flag
     
    7.有名管道(FIFO)。
    • 有名管道提供了一个路径名与之关联(即:有一个实体文件),以FIFO的文件形式存在与文件系统中。其打开方式与打开普通文件一样,因此通过访问该路径即可通过FIFO通信。
    • 与匿名管道的区别:1. FIFO有文件实体,但是其内容存放于内存中;2. 当使用FIFO的进程退出后,FIFO文件继续保存在文件系统中之后可以继续使用;3. FIFO有名字,不相关的进程可以通过打开有名管道进行通信。
     
    8.实现有名管道。
    # 创建管道命令
    mkfifo 名字
     
    # 通过函数创建,创建FIFO之后文件IO函数(open、read、write、close)都可用于FIFO
    #include <sys/types.h>
    #include <sys/stat.h>
    int mkfifo(const char *pathname, mode_t mode);
        - pathname: 管道名称的路径
        - mode: 文件的权限
        - 返回值: 成功返回0,失败返回-1并设置errno
     
    注意:
        1.一个为打开管道的只读进程会阻塞,直到另一个只写进程打开管道;
        2.一个为打开管道的只写进程会阻塞,直到另一个只读进程打开管道;
    读管道:
        管道中有数据:read返回实际读到的字节数;
        管道中无数据:a.管道写端被全部关闭,read返回0;b.写端没有全部被关闭,read阻塞等待
    写管道:
        管道读端被全部关闭,进程异常终止(收到一个SIGPIPE信号);
        管道读端没有全部关闭:a.管道已满,write阻塞;b.管道没满,write写入数据,并返回实际写入的字节数
     
    9.内存映射。将磁盘文件的数据映射到内存中,程序可以通过修改内存就能修改磁盘文件。
    • 同一个文件可以映射到不同进程的虚拟内存空间,因此某个进程修改内存后,系统会同步文件的修改,则另一个将内存映射到文件的进程即可收到消息。
    • 可以实现进程间通信:1.有关系的进程(父子进程);2.没有关系的进程间通信。
    • 内存映射区通信是非阻塞的。
    #include <sys/mman.h>
    void mmap(coid *addr, size_t length, int prot, int flags, int fd, off_t offset);
        - 作用:将一个文件或者设备的数据映射到内存中
        - addr: NULL,由内核指定
        - length: 要映射的数据的长度,这个值不能为0,建议使用文件的长度,使用stat或者lseek获取文件的长度
        - prot: 对申请的内存映射区的操作权限,PROT_EXEC 可执行权限;PROT_READ 读权限;PROT_WRITE 写权限;PROT_NONE 没有权限
        - flags: MAP_SHARED 映射区的数据会自动和磁盘文件进行同步,进程间通信,必须设置这个选项;MAP_PRIVATE 不同步,内存映射区的数据改变了,对原文件不会修改,会创建一个新的文件(copy on write)
        - fd: 需要映射的文件的文件描述符,通过open()得到,文件大小要>0
        - offset: 偏移量,4k的整数倍,0表示不偏移
        - 返回值:成功返回创建的内存的首地址,失败返回MAP_FAILED ((void *) -1)
     
    int munmap(void *addr, size_t length);
        - 作用:释放内存映射
        - addr: 要释放的内存地址
        - length: 要释放的内存的大小,和mmap参数的length一样
    

      

    10.使用内存映射实现进程间通信:
    • 有关系的进程(父子进程)
                 - 还没有子进程的时候
                        - 通过唯一的父进程,先创建内存映射区
                 - 有了内存映射区后,创建子进程
                 - 父子进程共享创建的内存映射区
    • 没有关系的进程间通信
                 - 准备一个大小不是0 的磁盘文件
                 - 进程1 通过磁盘文件创建内存映射区
                        - 得到一个操作这块内存的指针
                 - 进程2 通过磁盘文件创建内存映射区
                        - 得到一个操作这块内存的指针
                 - 使用内存映射区通信
    11.内存映射的注意事项。
    • 如果对mmap的返回值(ptr)做++操作(ptr++),munmap是否能成功?
    void *ptr = mmap(...);
    ptr++;                    // 可以操作
    munmap(ptr, len);         // 错误,应该保存使用之前的地址
    • 如果open时O_RDONLY,mmap时prot参数指定PROT_READ|PROT_WRITE会怎样?
    错误,返回MAP_FAILD
    open()函数的权限要大于等于prot的权限。
    e.g.: 
    open()读写,prot可以读写/只读/只写
    open()只读,prot只能只读
    • 如果文件偏移量是1000会怎样?
    返回错误MAP_FAILED,偏移量只能是4k的整数倍
    • mmap什么情况下会调用失败?
    1. 第二个该参数length = 0
    2. 第三个参数 prot 只有写权限
    3. 第三个参数有 PROT_READ | PROT_WRITE 权限,而第五个参数fd通过open打开时的权限是O_RDONLY / O_WRONLY(内存权限大于文件权限)
    • 可以open的时候O_CREAT一个新文件来创建映射区吗?
    可以,但是创建的文件大小为0不行,需要对新文件进行扩展( lseek() / truncate() )
    • mmap后关闭文件描述符,对mmap映射有没有影响?
    int fd = open("xxx");
    mmap(,,,,fd,0);
    close(fd);
    映射区还存在,创建营社区的fd被关闭,没有影响。
    • 对ptr越界操作会怎么样?
    void *ptr = mmap(NULL,100,,,,);
    越界操作操作的是非法内存,造成段错误
     
    12.匿名映射:不需要文件实体进行一个内存映射。只能用于父子进程间通信。
    mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARD | MAP_ANONYMOUS, -1, 0);
    文件描述符 fd = -1
    prot 要指定为 PROT_ANONYMOUS
     
     
  • 相关阅读:
    SM9-加解密
    SM9-密钥封装
    Cookie和Session的区别
    直观简单讲解单点登录的流程原理
    分布式环境下Session共享问题解决和原理讲解
    以微博开发平台为例,使用社交账号登录网站
    MD5&MD5盐值加密到BCryptPasswordEncoder
    CompletableFuture异步线程
    git clone 出现fatal: unable to access 'https://github 类错误解决方法
    Python包及其定义和引用详解
  • 原文地址:https://www.cnblogs.com/tristatl/p/15120551.html
Copyright © 2020-2023  润新知