「转」彻底理解Linux的各种终端类型以及概念

为了防止走丢,做了全文转载。 原文出自:彻底理解Linux的各种终端类型以及概念 作者:dog250 每天使用Linux每天都要接触到Bash,使用Bash时似乎永远都让人摸不着头脑的概念就是终端,坐在这台运行着Linux的机器的显示器前面,这个显示器就是终端的输出,而插在机器上的USB键盘或者PS/2键盘就是终端的输入,看来这是一种最直白意义上关于终端的解释。   但是有的时候,机器上并没有看到显示器或者键盘接口,但是却有一个串口,想操作这台机器想必只能通过这个串口来进行了,这个时候,串口另一端的那台电脑的显示器键盘也叫做终端。除了上述两种意义的终端之外,我们使用的类似SecureCRT这种软件上运行的SSH,Telnet等也算是一种终端程序,只是说它是通过TCP/IP网络而不是通过串口与主机连接的。   现在可以给终端下一个非严格意义上的定义了,什么是终端?终端就是处理计算机主机输入输出的一套设备,它用来显示主机运算的输出,并且接受主机要求的输入,典型的终端包括显示器键盘套件,打印机打字机套件等。但想要彻底理解终端的概念,还是要从计算机发展历史的角度去寻根溯源。   最开始的时候,计算机有三间房屋那么大,确切地讲应该叫三间车间。如此的庞然大物有一个专门的操作台,就好像机床厂车间的操作台一样,或者说它像飞机驾驶舱的操作台更加合适,各种仪器仪表,操作员只需要在这里对这部机器发出指令,整部机器就开始为他的指令而运算,然后机器运算后的结果也会反馈到这里而不是其它地方,这里这个操作台就是最原始的终端。这里曾经是整部机器的控制中枢。 后来有了多用户多任务分时系统,不同的程序竟然可以“同时运行”了,为了让不同的程序分别独立地接受输入和处理输出,就需要多个不同的上述的操作台,当然了,坐在或者站在操作台前面的最好始终是同一个人,这样不同的人拥有不同的操作台处理不同的程序,这就进入了多终端时代,从这时起一直到现在,每一个终端都是和一个用户绑定的。为了保证这种绑定,于是就出现了登录,即通过一种叫做登录的动作,去唤起一个终端起来工作。为了支持多用户,终端从硬件分离了出来,终端成了一个软件概念,在一个硬件终端上成功登录后,便获得了一个软件终端。   可见,这个时代已经和三车间的时代不同了,终端不再只有一个,而是变成了多个,每一个登录成功的用户拥有一个可工作的软件终端来处理输入输出。 分久必合。   到了个人计算机时代,计算机和终端又成了一对一的关系。毕竟嘛,这时的计算机叫做个人计算机,并不是随便谁都能用的,计算机本身就是归属个人,所以根本没必要去支持什么多用户,或者至少是淡化了多用户和多终端的概念。我们都曾记得,当时买电脑的时候,都是一个主机配一个显示器和一套键盘鼠标,这种情况从上世纪80年代初一直持续到今天。不过近些年来当人们逐渐全面认识到计算机和终端的一对一关系后,一体机的市场就来了,既然你几乎不会(我当然知道有人会,但这里我说的是大多数人,程序员占比寥寥,程序员为了装X,是不会用一体机的,就连品牌机套装有时也不屑的)在同一主机上接多个显示器多套键盘,何必再那么麻烦,干脆把主机和显示器合在一起不就好了嘛。嗯,这个点子不错,循着这个路子,最终有了触屏一体机,连键盘都内置了。对比一下下图和三车间里的计算机时代,是不是很像呢? 但是好景不长。   合久必分。   一切似乎又回到了大型机时代。在大型机时代,一台机器是拥有多个终端的,那是五十年以前。今天,我们拥有了各种各样的小型设备,智能手机,平板电脑,智能手表….然而这些东西,其实仅仅只是一系列的终端而已!那么既然这些东西都成了终端,真正的计算机在哪儿?当然在各大机房(也是类似车间大小的那种房间)里了,只是现在不叫大型机了,而叫做云端,这种技术叫做云计算(似乎有点炒作概念的意思)。如果你不信你花了几千上万块的钱买来的设备仅仅是一个完成输入输出功能的终端,那么请断网试试,看看你的iPhone是不是变砖头了。可见,昂贵的是云提供的计算服务,而不是终端设备本身,我们把所谓的云看作是一台计算机,这幅图景是不是跟五十年前的非常像呢? 你有多久没有打开过家里的PC了,是不是很久了,但是日子也还过得去。但是你能忍受哪怕几个小时不登录微信吗?某种意义上,成为新的终端的不是这些个硬件设备,而是基于云计算技术的现代互联网服务的各类APP。 …   是不是又要分久必合了呢?早就有迹象了,从用QQ号可以登录微信,微博,内推网的时候就有迹象了。 好了,扯了这么多关于终端的发展,其实根本上也就一句话,能接受输入,能显示输出,就这就够了,不管到了什么时代,终端始终扮演着人机接口的角色,所谓Terminal,即机器的边缘!   只要能提供给计算机输入和输出功能,它就是终端,而与其所在的位置无关。我可以用ls命令列举五千公里以外的一台计算机上某个目录下的文件并且显示在我眼前的屏幕上,至于我的输入如何到达五千公里以外,这并不是我要关注的,也不是计算机要关注的,这显然只是一个通信方式问题。那么使用TCP/IP网络进行这类通信传输就是再显然不过的了。   这就是SSH使用的方法。我们知道,SSH是一个TCP/IP协议族的协议,而其上跑的却是一个远程登录后的终端流,这显然只是用TCP/IP构建了一条隧道,然后终端流通行于该隧道。除此之外,更简单的Telnet也不例外,也是通过一个TCP/IP隧道来封装承载远程登录的终端流。除却TCP/IP,如果我们执意使用卡车来运载我们的输入和输出,也完全是合适的,TCP/IP也好,卡车也好,它们只是通信手段,它们并非终端本身。 我们现在可以想象一下终端存在的形式都会有哪些。 本地终端 用VGA连接主机和显示器,用PS/2或者USB连接主机和键盘,这样的一个显示器/键盘组合就是一个本地终端。用串口连接的远程终端 通过串口线把主机接到另外一个有显示器和键盘的主机,通过运行一个终端模拟程序,比如“Windows超级终端”来将这台主机的显示器和键盘借给串口对端的主机。用TCP/IP承载的远程终端 类似Telnet,SSH这般。 大致就先说这几类吧。可见上述的三类中,前两类都是在本地就直接关联了物理设备的,比如VGA口啊,PS/2口啊,串口啊之类的,这种终端叫做物理终端,而第三类在本地则没有关联任何物理设备,注意,不要把物理网卡当成终端关联的物理设备,它只是隧道关联的物理设备,这里的物理网卡完全可以换成卡车,它们与终端并不直接相关,所以这类不直接关联物理设备的终端叫做伪终端。   既然知道了这些终端到底是怎么回事,理解余下来的那些术语就不在话下了。这些术语的存在并非是为了故意增加复杂性,而是因为工程上的东西必须要有可操作性,要可操作就必须至少有个名字来称呼,仅此而已。这跟我们中国的传统道,可道非常道;名,可名非常名是完全不同的。可谓现代数学,既要有名又要有道,而现代工程,则必须舍道而取名。   先看下Linux系统中管终端都叫做什么。 tty是最令人熟悉的了,在Linux中,/dev/ttyX代表的都是上述的物理终端,其中,/dev/tty1~/dev/tty63代表的是本地终端,也就是接到本机的键盘显示器可以操作的终端。换句话说,你往/dev/tty3里写个东西,它就会显示在显示器对应的终端。   为什么会有63个终端这么多呢?毕竟显示器只是一个单独的显示设备,键盘往往也只有一个,但Linux内核有能力知道现在该干什么,所以事实上Linux内核在初始化时会生成63个本地终端,通过键盘上的Fn-Alt-FX(X为1,2,3…)可以在这些终端之间切换,每切换到一个终端,该终端就是当前的焦点终端,比如说,你按下了Fn-Alt-F4组合键,那么此时第4个终端就是焦点终端,即/dev/tty4就是焦点终端设备。 谁是焦点终端会被内核记录为全局变量,这样只要有键盘输入,就会把输入的字符交给焦点终端。这里顺便提一下,对于串口而言,不存在焦点终端的概念,谁连了串口就是谁,而对于伪终端来讲,一般情况下client都是运行在GUI环境,对于Windows那是微软的事,对于Linux,则有X系统完成同样的事,在此略过,继续我们正在说的话题。   系统中有没有什么变量可以表示焦点终端呢?当然有了,那就是/dev/console,不管你在哪里往/dev/console里写东西,这些东西总会出现在系统当前的焦点终端上!   按照以他人为中心,我们解释了/dev/console其实就是一个全局变量,指代当前的焦点终端,如果当前的焦点是/dev/tty4,那么/dev/console指的就是/dev/tty4,当然这一切都是由内核来维护的。   那么系统中有没有一个叫做自己的全局变量呢?当然有,那就是/dev/tty,也就是说,无论你在哪个终端下工作,当你往/dev/tty里写东西的时候,它总是会马上出现在你的眼前。   /dev/tty1~/dev/tty63我们知道了它们是什么,/dev/tty表示自己,/dev/console表示焦点终端这些我们也知道了,那么串口终端如何表示呢?很简单,以ttyS 开头的就是串口连接的终端,比如ttyS1,ttyS2…   最后,解释一下伪终端。其实也很好解释,只要你理解TUN/TAP虚拟网卡的原理就行,它们如出一辙!类似Telnet,SSH不是没有实际的物理设备吗?简单,给它模拟一个不就得了?系统是分层的,执行流只管调用接口,并不管具体实现。   模拟一个虚拟的终端设备,实现它的write,read等回调即可。对于VGA连接的显示器而言,write其实就是将显存刷新,而对于伪终端而言,write其实是想将数据导入到一个用户态的程序中(不然又能去哪里呢?它下面又没有任何物理的东西),这简直跟很多VPN的原理非常类似。为此,Linux设计出一对虚拟终端设备,即/dev/ptmx和/dev/pts/X,这就跟TUN/TAP网卡的网卡与字符设备之前的对应关系一致。   简单来讲,当有ssh客户端连接后,sshd会fork一个进程,然后在子进程中打开一个叫做/dev/pts/1(或者2,3,4,5…)的设备,然后和sshd进程的/dev/ptmx配对,这样在ptmx与pts之间就构成了一条管道,数据可以顺利被导入到sshd,然后通过TCP/IP封装发往ssh client所在的机器。   为了帮助理解上述的文字,我特意作图一张,希望能解释清楚这些终端之间的关系以及弄明白它们的工作流程。为了让图画的更加紧凑,避免横向网络吧图拉的过长而不好看,我这里采用了环形解释法,类似Intel早先的Ring1,Ring2,Ring3,我把最内层视作硬件(比它更里面的还有叫做人的东西),中间层视作内核,最外层视作软件。 理解了图例,我上我的图,这是我昨晚画到很晚才完成的,希望能有宝贵的意见提出(图有点大,请单独查看): /dev目录下的各种tty,ptmx,pts/X,console等等这些是令人混淆的根源,其实理解这些是有窍门的,记住它们只是操作某种终端设备的设备文件而已,这是UNIX风格的延续,这些设备文件对应的真实设备也就那么几种,比如显示器键盘套件,串口对面的超级终端,伪终端对面的SSH,Telnet等等。然后试着画出一个上面的图,基本就理清楚了。 本文的最后,我来简单说下关于getty和login相关的东西。   前面在讲终端发展历史的时候说到过,到了多终端时代,每一个终端必须绑定一个用户,只有登录成功的用户方可获得一个终端。因此当一个人站到一个终端面前并不意味着它就能在这个终端上操作计算机,他首先要做的就是登录。所谓的登录呢,就是输入用户名和密码,如果输入正确,则会给你一个Bash(或者别的Shell)让你操作计算机,如果输入不正确,则让你继续输入…   getty给了让你登录并且继续输入的机会!init进程不断调用getty,然后getty会发起login让你登录,当你输入正确的用户名和密码后,ttyXYZ就是你的了,如果你是用SSH进行的login,那么你将得到一个叫做/dev/pts/X,如果你是在显示器键盘登录,你将得到/dev/tttX(X取决于当前的焦点终端)。   所有这一切其实都是多终端以及多用户的产物,但归根结底其根源都在分时系统。在计算机最初被放在车间大小的屋子里的年代,可能把屋子的门禁做好以及将屋子外的鉴权系统做好显得比后来的多用户login更为重要,只有在后来,终端不再属于计算机了,终端与计算机分离了,用户也和终端分离了的时候,设计一套登录机制就显得尤为必要了,因为首先即便你把计算机锁在铁屋子里,只要终端在外面,那么计算机就毫无安全感可言,其次,你也不可能把终端全部锁在完全属于你控制的铁屋子里,特别是在TCP/IP出现以后,几乎所有的计算机都是互联互通的,这意味着任何一台计算机都可以作为其它任何一台另外的计算机的操作终端,任何外部的鉴权系统和物理保护在TCP/IP网络面前都堪比马其诺防线,看似固若金汤,实则百无一用。

