div网站模板,建设网站如何收费,wordpress无法加载图片大小,seo网站推广案例文章目录1. 进程概述1.1 程序概览1.2 进程概念1.3 单道、多道程序设计1.4 时间片1.5 并行与并发1.6 进程控制块#xff08;PCB#xff09;2. 进程的状态转换2.1 进程的状态2.2 进程相关命令查看进程实时显示进程动态杀死进程进程号和相关函数3. 进程的创建-fork函数3.1 进程创…
文章目录1. 进程概述1.1 程序概览1.2 进程概念1.3 单道、多道程序设计1.4 时间片1.5 并行与并发1.6 进程控制块PCB2. 进程的状态转换2.1 进程的状态2.2 进程相关命令查看进程实时显示进程动态杀死进程进程号和相关函数3. 进程的创建-fork函数3.1 进程创建函数函数声明与解释代码示例3.2 父子进程虚拟地址空间fock创建子进程写实复制父子进程关系3.3 GDB 多进程调试设置调试父进程或子进程设置调试模式4. 进程创建-exec 函数族4.1 exec 函数族介绍4.2 exec 函数族作用图解4.3 exec 常用函数汇总execlexeclpexecleexecvexecvpexecvpeexecve -linux函数5. 进程控制5.1 进程退出5.2 孤儿进程5.3 僵尸进程5.4 进程回收wait 函数退出信息相关宏函数waitpid 函数6. 进程间的通信6.1 进程间通讯概念6.2 linux 进程通信的方式6.3 管道通信6.3.1 匿名管道概述管道特点为什么可以使用管道进行进程间通信管道数据结构匿名管道的使用6.3.2 有名管道牛客网linux服务器课程 1. 进程概述
1.1 程序概览
程序是包含一系列信息的文件这些信息描述了如何在运行时创建一个进程 二进制格式标识每个程序文件都包含用于描述可执行文件格式的元信息。内核利用此信息来解释文件中的其他信息。ELF可执行连接格式 机器语言指令对程序算法进行编码。 程序入口地址标识程序开始执行时的起始指令位置。 数据程序文件包含的变量初始值和程序使用的字面量值比如字符串。 符号表及重定位表描述程序中函数和变量的位置及名称。这些表格有多重用途其中包括调试和运行时的符号解析动态链接。 共享库和动态链接信息程序文件所包含的一些字段列出了程序运行时需要使用的共享库以及加载共享库的动态连接器的路径名。 其他信息程序文件还包含许多其他信息用以描述如何创建进程。
1.2 进程概念 进程是正在运行的程序的实例。是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元在传统的操作系统中进程既是基本的分配单元也是基本的执行单元。 可以用一个程序来创建多个进程进程是由内核定义的抽象实体并为该实体分配用以执行程序的各项系统资源。从内核的角度看进程由用户内存空间和一系列内核数据结构组成 其中用户内存空间包含了程序代码及代码所使用的变量而内核数据结构则用于维护进程状态信息。记录在内核数据结构中的信息包括许多与进程相关的标识号IDs、虚拟内存表、打开文件的描述符表、信号传递及处理的有关信息、进程资源使用及限制、当前工作目录和大量的其他信息。
1.3 单道、多道程序设计
单道程序即在计算机内存中只允许一个的程序运行。多道程序设计技术是在计算机内存中同时存放几道相互独立的程序使它们在管理程序控制下相互穿插运行两个或两个以上程序在计算机系统中同处于开始到结束之间的状态, 这些程序共享计算机系统资源。引入多道程序设计技术的根本目的是为了提高 CPU 的利用率。对于一个单 CPU 系统来说程序同时处于运行状态只是一种宏观上的概念他们虽然都已经开始运行但就微观而言任意时刻 CPU 上运行的程序只有一个。在多道程序设计模型中多个进程轮流使用 CPU。而当下常见 CPU 为纳秒级 1秒可以执行大约 10 亿条指令。由于人眼的反应速度是毫秒级所以看似同时在运行。
1.4 时间片
时间片timeslice又称为“量子quantum”或“处理器片processor slice”是操作系统分配给每个正在运行的进程微观上的一段 CPU 时间。 事实上虽然一台计算机通常可能有多个 CPU但是同一个 CPU 永远不可能真正地同时运行多个任务。在只考虑一个 CPU 的情况下这些进程“看起来像”同时运行的实则是轮番穿插地运行由于时间片通常很短在 Linux 上为 5ms 800ms用户不会感觉到。 时间片由操作系统内核的调度程序分配给每个进程。 首先内核会给每个进程分配相等的初始时间片然后每个进程轮番地执行相应的时间当所有进程都处于时间片耗尽的状态时内核会重新为每个进程计算并分配时间片如此往复。
1.5 并行与并发
并行(parallel)指在同一时刻有多条指令在多个处理器上同时执行。并发(concurrency)指在同一时刻只能有一条指令执行但多个进程指令被快速的轮换执行使得在宏观上具有多个进程同时执行的效果但在微观上并不是同时执行的只是把时间分成若干段使多个进程快速交替的执行。
1.6 进程控制块PCB 为了管理进程内核必须对每个进程所做的事情进行清楚的描述。内核为每个进程分配一个 PCB(Processing Control Block)进程控制块维护进程相关的信息Linux 内核的进程控制块是 task_struct 结构体。 在/usr/src/linux-headers-xxx/include/linux/sched.h 文件中可以查 看struct task_struct结构体定义。其内部成员有很多我们只需要掌握以下 部分即可 进程id系统中每个进程有唯一的 id用 pid_t 类型表示其实就是一个非负整数进程的状态有就绪、运行、挂起、停止等状态进程切换时需要保存和恢复的一些CPU寄存器描述虚拟地址空间的信息描述控制终端的信息当前工作目录Current Working Directoryumask 掩码文件描述符表包含很多指向 file 结构体的指针和信号相关的信息用户 id 和组 id会话Session和进程组进程可以使用的资源上限Resource Limit
2. 进程的状态转换
2.1 进程的状态 2.2 进程相关命令
查看进程
ps aux / ajx
a显示终端上的所有进程包括其他用户的进程
u显示进程的详细信息
x显示没有控制终端的进程
j列出与作业控制相关的信息STAT参数意义
D 不可中断 Uninterruptibleusually IO
R 正在运行或在队列中的进程
S(大写) 处于休眠状态
T 停止或被追踪
Z 僵尸进程
W 进入内存交换从内核2.6开始无效
X 死掉的进程高优先级
N 低优先级
s 包含子进程位于前台的进程组实时显示进程动态
top
可以在使用 top 命令时加上 -d 来指定显示信息更新的时间间隔在 top 命令
执行后可以按以下按键对显示的结果进行排序M 根据内存使用量排序P 根据 CPU 占有率排序T 根据进程运行时间长短排序U 根据用户名来筛选进程K 输入指定的 PID 杀死进程杀死进程
kill [-signal] pid
kill –l 列出所有信号
kill –SIGKILL 进程ID
kill -9 进程ID
killall name 根据进程名杀死进程进程号和相关函数
每个进程都由进程号来标识其类型为 pid_t整型进程号的范围 0 32767。进程号总是唯一的但可以重用。当一个进程终止后其进程号就可以再次使用。任何进程除 init 进程都是由另一个进程创建该进程称为被创建进程的父进程对应的进程号称为父进程号PPID。进程组是一个或多个进程的集合。他们之间相互关联进程组可以接收同一终端的各种信号关联的进程有一个进程组号PGID。默认情况下当前的进程号会当做当前的进程组号。进程号和进程组相关函数 pid_t getpid(void);pid_t getppid(void);pid_t getpgid(pid_t pid);3. 进程的创建-fork函数
3.1 进程创建函数
函数声明与解释
系统允许一个进程创建新进程新进程即为子进程子进程还可以创建新的子进程形成
进程树结构模型。
#include sys/types.h
#include unistd.h
pid_t fork(void);
返回值成功子进程中返回 0父进程中返回子进程 ID失败返回 -1
失败的两个主要原因
1. 当前系统的进程数已经达到了系统规定的上限这时 errno 的值被设置
为 EAGAIN
2. 系统内存不足这时 errno 的值被设置为 ENOMEM#include sys/types.h#include unistd.hpid_t fork(void);函数的作用用于创建子进程。返回值fork()的返回值会返回两次。一次是在父进程中一次是在子进程中。在父进程中返回创建的子进程的ID,在子进程中返回0如何区分父进程和子进程通过fork的返回值。在父进程中返回-1表示创建子进程失败并且设置errno父子进程之间的关系区别1.fork()函数的返回值不同父进程中: 0 返回的子进程的ID子进程中: 02.pcb中的一些数据当前的进程的id pid当前的进程的父进程的id ppid信号集共同点某些状态下子进程刚被创建出来还没有执行任何的写数据的操作- 用户区的数据- 文件描述符表父子进程对变量是不是共享的- 刚开始的时候是一样的共享的。如果修改了数据不共享了。- 读时共享子进程被创建两个进程没有做任何的写的操作写时拷贝。
代码示例
代码测试
#include sys/types.h
#include unistd.h
#include stdio.hint main() {int num 10; // 栈空间的量在各自进程中都有栈处理互不干扰// 创建子进程pid_t pid fork();// 判断是父进程还是子进程if(pid 0) {// printf(pid : %d\n, pid);// 如果大于0返回的是创建的子进程的进程号当前是父进程printf(i am parent process, pid : %d, ppid : %d\n, getpid(), getppid());printf(parent num : %d\n, num);num 10; printf(parent num 10 : %d\n, num);} else if(pid 0) {// 当前是子进程printf(i am child process, pid : %d, ppid : %d\n, getpid(),getppid());printf(child num : %d\n, num);num 100;printf(child num 100 : %d\n, num);}// for循环-父子进程交替执行子进程继承了父进程的资源for(int i 0; i 3; i) { printf(i : %d , pid : %d\n, i , getpid());sleep(1);}return 0;
}/*
实际上更准确来说Linux 的 fork() 使用是通过写时拷贝 (copy- on-write) 实现。
写时拷贝是一种可以推迟甚至避免拷贝数据的技术。
内核此时并不复制整个进程的地址空间而是让父子进程共享同一个地址空间。
只用在需要写入的时候才会复制地址空间从而使各个进行拥有各自的地址空间。
也就是说资源的复制是在需要写入的时候才会进行在此之前只有以只读方式共享。
注意fork之后父子进程共享文件
fork产生的子进程与父进程相同的文件文件描述符指向相同的文件表引用计数增加共享文件偏移指针。
*/3.2 父子进程虚拟地址空间
fock创建子进程 写实复制
主要目的是降低内存使用和拷贝内存所需要的时间因为创建子进程可能为了使用exec函数调用其他可执行文件将子进程内容替换如果一创建子进程就将内存完全复制一份就没有意义了
实际上更准确来说Linux 的 fork() 使用是通过写时拷贝 (copy- on-write) 实现。
写时拷贝是一种可以推迟甚至避免拷贝数据的技术。
内核此时并不复制整个进程的地址空间而是让父子进程共享同一个地址空间。
只用在需要写入的时候才会复制地址空间从而使各个进行拥有各自的地址空间。
也就是说资源的复制是在需要写入的时候才会进行在此之前只有以只读方式共享。
注意fork之后父子进程共享文件
fork产生的子进程与父进程相同的文件文件描述符指向相同的文件表引用计数增加共享文件偏移指针。父子进程关系 父子进程之间的关系区别1.fork()函数的返回值不同父进程中: 0 返回的子进程的ID子进程中: 02.pcb中的一些数据当前的进程的id pid当前的进程的父进程的id ppid信号集共同点某些状态下子进程刚被创建出来还没有执行任何的写数据的操作- 用户区的数据- 文件描述符表父子进程对变量是不是共享的- 刚开始的时候是一样的共享的。如果修改了数据不共享了。- 读时共享子进程被创建两个进程没有做任何的写的操作写时拷贝。3.3 GDB 多进程调试
设置调试父进程或子进程
使用 GDB 调试的时候 GDB 默认只能跟踪一个进程可以在 fork 函数调用之前通过指令设置 GDB 调试工具跟踪父进程或者是跟踪子进程默认跟踪父进程。 查看调试父进程还是子进程 show follow-fork-mode 设置调试父进程或者子进程set follow-fork-mode [parent默认 | child]
设置调试模式 设置调试模式set detach-on-fork [on | off] 默认为 on表示调试当前进程的时候其它的进程继续运行如果为 off调试当前进程的时候其它进程被 GDB 挂起。 查看调试的进程 info inferiors 切换当前调试的进程 inferior id 使进程脱离 GDB 调试 detach inferiors id
4. 进程创建-exec 函数族
4.1 exec 函数族介绍 exec 函数族的作用是根据指定的文件名找到可执行文件并用它来取代调用进程的内容换句话说就是在调用进程内部执行一个可执行文件。 一般是先fock子进程再将可执行文件替换子进程内容去执行这样原父进程其他任务可以继续运行 exec 函数族的函数执行成功后不会返回 因为调用进程的实体包括代码段数据段和堆栈等都已经被新的内容取代只留下进程 ID 等一些表面上的信息仍保持原样颇有些神似“三十六计”中的“金蝉脱壳”。看上去还是旧的躯壳却已经注入了新的灵魂。只有调用失败了它们才会返回 -1从原程序的调用点接着往下执行。
4.2 exec 函数族作用图解 4.3 exec 常用函数
汇总
l(list) 参数地址列表以空指针结尾
v(vector) 存有各参数地址的指针数组的地址
p(path) 按 PATH 环境变量指定的目录搜索可执行文件
e(environment) 存有环境变量字符串地址的指针数组的地址execl
int execl(const char *path, const char *arg, .../* (char *) NULL */);/* #include unistd.hint execl(const char *path, const char *arg, ...);- 参数- path:需要指定的执行的文件的路径或者名称a.out /home/nowcoder/a.out 推荐使用绝对路径./a.out hello world- arg:是执行可执行文件所需要的参数列表第一个参数一般没有什么作用为了方便一般写的是执行的程序的名称从第二个参数开始往后就是程序执行所需要的的参数列表。参数最后需要以NULL结束哨兵- 返回值只有当调用失败才会有返回值返回-1并且设置errno如果调用成功没有返回值。*/
#include unistd.h
#include stdio.hint main() {// 创建一个子进程在子进程中执行exec函数族中的函数pid_t pid fork();if(pid 0) {// 父进程printf(i am parent process, pid : %d\n,getpid());sleep(1);}else if(pid 0) {// 子进程// execl(hello,hello,NULL); 在子进程中执行hello程序execl(/bin/ps, ps, aux, NULL); // 执行操作系统程序// perror(execl);// 测试不会执行子进程的剩下逻辑printf(i am child process, pid : %d\n, getpid());}// 测试只执行父进程程序for(int i 0; i 3; i) {printf(i %d, pid %d\n, i, getpid());}return 0;
}hello可执行文件内容
#include stdio.hint main() { printf(hello, world\n);return 0;
}execlp
int execlp(const char *file, const char *arg, ... /* (char *) NULL */);/* #include unistd.hint execlp(const char *file, const char *arg, ... );- 会到环境变量中查找指定的可执行文件如果找到了就执行- 找不到就执行不成功。- 参数- file:需要执行的可执行文件的文件名a.outps- arg:是执行可执行文件所需要的参数列表第一个参数一般没有什么作用为了方便一般写的是执行的程序的名称从第二个参数开始往后就是程序执行所需要的的参数列表。参数最后需要以NULL结束哨兵- 返回值只有当调用失败才会有返回值返回-1并且设置errno如果调用成功没有返回值。int execv(const char *path, char *const argv[]);argv是需要的参数的一个字符串数组char * argv[] {ps, aux, NULL};execv(/bin/ps, argv);int execve(const char *filename, char *const argv[], char *const envp[]);char * envp[] {/home/nowcoder, /home/bbb, /home/aaa};*/
#include unistd.h
#include stdio.hint main() {// 创建一个子进程在子进程中执行exec函数族中的函数pid_t pid fork();if(pid 0) {// 父进程printf(i am parent process, pid : %d\n,getpid());sleep(1);}else if(pid 0) {// 子进程execlp(ps, ps, aux, NULL);printf(i am child process, pid : %d\n, getpid());}for(int i 0; i 3; i) {printf(i %d, pid %d\n, i, getpid());}return 0;
}execle int execle(const char *path, const char *arg, .../*, (char *) NULL, char *
const envp[] */);execv int execv(const char *path, char *const argv[]);- argv是需要的参数的一个字符串数组- char * argv[] {ps, aux, NULL};- execv(/bin/ps, argv);execvp int execvp(const char *file, char *const argv[]);execvpe int execvpe(const char *file, char *const argv[], char *const envp[]);execve -linux函数 int execve(const char *filename, char *const argv[], char *const envp[]);5. 进程控制
5.1 进程退出 /*#include stdlib.hvoid exit(int status);#include unistd.hvoid _exit(int status);status参数是进程退出时的一个状态信息。父进程回收子进程资源的时候可以获取到。
*/
#include stdio.h
#include stdlib.h
#include unistd.hint main() {printf(hello\n); // 自动刷新缓存区printf(world); // C库的函数会先放到缓冲区// exit(0); // 标准c库与return 0 功能一样_exit(0); // linux库没有缓冲区最终程序只打印已经刷新缓冲区的helloreturn 0;
}5.2 孤儿进程
父进程运行结束但子进程还在运行未运行结束这样的子进程就称为孤儿进程Orphan Process。每当出现一个孤儿进程的时候内核就把孤儿进程的父进程设置为 init 而 init进程会循环地 wait() 它的已经退出的子进程。 这样当一个孤儿进程凄凉地结束了其生命周期的时候 init 进程就会代表党和政府出面处理它的一切善后工作。 因此孤儿进程并不会有什么危害。
5.3 僵尸进程
每个进程结束之后, 都会释放自己地址空间中的用户区数据内核区的 PCB 没有办法自己释放掉需要父进程去释放。进程终止时父进程尚未回收子进程残留资源PCB存放于内核中变成僵尸Zombie进程。僵尸进程不能被 kill -9 杀死这样就会导致一个问题如果父进程不调用 wait()或 waitpid() 的话那么保留的那段信息就不会释放其进程号就会一直被占用但是系统所能使用的进程号是有限的如果大量的产生僵尸进程将因为没有可用的进程号而导致系统不能产生新的进程此即为僵尸进程的危害应当避免。
5.4 进程回收
在每个进程退出的时候内核释放该进程所有的资源、包括打开的文件、占用的内存等。但是仍然为其保留一定的信息这些信息主要主要指进程控制块PCB的信息包括进程号、退出状态、运行时间等。父进程可以通过调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。wait() 和 waitpid() 函数的功能一样区别在于 wait() 函数会阻塞waitpid() 可以设置不阻塞 waitpid() 还可以指定等待哪个子进程结束。 注意一次wait或waitpid调用只能清理一个子进程清理多个子进程应使用循环。
wait 函数
/*#include sys/types.h#include sys/wait.hpid_t wait(int *wstatus);功能等待任意一个子进程结束如果任意一个子进程结束了此函数会回收子进程的资源。参数int *wstatus进程退出时的状态信息传入的是一个int类型的地址传出参数。返回值- 成功返回被回收的子进程的id- 失败-1 (所有的子进程都结束调用函数失败)调用wait函数的进程会被挂起阻塞直到它的一个子进程退出或者收到一个不能被忽略的信号时才被唤醒相当于继续往下执行如果没有子进程了函数立刻返回返回-1如果子进程都已经结束了也会立即返回返回-1.*/
#include sys/types.h
#include sys/wait.h
#include stdio.h
#include unistd.h
#include stdlib.hint main() {// 有一个父进程创建5个子进程兄弟pid_t pid;// 创建5个子进程for(int i 0; i 5; i) {pid fork();if(pid 0) { // 防止子进程递归fockbreak;}}if(pid 0) {// 父进程while(1) {printf(parent, pid %d\n, getpid());// 子进程不退出父进程就一直阻塞不进行直到一个子进程退出int ret wait(NULL); // 不需要获得子进程退出的状态if(ret -1) {break;}// 打印子进程是否被释放的信息printf(child die, pid %d\n, ret);sleep(1);}} else if (pid 0){// 子进程while(1) {printf(child, pid %d\n,getpid()); sleep(1); }}return 0; // exit(0)
}退出信息相关宏函数 #include sys/types.h
#include sys/wait.h
#include stdio.h
#include unistd.h
#include stdlib.hint main() {// 有一个父进程创建5个子进程兄弟pid_t pid;// 创建5个子进程for(int i 0; i 5; i) {pid fork();if(pid 0) { // 防止子进程递归fockbreak;}}if(pid 0) {// 父进程while(1) {printf(parent, pid %d\n, getpid());// 子进程不退出父进程就一直阻塞不进行直到一个子进程退出 int st;int ret wait(st); // 获取子进程退出的状态if(ret -1) {break;}// 获取子进程退出的状态信息与宏比较if(WIFEXITED(st)) {// 是不是正常退出printf(退出的状态码%d\n, WEXITSTATUS(st));}if(WIFSIGNALED(st)) {// 是不是异常终止printf(被哪个信号干掉了%d\n, WTERMSIG(st));}// 打印子进程是否被释放的信息printf(child die, pid %d\n, ret);sleep(1);}} else if (pid 0){// 子进程printf(child, pid %d\n,getpid()); sleep(1); exit(0); // 正常退出}return 0; // exit(0)
}waitpid 函数
/*#include sys/types.h#include sys/wait.hpid_t waitpid(pid_t pid, int *wstatus, int options);功能回收指定进程号的子进程可以设置是否阻塞。参数- pid:pid 0 : 某个子进程的pidpid 0 : 回收当前进程组的所有子进程 pid -1 : 回收所有的子进程相当于 wait() 最常用pid -1 : 某个进程组的组id的绝对值回收指定进程组中的子进程- options设置阻塞或者非阻塞0 : 阻塞WNOHANG : 非阻塞- 返回值 0 : 返回子进程的id 0 : optionsWNOHANG, 表示还有子进程或者 -1 错误或者没有子进程了
*/
#include sys/types.h
#include sys/wait.h
#include stdio.h
#include unistd.h
#include stdlib.hint main() {// 有一个父进程创建5个子进程兄弟pid_t pid;// 创建5个子进程for(int i 0; i 5; i) {pid fork();if(pid 0) {break;}}if(pid 0) {// 父进程while(1) {printf(parent, pid %d\n, getpid());sleep(1);int st;// int ret waitpid(-1, st, 0); // 阻塞int ret waitpid(-1, st, WNOHANG); // 非阻塞if(ret -1) {break;} else if(ret 0) {// 说明还有子进程存在continue;} else if(ret 0) { // 回收到了子进程if(WIFEXITED(st)) {// 是不是正常退出printf(退出的状态码%d\n, WEXITSTATUS(st));}if(WIFSIGNALED(st)) {// 是不是异常终止printf(被哪个信号干掉了%d\n, WTERMSIG(st));}printf(child die, pid %d\n, ret);}}} else if (pid 0){// 子进程while(1) {printf(child, pid %d\n,getpid()); sleep(1); }exit(0);}return 0;
}6. 进程间的通信
6.1 进程间通讯概念
进程是一个独立的资源分配单元不同进程这里所说的进程通常指的是用户进程之间的资源是独立的没有关联不能在一个进程中直接访问另一个进程的资源。但是进程不是孤立的不同的进程需要进行信息的交互和状态的传递等因此需要进程间通信( IPC Inter Processes Communication )。进程间通信的目的 数据传输一个进程需要将它的数据发送给另一个进程。通知事件一个进程需要向另一个或一组进程发送消息通知它它们发生了某种事件如进程终止时要通知父进程。资源共享多个进程之间共享同样的资源。为了做到这一点需要内核提供互斥和同步机制。进程控制有些进程希望完全控制另一个进程的执行如 Debug 进程此时控制进程希望能够拦截另一个进程的所有陷入和异常并能够及时知道它的状态改变。
6.2 linux 进程通信的方式 6.3 管道通信
6.3.1 匿名管道
概述 管道特点
管道其实是一个在内核内存中维护的缓冲器这个缓冲器的存储能力是有限的不同的操作系统大小不一定相同。管道拥有文件的特质读操作、写操作 匿名管道没有文件实体有名管道有文件实体但不存储数据。可以按照操作文件的方式对管道进行操作。 一个管道是一个字节流使用管道时不存在消息或者消息边界的概念从管道读取数据的进程可以读取任意大小的数据块而不管写入进程写入管道的数据块的大小是多少。通过管道传递的数据是顺序的从管道中读取出来的字节的顺序和它们被写入管道的顺序是完全一样的。在管道中的数据的传递方向是单向的一端用于写入一端用于读取管道是半双工的。 即同一时间只能A端读B端写但不同时间可以从B端读A端写 从管道读数据是一次性操作数据一旦被读走它就从管道中被抛弃释放空间以便写更多的数据在管道中无法使用 lseek() 来随机的访问数据。匿名管道只能在具有公共祖先的进程父进程与子进程或者两个兄弟进程具有亲缘关系之间使用。
为什么可以使用管道进行进程间通信
因为子进程与父进程共享文件描述符可以找到一个管道
管道数据结构
环形队列可以有效利用资源
匿名管道的使用
6.3.2 有名管道