Linux创建进程的奇怪方式

合集 · 直观理解计算机技术【CoreDumped】 (68)

  1. 11:03
    代码中指定变量类型为何如此重要?
  2. 13:46
    栈为何如此之快?
  3. 17:53
    堆为何如此之慢?
  4. 19:56
    动态数组的内部机制(ArrayList VS LinkedList)
  5. 15:37
    探索计算机并发与多任务处理的历史
  6. 12:54
    晶体管是如何运行代码的?
  7. 16:12
    晶体管是如何记忆数据的?
  8. 18:47
    制作一个CPU来运行程序
  9. 11:23
    计算机如何将字符串转换为数字
  10. 17:18
    动态随机存取存储器(DRAM)的工作原理
  11. 16:05
    计算机处理器是如何运行条件和循环的?
  12. 6:51
    一个程序不是一个进程
  13. 14:52
    深入理解进程,如何进行上下文切换?计算机科学中最深刻和最成功的想法之一。
  14. 20:20
    处理器内部的单个位如何保护操作系统的完整性?
  15. 13:14
    计算的心跳:时钟如何推动 CPU 前进?
  16. 14:22
    英语原声版-计算的心跳:时钟如何推动 CPU 前进?
  17. 14:15
    (英语原声)进程间通信:共享内存或发送消息
  18. 12:29
    (中文配音)进程间通信:共享内存或发送消息
  19. 14:59
    (中文配音)为什么单核处理器需要线程?
  20. 16:07
    (英语原声)为什么单核处理器需要线程?
  21. 9:26
    (中文配音)多核系统上的线程
  22. 11:27
    (英语原声)多核系统上的线程
  23. 11:52
    (中文配音)为什么应用程序是特定于操作系统的?
  24. 13:09
    (英语原声)为什么应用程序是特定于操作系统的?
  25. 13:15
    (英语原声)硬件如何在多任务处理中协助软件?
  26. 11:29
    (中文配音)硬件如何在多任务处理中协助软件?
  27. 20:34
    链表的一个实际应用
  28. 45:02
    让你的电脑运行更流畅的奇妙算法
  29. 43:38
    (流畅中文配音)让你的电脑运行更流畅的奇妙算法
  30. 19:41
    (原声双语字幕)当程序调用 sleep() 时会发生什么?
  31. 17:57
    (流畅中文配音)当程序调用 sleep() 时会发生什么?
  32. 20:40
    这个简单算法驱动了真正的解释器:Pratt Parsing
  33. 18:19
    (双语字幕)编程中最难以捉摸的Bug:竞态条件
  34. 19:23
    (流畅中文配音)编程中最难以捉摸的Bug:竞态条件
  35. 16:02
    (中文配音)阻止内存攻击的底层电路
  36. 14:51
    (英语原声)阻止内存攻击的底层电路
  37. 18:04
    为什么有些项目会使用多种编程语言?
  38. 19:32
    为什么有些项目会使用多种编程语言?
  39. 17:15
    别再杀进程了!让 Ctrl+C 变成“喵”……(用信号实现)
  40. 19:23
    别再杀进程了!让 Ctrl+C 变成“喵”...(用信号实现)
  41. 19:05
    80 年代避免竞态条件的算法(以及它为何失败)
  42. 19:43
    80 年代避免竞态条件的算法(以及它为何失败)
  43. 22:10
    CPU如何与众多不同设备交互?
  44. 23:52
    (中文配音)CPU如何与众多不同设备交互?
  45. 14:04
    (中文配音)你每天都在用的奇特CPU操作
  46. 13:04
    你每天都在用的奇特CPU操作
  47. 18:04
    (中文配音)历史如何塑造了我们今天仍在使用的编程术语?
  48. 17:42
    (英语原声)历史如何塑造了我们今天仍在使用的编程术语?
  49. 16:41
    为什么有些底层项目里会充满这种奇怪的代码?
  50. 16:39
    (中文配音)为什么有些底层项目里会充满这种奇怪的代码?
  51. 21:35
    编译与解释代码如何共处同一程序中?
  52. 21:30
    (英语原声)编译与解释代码如何共处同一程序中?
  53. 17:09
    【双语字幕】Core Dumped 常见问题的集中回答
  54. 13:36
    【双语字幕】90年代的设计决策阻碍了Python并行化
  55. 12:01
    【中文配音】90年代的设计决策阻碍了Python并行化
  56. 17:51
    Linux创建进程的奇怪方式
  57. 18:14
    【中文配音】Linux创建进程的奇怪方式
  58. 19:49
    CPU如何运行函数?
  59. 19:38
    【中文配音】CPU如何运行函数?
  60. 17:15
    没有人解释的问题:内核的边界在哪里?
  61. 19:36
    【英语原声】没有人解释的问题:内核的边界在哪里?
  62. 35:49
    硬件如何让线程问题不再那么棘手?
  63. 19:47
    为什么在macOS、Windows和Linux上安装应用程序如此不同?
  64. 24:00
    游戏公司真的能破坏你的电脑吗?
  65. 10:43
    一个视频说透“架构”:为什么你的程序换个CPU就可能跑不了?
  66. 14:45
    CPU如何执行有符号和无符号运算?
  67. 19:11
    简单指令, 奇妙算法
  68. 9:26
    当CPU遇到它不认识的指令,会发生什么?😱【底层原理】