2022-03-16 15:20:50 · 王二

Laravel 上传 Docx 文件,表单验证不通过

今天客户反映,上传文件报错。 拿到客户的文件 xxxx.docx 自己测试,接口返回 file 必须是一个 jpeg, bmp, png, jpg, doc, docx, xls, xlsx, pdf 类型的文件。,表单验证没通过。 但是项目配置里面已经允许 docx 类型的文件了,为啥会提示文件类型不对。 打印了下文件的 mimeType,结果为 application/vnd.openxmlformats-officedocument.wordprocessingml.document。 了解了下 openxml: Office Open XML(缩写:Open XML、OpenXML或OOXML),为由 Microsoft 开发的一种以 XML 为基础并以ZIP格式压缩的电子文件规范,支持文件、表格、备忘录、幻灯片等文件格式。 ——openxml 既然以 ZIP 格式压缩,那给允许的文件类型里面加上 zip 试试。 试了一下,可以通过验证了。

2022-03-16 12:48:30 · 王二

4-4 发送信号

发送信号的几种形式 kill -s 信号编号|信号名字 进程PID 在程序中使用posix_kill 给一个指定的进程或是进程组发送信号 pcntl_alarm SIGALRM 在终端按下特殊键 ctrl+c、 ctrl+z、ctrl+\ 网络 SIGURG、SIGPIPE、SIGCHLD(当子进程结束的时候) pcntl_signal_dispatch pcntl_signal_dispatch,调用等待信号的处理器。 调用每个等待信号通过 pcntl_signal 安装的处理器。 posix_kill posix_kill,Send a signal to a process. Send the signal to the process with the process identifier process_id. kill, send signal to a process. The kill() system call can be used to send any signal to any process group or process. If pid is positive, then signal sig is sent to the process with the ID specified by pid....

