linux及安全《Linux内核设计与实现》第三章——20135227黄晓妍
第三章
(由于linux不区分进程和线程,所以它们在linux中被称为task,也叫任务)
总结:本章主要包括进程以及线程的概念和定义,Linux内核如何管理每个进程,他们在内核中如何被列举,如何创建,最终如何消亡。操作系统存在的意义在于运行用户程序,进程管理是所有操作系统的心脏所在。
3.1进程
进程是处于执行期的程序,是正在执行的程序代码的实时结果。但不仅局限于一段可执行的代码,还包括其他资源(打开的文件,挂起的信号,内核内部数据,处理器的状态,一个或者多个内存映射的内存地址空间,一个或者多个执行线程,存放全局变量的数据段)
线程
执行线程,即为线程,进程中活动的对象,是内核调度的对象。(内核并不是调度进程哟~)每一个线程都拥有一个独立的计数器,进程栈和一组进程寄存器。
进程提供的两种虚拟机制:
虚拟处理器:给进程一种自己在独享处理器的假象
虚拟内存:让进程在分配和管理内存空间的时候觉得自己拥有整个系统的所有内存资源
程序
进程处理执行期的程序以及相关资源的总称。
进程的整个周期
从它被创建的时刻开始存活(一般是fork()系统调用创建),接着调用exec()创建新的地址空间,并把新的程序载入,最终程序通过exit()系统调用退出执行。进程退出执行后被设置为僵死状态,直到它的父进程调用wait()或者waitpid()为止。
3.2进程描述符及任务结构
进程描述符的结构:
task_struct,定义在<linux/sched.h>文件中。任务队列的双向循环链表(内核存放进程列表的地方),每一项都是task_sturct.它包含一个具体的进程的所有信息(打开的文件,进程的地址空间,挂起的信号,进程的状态等)
3.2.1分配进程描述符
Linux通过slab分配器分配task_struct结构。只需要在栈底(对于向下增长的栈)或者栈顶(向上增长的栈)创建新的struct thread_info来动态生成task_struct结构。
每个任务的thread_info在它的内核栈的尾端分配。结构中的task域存放指向该任务的实际task_struct指针。
3.2.2进程描述符的存放
内核通过唯一的pid进程标识符(其类型是pid_t,但实际上是int)来标识每一个进程。内核把pid放在她们各自的进程描述符中。它的默认值为32768(short int的 max),这个值表示系统中允许同时存在的进程的最大数目。(可以通过/proc/sys/kernel/pid_max来提高上限)
访问任务需要获得task_struct指针,通过current宏查找进程描述符的速度很重要。X86内核栈的尾端创建thread_info结构,通过偏移间接查找。栈大小(8KB)需要屏蔽掉后面13位(8192),4kb屏蔽12位(4096)
汇编代码:
图
返回:
图
X8的寄存器太少了才这样做,实际上访问进程描述符是一个很频繁的操作,所以寄存器多一些的操作系统,都会用一个专门的寄存器来保存task_struct。
3.2.3进程状态
进程描述符state描述进程当前的状态:
TASK_RUNNING运行
TASK_INTERRUPTIBLE可中断(阻塞)
TASK_UNINTERRUPTIBLE不可中断
_TASK_TRACED被其他进程跟踪
_TASK_STOPPPED停止
图3
3.2.4设置当前进程状态
使用set_current_stste(state)或者set_task_state(current,state)函数。
图4
等价于
图5
3.2.5进程上下文
可执行代码从一个可执行文件载入到进程地址空间执行。当它陷入内核时,内核“代表进程执行”并处于在进程上下文中,在此上下文中,current宏是有效的。
3.2.6进程家族树
进程之间有明显的继承关系。Linux系统中所有进程都是pid=1的进程的后代。(pid=1的进程是操作系统启动的最后阶段启动的init进程)
进程间的继承关系存放在进程描述符中。(task_struct结构)
获得父进程的进程描述符
图6
获得子进程的进程描述符
图7
因为有这样的继承关系,我们从任意一个进程去访问任务列表中其他所有进程。欣慰任务队列本身又是一个双向链表,所以其实是很容易实现的。
获取下一个进程:
图8
获取上一个进程
图9
遍历整个进程队列(没有必要不要遍历,因为代价很大)
图10
3.3进程创建
分成三步:1.在新的地址空间里创建进程2.读入可执行文件3.开始执行
3.3.1写时拷贝
写时拷贝是一种让子父进程共享同一个拷贝的技术。
相当于资源只有在需要写入的时候才会被复制,在此之前都是以只读的方式共享。
3.3.2fork()
do_fork()完成了创建进程的大部分工作,其中copy_process完成的工作:
1) 调用dup_task_struct()为新进程创建一个内核栈、thread_info、task_sturct这些值与当前进程同步
2) 检查用户所拥有的进程数目是否超出限制
3) 子进程开始把自己与父进程区分
4) 将子进程状态设置为不可中断
5) 调用copy_flags更新flag成员
6) 调用alloc_pid()为新进程分配一个有效的pid
7) 传递参数标识
8) 返回指向子进程的指针
3.3.3vfork()
除了不拷贝页表以外,和fork是一样的。
图11
3.4线程在linux中实现
线程被linux抽象成耗费比较少资源,运行迅速执行的单元。
3.4.1创建线程
和创建进程类似,但是在调用clone()需要传递一些标志性参数
图12
参数的含义:
图13
3.4.2内核线程
内核的后台任务一般通过内核线程执行,内核线程没有独立的地址空间只能运行在内核,内核线程启动以后就一直运行知道调用do_ex()退出,或者内核其他部分调用thread_stop()退出。
3.5进程终结
终结时要释放资源并告知父进程。大部分任务靠do_exit()来完成。
图14
3.5.1删除进程描述符
调用do_exit()以后,线程僵死但是保留了它的进程描述符。父进程获得已终结的子进程信息,或者它通知内核它不关注那些信息以后,子进程的进程描述符才被释放。
Wait()是挂起调用它的进程,直到其中的一个子进程退出,此时函数会返回该子进程的pid。
Release_task是最终执行释放进程描述符放入函数:
图15
3.5.2孤儿进程造成的进退维谷
如果父进程在子进程之前退出,需为子进程找到一个新的父亲。如果找不到,就让init作为它们的父进程
3.6小结
总结:
本章主要包括进程以及线程的概念和定义,Linux内核如何管理每个进程,他们在内核中如何被列举,如何创建,最终如何消亡。操作系统存在的意义在于运行用户程序,进程管理是所有操作系统的心脏所在。
linux及安全《Linux内核设计与实现》第三章——20135227黄晓妍的更多相关文章
- linux及安全《Linux内核设计与实现》第一章——20135227黄晓妍
<linux内核设计与实现>第一章 第一章Linux内核简介: 1.3操作系统和内核简介 操作系统:系统包含了操作系统和所有运行在它之上的应用程序.操作系统是指整个在系统中负责完成最基本功 ...
- linux及安全《Linux内核设计与实现》第二章——20135227黄晓妍
第二章:从内核出发 2.1获取源代码 2.1.1使用git Git:内核开发者们用来管理Linux内核源代码的控制系统. 我们使用git来下载和管理Linux源代码. 2.1.2安装内核源代码(如果使 ...
- linux及安全《Linux内核设计与实现》第四章——20135227黄晓妍
第四章 进程调度 进程调度程序是一个内核子系统 分配有限的处理器时间和资源 最大限度利用时间的原则(只要有可执行的进程,那么总会有进程执行) 基本工作:从一组处于等待(阻塞)状态的可执行进程中选择一个 ...
- linux及安全期中总结——20135227黄晓妍
Linux及安全期中总结 黄晓妍 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ...
- linux及安全第三周总结——20135227黄晓妍
总结部分: Linux内核源代码: Arch 支持不同cpu的源代码:主要关注x86 Init 内核启动的相关代码:主要关注main.c,整个Linux内核启动代码start_kernel函数 K ...
- Linux内核设计与实现 第三章
1. 进程和线程 进程和线程是程序运行时状态,是动态变化的,进程和线程的管理操作都是由内核来实现的. Linux中的进程于Windows相比是很轻量级的,而且不严格区分进程和线程,线程不过是一种特殊的 ...
- Linux基础入门学习笔记20135227黄晓妍
学习计时:共24小时 读书:1小时 代码:8小时 作业:3小时 博客:12小时 一.学习目标 1. 能够独立安装Linux操作系统 2. 能够熟练使用Linux系统的基本命令 3. 熟练使用L ...
- linux及安全第八周总结——20135227黄晓妍
实验部分 实验环境搭建 -rm menu -rf git clone https://github.com/megnning/menu.git cd menu make rootfs qemu -ke ...
- linux及安全第七周总结——20135227黄晓妍
实验部分 首先clone最新的menu 我们可以看到,test.c里多了一个exec的功能,它的代码和fork基本一致,多了一项加载hello rootfs也有一些变化 执行一下exec 让我们启动一 ...
随机推荐
- 【PHP+Redis】 php-redis 操作类 封装
<?php /** * redis操作类 * 说明,任何为false的串,存在redis中都是空串. * 只有在key不存在时,才会返回false. * 这点可用于防止缓存穿透 * */ cla ...
- linux 下 git gem 等代理设置问题
github.com,作为程序员的代码仓库,我们经常会用到.但有时候我们不能直接通过网络链接它,只能通过代理. 这里我有一台代理服务器,起初我以为在终端设置了代理环境就行了,其设置为在你的~/.bas ...
- 谈谈KV存储集群的设计要点
版权声明:本文由廖念波原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/150 来源:腾云阁 https://www.qclo ...
- linux下php安装
nginx中配置php: http://www.111cn.net/sys/nginx/64044.htm
- Sql Server 关于列名带中括号"[]"的问题
1.如果列名为数据库的关键字则自动加上中括号“[]” 例如[level] 2.如果列名中带有特殊符号.[date(a)] 数据存储的过程: 1.在添加数据的时候:要带有中括号,有必要在添加参数的时候不 ...
- spring 配置中相关属性的含义:
1:parent元素属性 一个bean定义可能会包含大量的配置信息,包括容器相关的信息(比如初始化方法,静态工厂方法等等)以及构造函数参数和属性的值.一个child bean定义是一个能够从paren ...
- 160227、javascript特效
1.给网页设定快捷键 js: function getkey(){ event = event || window.event; url = "www.baidu.com&q ...
- 编译安装基于nginx与lua的高性能web平台-openresty
1.首先编译安装nginx(不多说) 2.开始安装openresty cd /usr/local/src wget https://openresty.org/download/openresty-1 ...
- java 新手入门课程03
2017.7.6 java 课堂笔记 1.关于分支; if/else 是基于boolean 值的双分支 Switch 基于数字(包括整数 char byte 枚举, 字符串)类型的多分支 方法 ...
- 在idea中为函数自动生成注释(解决注释无法出现形参的情况)
1 点击“File”-->“Settings”-->“Live Templates”打开如下对话框,点击右边绿色的加号,创建一个自定义的Template Group,如“Java” 2.选 ...