Description
https://www.youtube.com/watch?v=SwIPOf2YAgI
本视频深入解析操作系统中的进程创建机制。
重点比较了Windows和Unix/Linux系统在进程创建方面的不同实现方式及底层原理。
开场问题:为什么像 Discord 这样的 Electron 应用一启动就能自动冒出多个进程,而不用我们手动先起服务器再开浏览器?
核心限制:用户进程自己没权限“造”新进程,必须把请求交给操作系统——也就是“系统调用”。
两条路线
‑ Windows:一条 CreateProcess 系统调用直接“生”新进程,参数简单直白。
‑ Unix/Linux:必须“两步走”——先 fork(把自己完整克隆一份),再在子进程里 execve(把克隆体替换成目标程序)。
fork 的细节
‑ 克隆后父子进程从同一位置继续执行,靠返回值区分:0=子进程,正数=父进程(得到的正是子进程 ID)。
‑ 若在循环里乱放 fork,会出现指数级“进程爆炸”。
execve 的细节
‑ 并不创建新进程,而是把当前进程“脱胎换骨”直接换成另一个可执行文件;成功后原程序后续代码永不会执行。
组合套路:实际写代码时,用 fork 的返回值判断,仅让子进程调用 execve,于是父进程继续干自己的事,子进程变身成全新程序——这就是 Unix 世界 99% 新进程的诞生方式。POSIX 的 spawn 函数内部也是这套逻辑。
进程树:所有用户进程都是“别人生的”,一路追溯能画出大树;树根那个“1 号进程”不是用户进程,而是内核启动后的第一个“祖宗”,留待下回分解。
收束回开场:Discord 启动后,第一个进程就按上述套路不停 fork+exec,瞬间拉起语音、渲染、GPU、扩展等一堆子进程,用户感知不到,却构成了完整应用。

Comments

蓝色的人造卫星 2026-01-20

unix的fork乍看难以理解,实际上比nt这个CreateProcess灵活很多,因为创建进程和运行程序本来就是两个操作,只fork然后分别跑两个函数完全可行,shell环境运行一些不需要exit的命令可以只用exec。也因为unix fork非常方便,所以unix传统上重进程轻线程(靠谱的pthread已经是比较晚近的东西了),而Windows创建进程开销很大,所以往往更倾向于多线程

♥ 93 ↩ 27

是钾离子呀 2026-01-20

python+fork非常地绝望,因为引用计数,读取一个变量就会改变其内存,所以cow会复制一段内存,如果程序很大而且fork了,那慢慢地子进程就会占用超多内存[笑哭]

♥ 37 ↩ 13

珞琼星 2026-01-20

《EXE》[doge]

♥ 20 ↩ 2