2022-03-15 17:42:37 · 王二

4-3 信号集

信号集 信号集是指信号的集合。 主程序可以选择阻塞某些信号,被阻塞的信号集称为阻塞信号集。 当进程阻塞了某个信号(通过 pcntl_sigpromask 来设置信号屏蔽字),如果在运行期间接收到了阻塞的信号时,这个信号的处理程序不会被执行,这个信号会放在被挂起的信号集里(信号未决集)。 sigpending PHP 没有实现这个函数。 examine pending signals. sigpending() returns the set of signals that are pending for delivery to the calling thread (i.e., the signals which have been raised while blocked). The mask of pending signals is returned in set. pcntl_sigpromask pcntl_sigpromask 设置或检索阻塞信号,用来增加,删除或设置阻塞信号,具体行为 依赖于参数how。 pcntl_sigprocmask(int $how, array $set, array &$oldset = ?): bool 参数: how: 设置 pcntl_sigprocmask()函数的行为。 可选值: SIG_BLOCK: 把信号加入到当前阻塞信号中。 SIG_UNBLOCK: 从当前阻塞信号中移出信号。 SIG_SETMASK: 用给定的信号列表替换当前阻塞信号列表。 set:...

2022-03-15 16:54:58 · 王二

4-2 编写中断信号处理程序

