当前课程知识点:Linux 内核分析与应用 > 第3章 进程管理 > 3.2 Linux进程创建 > Video
大家好 我们今天来讲进程的创建
目前在用户程序的开发中呢
不仅仅涉及到进程还涉及到线程和协程
那么它们到底是如何创建的呢
为什么创建了一个进程或者线程后呢
觉得自己对其没有控制权
这是因为创建这件事情完全由操作系统来控制
你只是发出了一个创建的请求
然后呢整个生孩子这件事情就交给了操作系统
如果你对这个过程不了解 那么一旦程序在运行过程中出现问题
你就可能束手无策了
但是这个过程实际上非常的复杂
那么如何入手呢我们来给予简单介绍
我们知道进程呢是系统资源分配的基本单位
线程是独立运行的基本单位
但是这种说法过于笼统
进程的资源到底有哪些 如何体现
线程为什么是轻量级的运行单位 如何体现
从图中可以看出 进程和线程几乎共享所有的资源
包括代码 数据
进程的空间 打开的文件等等
线程只拥有自己的寄存器和栈
这些概念上的代码段 数据段
进程空间 打开的文件
在内核中是如何来表现的呢
那么我们说task_struct结构具有统一性和多样性
内核如何对待
进程 线程和内核线程
Linux内核坚持平等的原则
对它们一视同仁
也就是内核使用唯一的数据结构task_struct结构呢
来分别表示它们
也使用相同的调度算法对这三者进行调度
尽管表面看起来它们很不一样
但是在内核中呢
最终都通过do_fork分别创建
这样的处理对内核来说简单方便
在同一的基础上又保持各自的特性
这是何如做到的呢
我们站在用户态函数库看过去
创建进程和创建线程呢
调用了不同的函数
分别为fork和pthread_create
而对应的系统调用分别为fork和clone
那么vfork和fork呢
比较类似后面会讲到它们二者之间的差别
所有的系统调用进入内核只有一个入口
进去以后就分道扬镳了各自有自己的服务历程
但分手只是暂时的 归到一处是最终的选择
因此do_fork就成为它们的聚合点
do_fork在内核中是怎样的原型呢
那么我们看这个函数的原型 看起来有一堆的参数
是否有眩晕的感觉呢
在用户态调用fork的时候呢
我们轻松自由 一个参数也不需要传递给它
为什么进入内核后就这么麻烦 在这里呢我们就知道
你的风花雪月到底是谁来帮你担当的
我们先看fork调用do_fork
除了SIGCHLD参数外呢
有三个参数呢是空手而来
有两个参数似乎也没有明确的目标
但作为子进程它完全由自己的个性
根本不想共享父进程的任何资源
而是让父进程把它所有的资源给自己复制一份
父进程就真的给他复制一份吗
老爸没有那么傻 而是假装复制了一下
也就是用一个指针指过去而已
等真正需要的时候呢比如
要写一个页面这时候写时复制技术就登场了
只要父子进程中
不管谁想写一个页面的时候呢这个页面才被复制一份
vfork比fork还狡猾直接传递两个标志过去 第一个标志
它表示什么意思表示儿子优先 老爸等待
于是父进程就去睡觉了 等子进程结束以后才能醒来
第二个标志 儿子干脆与父进程待在一个进程的地址空间中
对 就是父子进程共享内存的地址空间
但是呢父进程的页表除外
vfork看起来很聪明 但是实际上聪明反被聪明误
因为写时复制技术的招数更高
也就是更高效 因此呢
它就没有了生存空间 直接被取代了
Clone它的意思就是克隆 对就是克隆技术
线程就是这么诞生的 怎么clone呢 听起来很神秘
实际上很简单 无非就是传递了一堆参数告诉老爸
你这我要共享你 你那我也要共享
于是老爸就把地址空间 文件系统 打开的文件
信号处理函数等等呢
都被儿子的一句话说过去了
看起来实际上就是四个参数的或
如果我是一个内核线程 我的出生是不是更有优势呢 没错
因为用户空间对我来说根本没有意义
我根本就不知道它的存在
那该调用哪个函数创建呢
早期内核中创建内核线程是通过
kernel_thread而创建的 目前内核中调用
kthread_create创建的
其本质也是向do_fork提供
特定的标志而创建
下面我们来看一下task_struct结构带来的统一性
到底谁给我们thread的诞生呢
带来了方便 说到底还是因为我们站在同一个战壕中
对 也就是说task_strcut结构
由此我们的生命历程就有了诸多的相似
不管是被调度到CPU上去跑
还是分配各种资源到最终的诞生呢
都是调用了相同的函数do_fork
能不能看看do_fork代码流程是什么样子呢
下面呢就是do_fork代码流
程
首先调用copy_process复制父进程的进程控制块
然后获得子进程的PID
如果设置了暂停的标志呢 则子进程的状态也被置为暂停
否则呢通过唤醒函数
将子进程的状态设置为就绪
并且将子进程加入到就绪队列
如果使用了vfork创建进程则阻塞父进程
下面我们来看一下copy_process做了什么事情
那么copy_process主要用于创建进程控制块
以及子进程执行时所需要的其他的数据结构
该函数的参数与do_fork的参数大致相同
并添加了子进程的PID
copy_process所做的处理必须考虑到各种可能的情况
这些特殊的情况呢 也就是通过clone_flags来具体体现
下面呢我们忽略特殊的情况给出一般的执行过程
这张图是copy_process的代码流程图
我们为什么没有给出具体的代码呢
因为内核版本不同呢
它的代码稍有差异 所以我们这里给出的是流程图
那么copy_process这个函数呢它主要是为了
子进程创建父进程PCB的副本
然后呢对子进程PCB中的各个字段进行初始化
同时呢子进程对父进程的各种资源呢
进行复制或者共享
具体取决于clone_flags所设置的标志
每一种资源的复制
或者共享呢
都通过copy_xyz这样的函数完成的
当然子进程通过这样一个函数呢就获得了自己的PID
父进程通过copy_xyz这样的
函数共享各种资源
比如打开的文件
所在的文件系统 进程的地址空间 信号
命名空间等等
如果进入这些函数去阅读
几乎延伸到内核的各个子系统
因此当你还对这些内容
没有了解的时候 先简单的阅读
当你了解了各个子系统以后呢
再回头阅读相关的内容会发现
一个进程的创建牵一发而动全局
把相关的知识呢都可以串起来了
那些零零散散的知识通过do_fork聚集在一起
知识的活力呢也充分的体现出来了
这里为什么不给具体的源代码呢 因为内核版本不同
代码一直在变化 但是
当你对各个对象之间的关系理清楚以后去阅读的话
这些源码就是一种实现了
下面我们看一下进程的生命周期
前面我们详细的介绍了fork的创建过程
与此相应的还有三个系统调用分别为
exec wait exit三个系统调用
在这里我们对三个系统调用的过程就不详细介绍了
它们之间是如何配合的呢
下面让我们用一些形象的比喻来对进程短暂的一生
做一个小小的总结
随着一句fork呢一个新进程呱呱落地
但这时只是老进程的一个克隆
然后呢随着exec呢
新进程呢脱胎换骨
离家独立开始了独立工作的职业生涯
人有生老病死进程也一样
它可以是自然的死亡
即运行到主函数的最后一个大括弧
从容的离我们而去
也可以是中途退场
退场有两种方式
一种是调用exit函数
一种是在主函数内使用return函数
无论哪一种方式呢它都可以留下留言
放在返回值里保留下来
甚至它还可能被谋杀 被其他进程
通过另外一种方式呢结束它的生命
进程死掉以后呢会留下一个空壳
wait站好最后一班岗 打扫战场
使其最终归于无形 这就是进程完整的一生
-1.1 Linux操作系统概述
-1.2 Linux内核结构以及内核模块编程
--Video
-1.3 Linux内核源码中的双链表结构
--Video
-1.4 源码分析-内核中的哈希表
--Video
-1.5 动手实践-Linux内核模块的插入和删除
--Video
-第1章 概述--章节测验
-2.1 内存管理之内存寻址
--Video
-2.2 段机制
--Video
-2.3分页机制
--Video
-2.4 动手实践-把虚拟地址转换成物理地址
--Video
-第2章 内存寻址--章节测验
-3.1 进程概述
--Video
-3.2 Linux进程创建
--Video
-3.3 Linux进程调度
--Video
-3.4 动手实践-打印进程描述符task_struct中的字段
--Video
-3.5工程实践-基于内核模块的负载监控
--Video
-第3章 进程管理--章节测验
-4.1 Linux内存管理机制
--Video
-4.2 进程用户空间管理机制
--Video
-4.3 物理内存分配与回收机制(上)
--Video
-4.4 物理内存分配与回收机制(下)
--Video
-4.5 动手实践-Linux内存映射基础(上)
--Video
-4.6 动手实践-Linux内存映射实现(中)
--Video
-4.7 动手实践-Linux内存映射测试(下)
--Video
-4.8 初学者对内存管理的常见疑惑
-第4章 内存管理--章节测验
-5.1 中断机制概述
--Video
-5.2 中断处理机制
--Video
-5.3 中断下半部处理机制
--Video
-5.4 时钟中断机制
--Video
-5.5 动手实践-中断上半部的代码分析及应用
--Video
-5.6 动手实践-中断下半部的代码分析及应用
--Video
-第5章 中断--章节测验
-6.1 Linux中的各种API
--Video
-6.2 系统调用机制
--Video
-6.3 动手实践-添加系统调用(系统调用日志收集系统)
--Video
-第6章 系统调用--章节测验
-7.1 内核同步概述
--Video
-7.2 内核同步机制
--Video
-7.3 动手实践-内核多任务并发实例(上)
--Video
-7.4 动手实践-内核多任务并发实例(下)
--Video
-第7章 内核同步--章节测验
-8.1 虚拟文件系统的引入
--Video
-8.2 虚拟文件系统的主要数据结构
--Video
-8.3 文件系统中的各种缓存
--Video
-8.4 页高速缓存机制以及读写
--Video
-8.5 动手实践-编写一个文件系统(上)
--Video
-8.6 动手实践-编写一个文件系统(中)
--Video
-8.7 动手实践-编写一个文件系统(下)
--Video
-第8章 文件系统--章节测验
-9.1 设备驱动概述
--Video
-9.2 I/O空间管理
--Video
-9.3 设备驱动模型
--Video
-9.4 字符设备驱动程序简介
--Video
-9.5 块设备驱动程序简介
--Video
-9.6 动手实践-编写字符设备驱动程序
--Video
-9.7工程实践-编写块设备驱动的基础(上)
--Video
-9.8 工程实践-块设备驱动程序分析(中)
--Video
-9.9 工程实践-块设备驱动程序实现(下)
--Video
-第9章 设备驱动--章节测验
-致谢与说明
--Video