删除文件 本章的剩余内容将概述XV6所提供的服务——进程、内存、文件描述符、管道以及文件系统,通过一段段的代码来介绍它们并且讨论shell是如何使用它们的。...这些系统调用在shell上的使用,体现了它们的设计是多么独具匠心。 shell是一个普通的程序,它读取用户的命令并且执行它们,shell也是传统的类Unix(Unix-like)系统中主要的用户界面。...在父进程的程序中,fork函数返回的是子进程的pid,而在子进程的程序中,fork函数返回0。...shell的主要结构很简单:详见main的代码(在8501行),主循环使用getcmd读取命令行的输入,然后它调用fork,来创建shell进程的一份拷贝。...如果wc指向一个管道的写端口,那么wc就永远看不到eof了。 xv6 shell使用了与上面代码类似的方法,实现了如grep fork sh.c | wc -l这样的管道(在8450行)。
我们知道,我们在用户态进程里申请的每一块内存,在内核中均以vm_area_struct对象被维护,如果我调用了10000次mmap,那就有10000个vm_area_struct对象被创建,在fork调用中...在父进程中创建大量的常驻内存的页面,在fork之后子进程exec之前,父进程写这些页面,将会造成这些页面被复制,这是一种明显的不必要的开销。...事实上,早在1970年代,fork还未成为神话的年代,人们对创建新进程的方案持有大致两类观点,它们争议不断: 使用fork+exec。 使用spawn。 什么是spawn?...无论如何,争论的焦点在于: 在子进程创建前设置好它的属性? 在子进程image加载前设置好它的属性?...我和UNIX fork神话 fork太过完美,它没有任何参数,却承诺在底层把一切帮你拿捏的足够好,果真如此吗? 我第一次接触fork是在2006年中软吉大工作试用期期间,我就是一个写Java的。
的实现原理,或者不知道 fork 返回值为什么在父子进程中不同,或者不知道如何做到父子进程中返回值不同……了解这些是要有点知识积累的。...,函数返回时再将多个寄存器的值赋值到变量中; 也可能会像 golang 这样,通过栈内存来返回; 2.3.fork 返回值 fork 系统调用的返回值,有点特殊,在父进程和子进程中,这个函数返回的值是不同的...2.4.局限性 很多人清楚 fork 可以创建一个进程的副本并继续往下执行,可以根据 fork 返回值来执行不同的分支逻辑。如果进程是多线程的,在一个线程中调用 fork 会复制整个进程吗?...子进程中接收到这些 fd 之后,在类 unix 系统下一般会按照从 0、1、2、3 这样递增的顺序来分配 fd,那么传递过去的 fd 是可以预测的,假如除了 stdin, stdout, stderr...总结 热重启作为一种保证服务平滑重启、升级的实现方式,在今天看来依然非常有价值。本文描述了实现热重启的一些大致思路,并且通过 demo 循序渐进地描述了在 go 服务中如何予以实现。
当进程 A 相对进程 B 发送数据时,它把数据写到管道上,相当于管道就是输出文件。这样,在 UNIX 中两个进程之间的通信就非常类似于普通文件的读写了。...从宏观角度上看,这些系统调所提供的服务确定了多数操作系统应该具有的功能,下面分别来对不同的系统调用进行解释 用于进程管理的系统调用 在 UNIX 中,fork 是唯一可以在 POSIX 中创建进程的途径...例如,如果没有已经退出的子进程则立刻返回。 那么 shell 该如何使用 fork 呢?在键入一条命令后,shell 会调用 fork 命令创建一个新的进程。这个子进程会执行用户的指令。...CreateProcess 用于创建一个新进程,它把 UNIX 中的 fork 和 execve 两个指令合成一个,一起执行。它有许多参数用来指定新创建进程的性质。...Windows 中没有类似 UNIX 中的进程层次,所以不存在父进程和子进程的概念。在进程创建之后,创建者和被创建者是平等的。
就是这样,最初的UNIX为了体现分时特性,实现了最少的两个终端。注意,最初的UNIX没有fork,没有exec,甚至没有多进程的概念,为了实现分时,系统中仅有两个朴素的shell进程。...当命令执行完后,再用shell的代码覆盖掉命令程序的代码,针对单独的终端,系统其实一直在执行下面的覆盖循环(摘自论文的Process control 章节): 然而,在fork被引入UNIX之前,事实就是这样...UNIX fork的诞生 fork是如何引入UNIX的呢?...交换技术指的是用将进程的内存映像交换到磁盘,载入一个别的进程磁盘映像。 使用交换技术解决覆盖的问题,意味着要创建新的进程: 在新的进程中执行命令程序。...当然,解决方案也并不麻烦: 要讲效率,创造不如抄袭,创建新进程的最直接的就是copy当前shell进程,在copy的新进程中执行覆盖,命令程序覆盖copy的新进程,而当前的终端shell进程则被交换到磁盘保得全身
然而,在fork被引入UNIX之前,事实就是这样。...交换技术指的是用将进程的内存映像交换到磁盘,载入一个别的进程磁盘映像。 使用交换技术解决覆盖的问题,意味着要创建新的进程: 在新的进程中执行命令程序。...UNIX需要进行改动,两个配额的进程表显然不够用了。当然,解决方案也并不麻烦: ? 现在,剩下唯一的问题就是如何创建新进程了!谁来临门一脚呢?...要讲效率,创造不如抄袭,创建新进程的最直接的就是copy当前shell进程,在copy的新进程中执行覆盖,命令程序覆盖copy的新进程,而当前的终端shell进程则被交换到磁盘保得全身。...换句话说,UNIX只是借用了fork的copy逻辑的实现,来完成一件别的事。 于是,UNIX非常粗暴的实现了fork!即完全copy父进程,这就是直到现在我们依然在使用的fork系统调用: ?
本文fork三部曲的后传,建议先阅读: 正传:《Linux fork那些隐藏的开销》 前传:《Unix/Linux fork前传》 在本文中,传统UNIX fork之后,我给出传统的UNIX fork...一个处理器未必处理特定的进程。 系统中进程数量和处理器数量不需要相等。 fork为上述的核心思想提供了实现的手段。后来fork被引入到UNIX系统,成了创建新进程几十年不变的通用操作。...在一个可并行的系统中,进程之间本就是资源隔离的,如果需要join操作,引入IPC机制便是。 线程概念的出现,就是对UNIX进程抽象的资源如何共享重新解构再重构。...Windows没有fork,它没有可以实现进程在任意点的分叉的机制。 当然,现实中,Windows可以使用多线程API CreateThread来干这件事。还可以大肆声张多线程要比多进程方案高效。...clone创建新进程,减少不必要的资源复制。 好了,这就是我要为你讲述的 “fork” 的故事。 ---- 浙江温州皮鞋湿,下雨进水不会胖。 (完)
操作系统管理和抽象底层硬件,例如: 文字处理器不需要关心使用哪种类型的磁盘硬件。 一个操作系统在多个程序之间共享硬件,这样它们就可以(或者看起来可以)同时运行。...(Unix的命令行用户界面)如何使用它们的讨论来阐释。...Shell对系统调用的使用说明了它们是如何被精心设计的。 Shell是一个普通的程序,它从用户那里读取命令并执行它们。...一个进程可以使用fork系统调用创建一个新的进程。 Fork创建了一个新的进程,其内存内容与调用进程(称为父进程)完全相同,称其为子进程。 Fork在父子进程中都返回值。...在父进程中,fork返回子类的PID; 在子进程中,fork返回零。
我们来看看如何60行C代码实现一个shell! 在实现它之前,先看看这样做的意义。 美是有目共睹的。Unix之美,稍微体会,便能得到。...Melvin Conway在1963年的论文中叙述fork思想时就解释说并行路径要用结果来交互,也就是在汇合的join点来同步结果。这个同步点所得到的,就是一个并行进程的 输出 。...然后我们通过pipe就能将它们组合成任意的数学式子。 现在谈谈Unix组合程序的具体写法,如果我们要化简薛定谔方程,我们应该如何用Unix命令写出与上述式子等价的组合程序命令行呢?...在Unix/Linux中,各种shell本身就实现了这样的功能,但是为了彻底理解这种处理方式的本质,只能自己写一个才行。来写一个微小的shell吧。.../div 6 3 6.000000 [root@10 test]# 计算结果显然是正确的。现在我在自己实现的tinysh中去做类似的事情: [root@10 test]# .
---- *Unix 在传统的Unix环境下,有两个基本的操作用于创建和修改进程: 函数fork( )用来创建一个新的进程,该进程几乎是当前进程的一个完全拷贝 函数族exec( )用来启动另外的进程以取代当前运行的进程...故: 父进程在执行if代码块的时候,fpid变量的值是子进程的pid,子进程在执行if代码块的时候,fpid变量的值是0 ---- 函数族exec( ) 在Linux中要使用exec函数族。...exec函数族不止一个,但它们大致相同,在 Linux中,它们分别是:execl,execlp,execle,execv,execve和execvp。...早期的 Unix 在实现 fork 系统调用时,并没有使用该技术,创建新进程的开销很大。...Linux在使用fork()函数进程创建时,传统fork()的做法是系统把所有的资源复制给新创建的进程,这种方式不仅单一,而且效率低下。因为所拷贝的数据或别的资源可能是可以共享的。
PID关系: 子进程的PID(进程标识符)是由父进程调用fork()或类似系统调用创建的。 子进程的PPID(父进程标识符)与创建它的父进程的PID相同。...但是,也有一些特殊情况下的进程,比如内核线程和守护进程,它们可能是由操作系统内核直接创建的,而不是由其他进程创建的。总体而言,大多数进程都是有其父进程创建的。...在Unix/Linux系统中,可以使用 getpid() 系统调用来获取当前进程的PID,使用 getppid() 系统调用来获取当前进程的父进程的PID。...fork()函数疑问 怎么理解fork()是系统调用也是库函数 fork 在 Unix-like 系统中既是一个系统调用,也是一个库函数,可以这样理解: 系统调用(System Call):...因此,fork 在执行时会返回两次:一次在父进程中(返回子进程的 PID),另一次在子进程中(返回 0)。这样做是为了让父进程和子进程可以根据返回值来执行不同的代码路径。
大家好,又见面了,我是全栈君。 线程是有趣的 线程类似于进程。如同进程,线程由内核按时间分片进行管理。在单处理器系统中,内核使用时间分片来模拟线程的并发运行。这样的方式和进程的同样。...管道,信号量或共享内存) UNIX PROCESS THREADS WITHIN A UNIX PROCESS 线程使用并存在于进程资源中,还能够被操作系统调用并独立地执行,这主要是由于线程只复制必要的资源以使自己得以存在并执行...因此,在UNIX环境下线程: 存在于进程,使用进程资源 拥有自己独立的控制流。...由于大部分额外开销已经在进程创建时完毕了 由于在同一个进程中的线程共享资源: 一个线程对系统资源(如关闭一个文件)的改变对全部其他线程是能够见的 两个相同值的指针指向相同的数据 读写同一个内存位置是可能的...在某些地方使用非常多CPU循环而其它地方没有。 对异步事件必须响应。 一些工作比其它的重要(优先级中断)。 Pthreads 也能够用于串行程序,模拟并行执行。
因此,你可以使用 libc::foo 这种形式访问这个库中的任何导出内容。 它可以与 std 配合使用,也可以在 no_std 环境下使用。...因此,可以直接这样说,Rust libc crate 在 Windows 平台上的功能有限。在 Windows 平台上,建议使用 winapi 这个 crate 进行开发。...举例:使用 libc 创建子进程 说得那么神乎其神,还是让我们见见 libc 的庐山真面目吧。...下面,我们就用一个示例——创建一个子进程——来展示 libc 的用法,以及与 Rust 标准库中线程操作的不同。 Rust 标准库中没有提供创建子进程的设施,不过可以创建一个子线程。...而 libc 可以对进程的操作(及后面对子进程的功能扩充,父进程中的信号管理等),做到完全的控制,更加灵活,功能强大; std 本身无法实现进程 fork 的功能。
2、基本概念 我们知道在unix/linux中,正常情况下,子进程是通过父进程创建的,子进程在创建新的进程。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束。 ...僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。...3、问题及危害 unix提供了一种机制可以保证只要父进程想知道子进程结束时的状态信息, 就可以得到。这种机制就是: 在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。...但这样就导致了问题,如果进程不调用wait / waitpid的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程...枪毙了元凶进程之后,它产生的僵死进程就变成了孤儿进 程,这些孤儿进程会被init进程接管,init进程会wait()这些孤儿进程,释放它们占用的系统进程表中的资源,这样,这些已经僵死的孤儿进程 就能瞑目而去了
大家好,又见面了,我是你们的朋友全栈君。 1、前言 之前在看《unix环境高级编程》第八章进程时候,提到孤儿进程和僵尸进程,一直对这两个概念比较模糊。...2、基本概念 我们知道在unix/linux中,正常情况下,子进程是通过父进程创建的,子进程在创建新的进程。...僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。...枪毙了元凶进程之后,它产生的僵死进程就变成了孤儿进 程,这些孤儿进程会被init进程接管,init进程会wait()这些孤儿进程,释放它们占用的系统进程表中的资源,这样,这些已经僵死的孤儿进程 就能瞑目而去了...在信号处理函数中调用wait进行处理僵尸进程。
/racing_mmap shm_p: 15 这段共享内存的使用是有竞争条件存在的,从文件锁的例子我们知道,进程间通信绝不仅仅是通信这么简单,还需要处理类似这样的临界区代码。...由于UNIX系统的历史悠久,在不同时间点的不同厂商和标准化组织定义过一些列标准,而目前比较通用的标准实际上是POSIX。...这意味着,我们在进程中不能用select、poll、epoll这样的方法去控制一个XSI共享内存,因为它并不是“文件描述符”。...而使用fork产生的子进程,则可以直接通过shmid访问到相关共享内存段。这就是key的本质:系统中对XSI共享内存的全局唯一表示符。 明白了这个本质之后,我们再来看看这个key应该如何产生。...shmget取到已经在父进程中创建好的共享内存id,注意shmget的第三个参数的使用。
在 Rust 中使用 nix 这个库,在某些情况下可以简化 Unix 系统编程。本文主要包括以下内容: 前言:什么是 Unix 系统编程?...在进行 Unix 系统编程时,关键要熟悉 POSIX 规范 中定义的接口函数,以及 Unix/Linux 的 man 手册,以下是一些示例: 进程管理(例如,fork,kill) 文件处理(例如,read...unistd,在 libc 库unistd.h头文件中函数的 safe 封装。 nix 库使用示例 在项目的 Cargo.toml 中添加如下配置,就可以导入 nix 库了。...[dependencies] nix = "0.17.0" 用 nix 如何创建一个子进程 我们用 nix 库重写 libc 文章中创建一个子进程的示例,代码如下: use nix::unistd::...上述代码中没有处理fork()函数失败时的逻辑,这样则可能将 -1(fork的错误结果)视为子进程的进程 ID。
( )来控制进程执行顺序 实验指导 一、所涉及的系统调用 在UNIX/LINUX中fork( )是一个非常有用的系统调用,但在UNIX/LINUX中建立进程除了fork( )之外,也可用与fork( )...fork( )只是将父进程的用户级上下文拷贝到新进程中,而exec( )系列可以将一个可执行的二进制文件覆盖在新进程的用户级上下文的存储空间上,以更改新进程的用户级上下文。...exec( )系列中的系统调用都完成相同的功能,它们把一个新程序装入内存,来改变调用进程的执行代码,从而形成新进程。...用fork( )建立子进程,然后在子进程中使用exec( ),这样就实现了父进程与一个与它完全不同子进程的并发执行。...为 了及时回收进程所占用的资源并减少父进程的干预,UNIX/LINUX利用exit( )来实现进程的自我终止,通常父进程在创建子进程时,应在进程的末尾安排一条exit( ),使子进程自我终止。
围绕这段代码,本文希望讲述清楚几个关键问题: 从进程的创建过程; 在使用同一主机地址的前提下,如果指定端口已经被监听,其它进程尝试监听同一端口时本应该会报错(EADDRINUSE,即端口已被占用);那么...进程 fork 是如何完成的?...在 Node.js 中,cluster.fork 与 POSIX 的 fork 略有不同:虽然从进程仍旧是 fork 创建,但是并不会直接使用主进程的进程映像,而是调用系统函数 execvp 让从进程使用新的进程映像...其实,通过指定 socketpair 的第一个参数为 AF_UNIX,表示创建匿名 UNIX 域套接字(UNIX domain socket),这样就可以使用系统函数 sendmsg 和 recvmsg...这样,当从进程往 fd=3 的流中写入数据时,主进程就可以收到消息;反之,亦类似。 ?
领取专属 10元无门槛券
手把手带您无忧上云