pcntl_signal 信号处理函数 安装一个信号处理器。 说明: pcntl_signal(int $signo, callback $handler, bool $restart_syscalls = true): bool 函数 pcntl_signal 为 signo 指定的信号安装一个新的信号处理器。 参数: signo 信号编号。 handler 信号处理器可以是用户创建的函数或方法的名字,也可以是系统常量 SIG_IGN(译注:忽略信号处理程序)或SIG_DFL(默认信号处理程序). 注意: 注意当你使用一个对象方法的时候,该对象的引用计数回增加使得它在你改变为其他处理或脚本结束之前是持久存在的。 restart_syscalls 指定当信号到达时系统调用重启是否可用。(译注:经查资料,此参数意为系统调用被信号打断时,系统调用是否从 开始处重新开始,但根据http://bugs.php.net/bug.php?id=52121,此参数存在bug无效。 成功时返回 true, 或者在失败时返回 false。 中断系统调用 当进程正在执行系统调用的时候,接收到中断信号,那么这个系统调用就会被中断,比如说进程正在写文件,无法恢复。 如果能恢复我们称为:可重入函数,否则就是非可重入函数。 若一个程序或副程序可以“在任意时刻被中断然后操作系统调度执行另一段代码,这段代码又使用了该副程序不会出错”,则称其为可重入(reentrant 或 re-entrant)的。即当该副程序正在运作时,执行线程可以再次进入并执行它,仍然可得到符合设计时所预期的结果。与多线程并发执行的线程安全不同,可重入强调对单一线程执行时重新进入同一个子程序仍然是安全的。 ——可重入 一般在中断信号处理函数,不要写太多的业务逻辑。 我们经常把中断信号用于通知。 中断信号动作 每个信号都有相应的动作(信号处理程序): 用户自定义的中断信号处理程序 SIG_DEF 系统默认动作(结果一般都会让进程终止或是停止,终止+core) 忽略 SIG_IGN ignore 进程启动的时候,信号的动作默认是系统行为,如果编写对应处理程序,会覆盖掉默认动作,有些信号不可以覆盖,例如 SIGKILL、SIGSTOP。 信号处理程序的继承 当父进程创建一个子进程的时候,子进程是继承父进程的中断信号处理程序的。 function sigHandler($signo) { fprintf(STDOUT, "pid = %d,我接收到一个信号:%d \n", posix_getpid(), $signo); } pcntl_signal(SIGINT, 'sigHandler'); pcntl_signal(SIGUSR2,SIG_IGN); // 忽略信号 // SIGKILL、SIGSTOP 信号是无法捕捉的,编写的信号处理程序不会执行 // pcntl_signal(SIGKILL, 'sigHandler'); // pcntl_signal(SIGSTOP, 'sigHandler'); $pid = pcntl_fork(); while (1) { pcntl_signal_dispatch(); fprintf(STDOUT, "pid = %d, main process doing something ....

2022-03-15 10:57:49 · 王二
看鹅

2022-03-10 面试复盘

笔试题 40分钟,A4纸手写。 好多年都没握过笔了。 MVC 中的 M, V, C 分别代表什么? 我的答案: Model View Controller 分析:回答比较粗浅,只写出了完整的单词。 以下代码的运行结果 $value = FALSE; if (empty($value)) { echo 'null'; } else { echo 'have value'; } 我的回答:have value 正确答案:null 分析:对 empty 方法记忆模糊不清。 判断一个变量是否被认为是空的。当一个变量并不存在,或者它的值等同于false,那么它会被认为不存在。如果变量不存在的话,empty()并不会产生警告。 ——empty 写出示例中日本电话号码的正则:03-1234-5678,06-1224-5989 我的回答:正则不熟悉,随便写了些。 正确答案: 改进:系统学习下正则,之前也学过,用的时候还是习惯去搜现成的表达式。归根结底还是没学会。 在线正则表达式 正则表达式 – 教程 | 菜鸟教程 正则表达式 - JavaScript | MDN 写出下面代码的运行结果: $arr = [2, 3, 5, 8, 10, 9, 7, 5, 9]; // 2 3 5 8 10 9 7 5 9 // 0 1 2 3 4 5 6 7 8 // 0 5 // 1 8 // 2 9 // 3 9 // 4 数组没有下标为10的元素 null $num = 0; for ($i = 0; $i < 5; $i++) { $num += $arr[$arr[$i]]; } echo $num; 我的回答:31...

2022-03-11 18:03:51 · 王二

4-1 什么是中断信号(软中断)

中断信号 指软件中断信号,简称软中断。 中断信号处理程序(信号处理函数,信号捕捉函数)完以后,就会返回继续执行主程序。 中断是用以提高计算机工作效率、增强计算机功能的一项重要技术。最初引入硬件中断,只是出于性能上的考量。如果计算机系统没有中断,则处理器与外部设备通信时,它必须在向该设备发出指令后进行忙等待(Busy waiting),反复轮询该设备是否完成了动作并返回结果。这就造成了大量处理器周期被浪费。 引入中断以后,当处理器发出设备请求后就可以立即返回以处理其他任务,而当设备完成动作后,发送中断信号给处理器,后者就可以再回过头获取处理结果。这样,在设备进行处理的周期内,处理器可以执行其他一些有意义的工作,而只付出一些很小的切换所引发的时间代价。后来被用于CPU外部与内部紧急事件的处理、机器故障的处理、时间控制等多个方面,并产生通过软件方式进入中断处理(软中断)的概念。 ——中断 中断尽管可以提高计算机处理性能,但过于密集的中断请求/响应反而会影响系统性能。这类情形被称作中断风暴(interrupt storm)。 中断处理过程示意图 中断源 就是产生中断信号的单元。 在终端按下按键产生的中断信号 ctrl+c, ctrl+z, ctrl+\ 硬件异常 在终端使用 kill 来发送中断信号 posix_kill / kill(2) 函数、pcntl_alarm / alarm(2)函数 软件产生的中断信号 SIGURG [TCP/IP],SIGALRM 中断响应 对信号的处理。 忽略 执行中断处理函数(捕捉信号执行信号处理函数) 执行系统默认 signal ===> 动作[忽略,默认,执行用户编写好的信号处理函数] 中断返回 中断服务程序运行完之后返回。 信号对进程的影响: 直接让进程终止 让进程停止 SIGCONT 可以唤醒进程到前台继续运行 demo11.php <?php echo posix_getpid(); while (1){ ; } 发送 SIGSTOP 让进程停止之后 [1] Killed php demo11.php [2] Killed php demo11.php [3]+ Stopped php demo11.php $jobs [1]+ Stopped php demo11....

2022-03-09 11:27:09 · 王二

3-7 进程查看

一个程序启动之后就是一个进程,进程的数据在内存中,包括正文段和数据段,内存中的一些数据也会写入到proc文件系统中。 $ ll /proc/ total 4 dr-xr-xr-x 447 root root 0 Mar 8 06:31 ./ drwxr-xr-x 1 root root 4096 Mar 1 07:45 ../ dr-xr-xr-x 9 root root 0 Mar 8 11:43 1/ dr-xr-xr-x 9 root root 0 Mar 8 11:47 12/ dr-xr-xr-x 9 root root 0 Mar 8 11:47 21/ dr-xr-xr-x 9 root root 0 Mar 8 11:47 22/ dr-xr-xr-x 9 root root 0 Mar 8 11:47 23/ dr-xr-xr-x 9 root root 0 Mar 8 11:47 24/ dr-xr-xr-x 9 root root 0 Mar 8 11:47 3252/ dr-xr-xr-x 9 laradock laradock 0 Mar 8 11:47 499/ dr-xr-xr-x 9 root root 0 Mar 8 11:47 983/ top top - display Linux processes....

2022-03-08 15:20:35 · 王二

3-6 SUID特权进程

SUID、SGID 概念 The Unix access rights flags setuid and setgid (short for “set user ID” and “set group ID”) allow users to run an executable with the file system permissions of the executable’s owner or group respectively and to change behaviour in directories. They are often used to allow users on a computer system to run programs with temporarily(暂时、临时) elevated(提高) privileges in order to perform a specific task. While the assumed user id or group id privileges provided are not always elevated, at a minimum they are specific....

2022-03-08 10:19:25 · 王二

3-5多进程编写

多进程编写需要清楚的几个问题 创建了几个进程? 每个进程 $count 是多少? 每个进程从哪个地方开始运行代码的? fork 之后,每个进程的变量 $i, $count 的值是多少 ? 每个进程运行到哪一行语句结束? 示例一: 代码 <?php $count = 10; for ($i = 0; $i < 2; $i++){ $pid = pcntl_fork(); // step1-1 parent 创建子进程 child-1: count = 10, i = 0 // step3-1 cpu 调度 parent 创建子进程 child-2: count = 100, i = 1 // step5-1 cpu 调度 child-1 创建子进程 child-3: count = 11, i = 1 if ($pid == 0){ $count += 1; // step1-2 child-1 执行:count = 11, i = 1 // step4 child-2 执行: count = 101, i = 2 // child-2 最终结果:count = 101 // step6 child-3 执行:count = 12, i = 2 } else { $count *= 10; // step2 cpu 调度 parent:count = 100, i = 1 // step3-2 cpu 调度 parent:count = 1000, i = 2 for循环退出 // parent 最终结果:count = 1000 // step5-2 cpu 调度 child-1:count = 110, i = 2 // child-1 最终结果:count = 110 } } while(1){ fprintf(STDOUT, "pid=%d,count=%d\n", posix_getpid(), $count); sleep(3); } 分析: step1-1 parent 创建子进程 child-1:count = 10, i = 0...

2022-03-07 12:15:30 · 王二