趣谈Linux操作系统
刘超
网易杭州研究院云计算技术部首席架构师
立即订阅
19315 人已学习
课程目录
已完结 72 讲
0/4登录后,你可以任选4讲全文学习。
入门准备篇 (3讲)
开篇词 | 为什么要学习Linux操作系统?
免费
01 | 入学测验:你究竟对Linux操作系统了解多少?
02 | 学习路径:爬过这六个陡坡,你就能对Linux了如指掌
核心原理篇:第一部分 Linux操作系统综述 (3讲)
03 | 你可以把Linux内核当成一家软件外包公司的老板
04 | 快速上手几个Linux命令:每家公司都有自己的黑话
05 | 学会几个系统调用:咱们公司能接哪些类型的项目?
核心原理篇:第二部分 系统初始化 (4讲)
06 | x86架构:有了开放的架构,才能打造开放的营商环境
07 | 从BIOS到bootloader:创业伊始,有活儿老板自己上
08 | 内核初始化:生意做大了就得成立公司
09 | 系统调用:公司成立好了就要开始接项目
核心原理篇:第三部分 进程管理 (10讲)
10 | 进程:公司接这么多项目,如何管?
11 | 线程:如何让复杂的项目并行执行?
12 | 进程数据结构(上):项目多了就需要项目管理系统
13 | 进程数据结构(中):项目多了就需要项目管理系统
14 | 进程数据结构(下):项目多了就需要项目管理系统
15 | 调度(上):如何制定项目管理流程?
16 | 调度(中):主动调度是如何发生的?
17 | 调度(下):抢占式调度是如何发生的?
18 | 进程的创建:如何发起一个新项目?
19 | 线程的创建:如何执行一个新子项目?
核心原理篇:第四部分 内存管理 (7讲)
20 | 内存管理(上):为客户保密,规划进程内存空间布局
21 | 内存管理(下):为客户保密,项目组独享会议室封闭开发
22 | 进程空间管理:项目组还可以自行布置会议室
23 | 物理内存管理(上):会议室管理员如何分配会议室?
24 | 物理内存管理(下):会议室管理员如何分配会议室?
25 | 用户态内存映射:如何找到正确的会议室?
26 | 内核态内存映射:如何找到正确的会议室?
核心原理篇:第五部分 文件系统 (4讲)
27 | 文件系统:项目成果要归档,我们就需要档案库
28 | 硬盘文件系统:如何最合理地组织档案库的文档?
29 | 虚拟文件系统:文件多了就需要档案管理系统
30 | 文件缓存:常用文档应该放在触手可得的地方
核心原理篇:第六部分 输入输出系统 (5讲)
31 | 输入与输出:如何建立售前售后生态体系?
32 | 字符设备(上):如何建立直销模式?
33 | 字符设备(下):如何建立直销模式?
34 | 块设备(上):如何建立代理商销售模式?
35 | 块设备(下):如何建立代理商销售模式?
核心原理篇:第七部分 进程间通信 (7讲)
36 | 进程间通信:遇到大项目需要项目组之间的合作才行
37 | 信号(上):项目组A完成了,如何及时通知项目组B?
38 | 信号(下):项目组A完成了,如何及时通知项目组B?
39 | 管道:项目组A完成了,如何交接给项目组B?
40 | IPC(上):不同项目组之间抢资源,如何协调?
41 | IPC(中):不同项目组之间抢资源,如何协调?
42 | IPC(下):不同项目组之间抢资源,如何协调?
核心原理篇:第八部分 网络系统 (7讲)
43 预习 | Socket通信之网络协议基本原理
43 | Socket通信:遇上特大项目,要学会和其他公司合作
44 | Socket内核数据结构:如何成立特大项目合作部?
45 | 发送网络包(上):如何表达我们想让合作伙伴做什么?
46 | 发送网络包(下):如何表达我们想让合作伙伴做什么?
47 | 接收网络包(上):如何搞明白合作伙伴让我们做什么?
48 | 接收网络包(下):如何搞明白合作伙伴让我们做什么?
核心原理篇:第九部分 虚拟化 (7讲)
49 | 虚拟机:如何成立子公司,让公司变集团?
50 | 计算虚拟化之CPU(上):如何复用集团的人力资源?
51 | 计算虚拟化之CPU(下):如何复用集团的人力资源?
52 | 计算虚拟化之内存:如何建立独立的办公室?
53 | 存储虚拟化(上):如何建立自己保管的单独档案库?
54 | 存储虚拟化(下):如何建立自己保管的单独档案库?
55 | 网络虚拟化:如何成立独立的合作部?
核心原理篇:第十部分 容器化 (4讲)
56 | 容器:大公司为保持创新,鼓励内部创业
57 | Namespace技术:内部创业公司应该独立运营
58 | CGroup技术:内部创业公司应该独立核算成本
59 | 数据中心操作系统:上市敲钟
实战串讲篇 (9讲)
60 | 搭建操作系统实验环境(上):授人以鱼不如授人以渔
61 | 搭建操作系统实验环境(下):授人以鱼不如授人以渔
62 | 知识串讲:用一个创业故事串起操作系统原理(一)
63 | 知识串讲:用一个创业故事串起操作系统原理(二)
64 | 知识串讲:用一个创业故事串起操作系统原理(三)
65 | 知识串讲:用一个创业故事串起操作系统原理(四)
66 | 知识串讲:用一个创业故事串起操作系统原理(五)
67 | 期末测试:这些操作系统问题,你真的掌握了吗?
结束语 | 永远别轻视任何技术,也永远别轻视自己
免费
专栏加餐 (2讲)
学习攻略(一):学好操作系统,需要掌握哪些前置知识?
“趣谈Linux操作系统”食用指南
免费
趣谈Linux操作系统
登录|注册

