网站主机英文网页开发软件哪个好用
网站主机英文,网页开发软件哪个好用,菜鸟教程官网,做网站一般图片的比例一、管道就像现实中的人一样#xff0c;各进程之间一定会出现需要信息交换的场景#xff08;如数据传输#xff0c;资源共享等#xff09;#xff0c;这就是进程间通信。进程间通信有许多种方法#xff0c;管道是其中之一。虽然管道在进程间通信中的地位不高#xff0c;…一、管道就像现实中的人一样各进程之间一定会出现需要信息交换的场景如数据传输资源共享等这就是进程间通信。进程间通信有许多种方法管道是其中之一。虽然管道在进程间通信中的地位不高但依旧能帮助我们学习进程间通信原理。管道是内核级结构之前使用的 | 指令就是管道。如何通过管道进行进程间通信进程之间是具有独立性的哪怕是父子进程其在进行写时操作后也会彻底分流。那么就父子进程而言父子进程间是如何通过管道进行通信的呢在父进程fork子进程的时候父进程的数据也会被拷贝到子进程中。但是子进程在创建的时候files_struct结构体和struct_file结构体都会拷贝过去注意是浅拷贝。不同的是子进程中的struct_file依旧会指向内存中的文件内核缓冲区这个被父子进程共同指向的文件内核缓冲区就是实现父子进程间通信的关键。子进程可通过这个缓冲区直接读取父进程中的数据。而这个依靠struct file、父子进程之间的血缘关系以及内存中的文件内核缓冲区且磁盘中没有标识这是典型的匿名管道。匿名管道父子进程在 fork 时会继承并共享同一个内核级的管道缓冲区通过继承下来的文件描述符收发数据这就是匿名管道通信。管道属于内核资源不需要路径和文件名只能在亲缘进程间使用并且通信是单向的。pipe函数pipe函数是 Linux/Unix 中创建匿名管道的核心系统调用作用是让内核在内存中生成一个匿名管道缓冲区并返回两个文件描述符fd分别对应管道的读端和写端为后续父子进程通信铺路。#include unistd.h int pipe(int fd[2]);项说明参数fd输出型数组长度为 2fd[0]管道读端只能读写会报错fd[1]管道写端只能写读会报错返回值成功返回 0失败返回 -1同时设置errno如EMFILE表示文件描述符耗尽#include stdio.h #include unistd.h #include string.h #include sys/wait.h int main() { int fd[2]; // 1. 创建匿名管道内核生成缓冲区返回读/写端fd if (pipe(fd) -1) { perror(pipe failed); return 1; } // 2. fork创建子进程子进程继承fd[0]/fd[1]指向同一管道缓冲区 pid_t pid fork(); if (pid -1) { perror(fork failed); return 1; } if (pid 0) { // 子进程读数据 // 3. 关闭无用的写端单向通信子只读 close(fd[1]); char buf[1024]; // 4. 从读端fd[0]读取管道数据无数据则阻塞 ssize_t n read(fd[0], buf, sizeof(buf)-1); if (n 0) { buf[n] \0; printf(子进程读到%s\n, buf); } // 5. 关闭读端 close(fd[0]); } else { // 父进程写数据 // 3. 关闭无用的读端单向通信父只写 close(fd[0]); const char *msg Hello from parent (匿名管道通信); // 4. 向写端fd[1]写入管道数据 write(fd[1], msg, strlen(msg)); // 5. 关闭写端 close(fd[1]); // 等待子进程结束 wait(NULL); } return 0; }在使用pipe函数时如果有一端不使用为了防止误操作我们建议将不需要使用的端口关闭close掉。上图详细直白说明了文件描述符与管道之间的关系接下来我们将在内核的角度深入了解管道。站在内核角度看管道上图中两个进程的 f_inode 都指向同一个管道 inode共用同一个管道inode 关联 “数据页”这就是管道的内核缓冲区是通信数据的实际存储位置管道。两进程的 f_op 则是分别指向管道读写函数write/read 而不是普通文件的读写函数。private_data指向管道的核心管理结构 pipe_inode_info 里面记录了缓冲区地址、读写指针、等待队列等。进程通信的本质两个进程的 struct file 指向同一个 inode而 inode 关联同一个内核缓冲区因此它们操作的是同一块内存。对于父子进程这是通过 fork 继承父进程的 struct file 指针实现的对于命名管道是通过 open 同一个 FIFO 文件让内核分配指向同一 inode 的 struct file。二、匿名管道的特征匿名管道 5 种特性1.单向通信所谓的单项通信就是数据需要严格通过写 fd[1] 端到读 fd[0]端不能倒转方向固定。2.仅用于有血缘关系的进程什么是具有血缘关系的呢就是进程之间通过创建fork追溯能建立联系的进程最常见的就是父子进程。为什么必须要求具备血缘关系呢是因为匿名管道没有磁盘可见的名称无法被无关进程通过路径名寻址只能通过fork()继承文件描述符来共享。3.面向字节流字节流是指数据以连续的字节序列形式传输没有固定的 “消息边界”就像水流一样连续不断。字节流带来的问题1、消息边界消失也就是写端多次写入的内容读端可能一次全部读完写端一次写入的大段数据读端也可能分多次读取。例如将“hello world”字符串拆分为字符流的时候因为读端是将缓冲区中的字节流截断读入就容易导致上面的问题。2、粘、拆包问题粘包多个小的写操作被内核合并导致读端收到 “粘在一起” 的字节拆包一个大的写操作被拆分成多次读操作返回导致一个完整消息被拆成多段。这会让应用层无法直接按 “消息” 为单位处理数据。解决方案1、长度前缀法发送数据时先发送固定长度的 “长度字段”比如 4 字节整数表示后续实际消息的字节长度接收方先读取这个长度值再按长度精准读取消息内容彻底避免粘包 / 拆包。2、分割符号法用特殊分隔符作为消息的结束标记接收方逐字节读取数据直到遇到分隔符就认为拿到一个完整消息。4、生命周期随进程管道的内核缓冲区由内核管理其生命周期绑定在持有它的文件描述符fd上。这个跟智能指针很像每个管道创建时都会记录有多少个进程绑定当其绑定的进程数量为0的时候该管道就会被销毁。5. 自带互斥与同步机制互斥内核保证同一时间只有一个进程能对管道缓冲区进行写操作避免数据混乱。内核给每个管道配一把写互斥锁mutex进程要写管道必须先拿到这把锁没拿到就暂停睡眠等锁释放后再抢如果单次写的数据量 ≤ 64KBLinux 默认PIPE_BUF内核保证这一次写操作 “要么全写完要么全不写”不会被其他进程打断完全原子若超过 64KB则无法保证读操作无需互斥锁多个进程同时读不会乱内核通过原子操作更新读指针避免重复读取同一字节。同步协调读 / 写进程的操作节奏实现 “有空再读、有位再写”避免读空管道、写满管道导致的无效循环以及进程无限阻塞。内核给管道维护「读等待队列」「写等待队列」结合缓冲区 “已用字节数”“读写进程数” 来控制读空管道的读进程会进入读等待队列睡眠直至写进程写入数据被内核唤醒写满管道默认 64KB的写进程会进入写等待队列睡眠直至读进程读走数据腾出空间被内核唤醒所有写端关闭时读进程读完缓冲区剩余数据后 read () 返回 0EOF所有读端关闭时写进程写管道会触发内核发送SIGPIPE信号默认终止进程进程捕获则 write () 返回 - 1 且报错 EPIPE。互斥同步这里先简单了解一下后面会再讲清楚的。匿名通道的4种情况读空管道当子进程写得慢父进程尝试读取时管道为空父进程会进入阻塞状态直到子进程写入数据后才被唤醒继续读取。写满管道当子进程写得快而父进程不读取导致管道缓冲区默认 64KB被写满子进程会进入阻塞状态直到父进程读取数据腾出空间后才被唤醒继续写入。写端关闭读端读取当所有写端关闭后读端会先读完管道中剩余的数据之后再次调用read()时会返回0EOFend of file标识符表示管道已无数据可读读到了 “文件结尾”。读端关闭写端写入当所有读端关闭后写端继续向管道写入数据内核会向写进程发送SIGPIPE信号默认终止进程如果进程捕获了该信号write()会返回-1并设置errno为EPIPE管道损坏错误。