星天浩瀚 2026-01-21

[doge]那如果自身进程很大,那要清空重建不是很浪费?

♥ 12 ↩ 6

账号已注销 2026-01-21

其实我觉得两个模型都不够优雅,我给自己OS设计的创建进程接口是一个类似ptrace的接口,父进程先通过调用创建一个空的进程上下文,然后通过类似ptrace的调用在里面mmap可执行文件,设置初始的寄存器,包括如果需要的话把想要继承的fd放回去,然后调用detach让这个上下文开始运行。好处首先类似shebang(binfmt)的功能不需要内核参与了,让libc搜索加载共享库就行,然后各种继承语义更加一致,比如sigaction可以继承IGN和TERM但是不能继承用户定义函数这个语义感觉不对称,解决了,然后想要修改子进程的环境也不需要先调整自己本身的参数再调回来,消除了可能的竞态,等等。

♥ 7 ↩ 1

qliqww 2026-01-21

把一步操作,分解成两个更基本操作的组合,符合Unix“大道至简”的哲学。Unix系统最底层的操作系统调用都是基本得不能再基本的,其它功能可以由这些最底层功能的组合来实现。fork()和execve()的组合可以实现windows的CreateProcess(),在运行全新程序的时候可能会损失一些性能,但fork()和execve()都有各自的妙用,有的场合可以增强性能。Windows API以实用为目的,并不追求什么哲学,用起来越方便越好,因而Windows API提供了数千个标准的API函数。

♥ 7

PumpkinXD 2026-01-21

其实支持posix的系统通常还有posix_spawn

♥ 6

ListProcessing 2026-01-21

极端丑陋,现代做法是创建进程对象传回调函数,直观很多

♥ 6 ↩ 5

21世纪福音战士塔兰特 2026-01-20

本科的时候差点没学死[doge]

♥ 6

柒星玖曜 2026-01-20

秒了[doge]

♥ 6 ↩ 1

抱歉我只会举一反三 2026-01-23

这集真的是任何语言的程序员都要看看。

♥ 4 ↩ 1

aiue0oo 2026-02-10

linux的进程管理也确实有点bt的,以前为了实现daemon需要做很多前置步骤,需要了解会话 进程组 父子进程退出的概念,学习成本确实很劝退人

♥ 3

鸽子飞了咕咕咕 2026-01-23

通过Fork 的返回值就能判断父子进程吧,父进程返回子进程pid 子进程返回0

♥ 3 ↩ 1

遥不可及的星星的梦 2026-01-21

我觉得这挺好的,有个好处就是我还可以不 fork 就 exec,把自己变成另一个程序同时 pid 不变,也不会在进程树上产生多余的中间进程,而 windows 就做不到这事了

♥ 3 ↩ 6

昵称有害视力 2026-01-21

fork和创建进程就是完全不同的两个事情

♥ 3 ↩ 1

CharlesCTy 2026-04-30

2026年了,仍然有人认为他们的计算机只不过是一个快一点的PDP-11,内存当然是平坦的而且很小,有CoW复制一下开销肯定不大,多线程是不用考虑的,至于safety和security,连想都没想过,这是为什么fork、signal、setjmp/longjmp、memory overcommit ……这一套历史遗留问题能继续得到认可的原因

♥ 2

账号已注销 2026-02-14

各有优缺点,但我是来看linux宗教徒的[吃瓜]

♥ 2 ↩ 2

28283844972_bili 2026-01-21

话又说回来了,在类Unix系统中,用户空间1号进程作为所有用户态进程的父进程,既然所有进程都是父进程fork得到,那么作为首个进程又是如何创建的呢?这是个典型的“鸡生蛋,蛋生鸡”的问题。内核态“0号”进程在初始化完成后,创建了用户态1号进程,那么内核态首个进程如何创建的呢?这属于“无中生有”的问题,往后几期视频不就有了[doge]

♥ 2 ↩ 7

forjanezz 2026-02-08

https://cloud.tencent.com/developer/article/1498377 可以参考一下

♥ 1