18 | 进程的创建:如何发起一个新项目?

刘超 2019-05-08
前面我们学习了如何使用 fork 创建进程,也学习了进程管理和调度的相关数据结构。这一节,我们就来看一看,创建进程这个动作在内核里都做了什么事情。
fork 是一个系统调用,根据咱们讲过的系统调用的流程,流程的最后会在 sys_call_table 中找到相应的系统调用 sys_fork。
sys_fork 是如何定义的呢?根据 SYSCALL_DEFINE0 这个宏的定义,下面这段代码就定义了 sys_fork。
SYSCALL_DEFINE0(fork)
{
......
return _do_fork(SIGCHLD, 0, 0, NULL, NULL, 0);
}
sys_fork 会调用 _do_fork。
long _do_fork(unsigned long clone_flags,
unsigned long stack_start,
unsigned long stack_size,
int __user *parent_tidptr,
int __user *child_tidptr,
unsigned long tls)
{
struct task_struct *p;
int trace = 0;
long nr;
......
p = copy_process(clone_flags, stack_start, stack_size,
child_tidptr, NULL, trace, tls, NUMA_NO_NODE);
......
if (!IS_ERR(p)) {
struct pid *pid;
pid = get_task_pid(p, PIDTYPE_PID);
nr = pid_vnr(pid);
if (clone_flags & CLONE_PARENT_SETTID)
put_user(nr, parent_tidptr);
......
wake_up_new_task(p);
......
put_pid(pid);
}
......

fork 的第一件大事:复制结构

_do_fork 里面做的第一件大事就是 copy_process,咱们前面讲过这个思想。如果所有数据结构都从头创建一份太麻烦了,还不如使用惯用“伎俩”,Ctrl C + Ctrl V。
这里我们再把 task_struct 的结构图拿出来,对比着看如何一个个复制。
static __latent_entropy struct task_struct *copy_process(
unsigned long clone_flags,
unsigned long stack_start,
unsigned long stack_size,
int __user *child_tidptr,
struct pid *pid,
int trace,
unsigned long tls,
int node)
{
int retval;
struct task_struct *p;
......
p = dup_task_struct(current, node);
dup_task_struct 主要做了下面几件事情:
调用 alloc_task_struct_node 分配一个 task_struct 结构;
调用 alloc_thread_stack_node 来创建内核栈,这里面调用 __vmalloc_node_range 分配一个连续的 THREAD_SIZE 的内存空间,赋值给 task_struct 的 void *stack 成员变量;
调用 arch_dup_task_struct(struct task_struct *dst, struct task_struct *src),将 task_struct 进行复制,其实就是调用 memcpy;
调用 setup_thread_stack 设置 thread_info。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《趣谈Linux操作系统》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(18)

  • why
    - fork -> sys_call_table 转换为 sys_fork()->`_do_fork`
    - 创建进程做两件事: 复制初始化 task_struct; 唤醒新进程
    - 复制并初始化 task_struct, copy_process()
        - dup_task_struct: 分配 task_struct 结构体; 创建内核栈, 赋给`* stack`; 复制 task_struct, 设置 thread_info;
        - copy_creds: 分配 cred 结构体并复制, p->cred = p->real_cred = get_cred(new)
        - 初始化运行时统计量
        - sched_fork 调度相关结构体: 分配并初始化 sched_entity; state = TASK_NEW; 设置优先级和调度类; task_fork_fair()->update_curr 更新当前进程运行统计量, 将当前进程 vruntime 赋给子进程, 通过 sysctl_sched_child_runs_first 设置是否让子进程抢占, 若是则将其 sched_entity 放前头, 并调用 resched_curr 做被抢占标记.
        - 初始化文件和文件系统变量
            - copy_files: 复制进程打开的文件信息, 用 files_struct 维护;
            - copy_fs: 复制进程目录信息, 包括根目录/根文件系统; pwd 等, 用 fs_struct 维护
        - 初始化信号相关内容: 复制信号和处理函数
        - 复制内存空间: 分配并复制 mm_struct; 复制内存映射信息
        - 分配 pid
    - 唤醒新进程 wake_up_new_task()
        - state = TASK_RUNNING; activate 用调度类将当前子进程入队列
        - 其中 enqueue_entiry 中会调用 update_curr 更新运行统计量, 再加入队列
        - 调用 check_preempt_curr 看是否能抢占, 若 task_fork_fair 中已设置 sysctl_sched_child_runs_first, 直接返回, 否则进一步比较并调用 resched_curr 做抢占标记
        - 若父进程被标记会被抢占, 则系统调用 fork 返回过程会调度子进程
        
    2019-05-10
    13
  • 刘強
    有个问题:
    在数据库中,有个事务的概念,也就是保证一连串操作的原子性,如果其中任何一步错误,整个操作回滚,回到原来的状态,好像什么也没发生。但是在文章中我看到,在创建进程的过程中,步骤太多了。每一步都要申请空间,复制数据。如果其中一步发生了错误,怎么保证释放这些空间,回到原来状态?

    作者回复: 错了会做错误处理的,没有啥捷径,都是代码里面自己做的,写c就要这样,每一步都要清楚自己创建了什么,万一错误应该销毁什么。如果程序员不做这个,没有人帮忙,不像java还有个gc

    2019-05-08
    10
  • 刘強
    文章中出现了SYSCALL_DEFINE0宏定义,不明白,就网上查了一下,一看吓一跳,宏定义里面又有一堆宏定义,其实就是一个函数调用,为什么弄得这么复杂呢?原来是为了修复一个bug。这让我意识到linux内核代码的复杂性。linux是一个集大成者,为了适应各种硬件架构平台,修复各种意想不到的bug,里面充斥着各种兼容性代码,修复补丁等等。而且里面的代码也是世界各路大神,黑客写出来的,为了保证内核的安全性,健壮性,扩展性,考虑的东西非常之多,充斥着各种奇技淫巧,不是我等普通人短时间能够理解。每一行代码,甚至一个宏定义,都是要花时间研究的。从这个角度上来说,linux就像是一个迷宫,如果没有一个向导,进去后估计就出不来了。也许这个专栏的作用就是充当一个向导,欣赏沿途风景的同时,带领我们穿越迷宫,找到出口...

    作者回复: 是的。

    2019-05-08
    5
  • Milittle
    老师,要是能把对应代码路径给出就好了,有时候自己找不见,谢谢老师~

    作者回复: 其实不用纠结,因为代码过一阵就变了,关键是理解原理和流程。我原来做过代码逐行分析的这种,但是发现这种文章过一阵就没法看了。

    2019-05-10
    4
  • garlic
    先前一些版本中人们讨论child first run主要为了减少COW对子进程造成影响,CFS调度器在2.6.23 版本引入后 2.6.32 将child first run关闭默认,父进程运行,理由是尤其引发的一些bash bug 和 更好的利用TLB和cache, 学习笔记https://garlicspace.com/2019/09/07/linux内核参数sysctl_sched_child_runs_first/
    2019-09-07
    2
  • 免费的人
    我是来收图的。
    2019-05-09
    2
  • 青石
    如果是完全公平调度算法的话,sched_fork的时候,将子进程的vruntime修改为与父进程的vruntime一致,是为了将子进程的vruntime设置到与其他进程在同一个量级上,父进程的执行说明当前它处在红黑树的最左节点,将父进程的TIF_NEED_RESCHED标记为允许被抢占,当系统回调时调用__schedule(),更新父进程的vruntime后,子进程处在红黑树最左节点,此时运行子进程。
    2019-05-09
    1
  • 一苇渡江
    老师写的太棒了,特别是这个图,肯定是花了不少时间,把这个图手抄了一遍,时不时拿出来看看

    作者回复: 手抄啊,牛

    2019-05-08
    1
  • 阿西吧
    老实说Ctrl C + Ctrl V是一种非常不好的编程习惯
    2019-11-26
  • 陈志恒
    进程创建的过程其实就是执行系统调用fork的过程。它包含两个重要的事件,一个是将 task_struct 结构复制一份并且初始化,另一个是试图唤醒新创建的子进程。
    2019-11-25
  • 懒懒懒!
    老师好,请问下linux加入写时拷贝技术后,对fork这块的具体影响是怎么样的呢
    2019-09-25
  • -W.LI-
    老师好!虽然看不懂这些函数调用,but还在坚持,老师的图画的很好。思虑很清晰,向老师学习
    2019-08-23
  • 小美
    内核态的内核进程和用户态的用户进程创建过程有区别吗?

    作者回复: 有区别的

    2019-05-24
  • 周平
    讲得好的细节,与前面的内容可以无缝连接,不至于管中窥豹,让学习者越学越乱,谢谢老师

    作者回复: 赞

    2019-05-17
  • 尚墨
    反复研读都已经高亮了。我几乎每篇都要听,读三次以上,才能懵懵懂懂。

    作者回复: 再难的知识就怕反复研究,加油

    2019-05-11
  • chengzise
    解析的逻辑特别清晰,后面需要自己结合代码,加深理解。老师说的太好了
    2019-05-08
  • 安排
    调度类是全局的吗?还是每个cpu核有自己的调度类集合?

    作者回复: 类是全局的。里面主要实现的是算法。

    2019-05-08
    1
  • blentle
    子进程是如何抢占父进程的呢?

    作者回复: 一旦创建了,就独立竞争了

    2019-05-08
收起评论
18
返回
顶部