复制粘贴老师课件,边看边复制;后面有更新
“A process is just an instance of an executing program , including the current values of the program counter, registers, and variables. Conceptually, each process has its own virtual CPU.”
—-《Modern operating system 》
Similarities:
Differences:
主要把握一点,进程是资源分配的最小单位,线程是CPU调度的最小单位。
Linux has a unique implementation of threads.
进程四要素:
1. 有一段程序供其执行,就好像一场戏要有剧本一样。这段程序不一定是进程所专有,可以与其他进程公用,就好像不同剧团的许多演出可以公用一个剧本一样
2. 有起码的“私有财产”,这就是进程专用的系统堆栈空间
3. 有“户口”,这就是在内核中的一个task_struct数据结构,操作系统教科书中常称它为“进程控制块”。有了这个数据结构,进程才能成为内核调度的一个基本单位接收内核的调度。同时,这个结构又是进程的“财产登记表”,记录着进程做占用的各项资源
4. 有独立的存储空间,意味着拥有专有的用户空间;进一步,还意味着除前述的系统空间堆栈外还有专用的用户空间堆栈。注意,系统空间是不能独立的,任何进程都不可能直接(不通过系统调用)改变系统空间的内容(除其本身的系统空间堆栈之外)
如果缺少第四条,则称为线程——完全没有用户空间称为内核线程;共享用户空间称为“用户线程”
线程
multithreaded process: a process is composed of several threads, each of which represents an execution flow of the process
从内核角度看,Linux内核中没有线程的概念
用户线程:通过POSIX兼容的Pthreads线程库实现
内核线程:由内核创建和撤销的
POSIX表示可移植操作系统接口(Portable Operating System Interface of UNIX,缩写为 POSIX ),POSIX标准定义了操作系统应该为应用程序提供的接口标准,是IEEE为要在各种UNIX操作系统上运行的软件而定义的一系列API标准的总称,其正式称呼为IEEE 1003,而国际标准名称为ISO/IEC 9945。
内核线程
系统把一些重要的任务委托给周期性执行的进程
内核线程与普通进程的差别
Introduction
PCB: Process Control Block
contain all the information related to a single process
-opened files
-the process’s address space
-pending signals
-the process’s state
-……
task_struct (task_t)
-defined in < include/linux/sched.h >
-about 1.7KB on a 32-bit machine
分配进程描述符
当创建一个新的进程时,内核为其分配task_struct结构
在2.6以前的内核中,每个进程的task_struct存放在它们内核栈的尾端:
内核栈: 每个进程切换到内核态后使用 , 8K
Identifying a Process
enum pid_type {
PIDTYPE_PID
PIDTYPE_PGID
PIDTYPE_SID
PIDTYPE_MAX };
进程组:其所有进程task_struct的pgrp属性值都是组长的 PID,可简化向成员 发送信号的操作。通过管道连接的进程属于同一个进程组;也可使用setpgrp系 统调用设置
会话:所有进程的SID一致,存储在task_struct的session中。SID可以使用 setsid系统调用设置
PID: process ID
局部和全局
全局ID:在内核本身和初始命名空间中的唯一ID号,在系统启动期间开始的 init进程即属于初始命名空间。对每个ID类型,都有一个给定的全局ID,保证 在整个系统中是唯一的。全局PID和TGID直接保存在task_struct中,分别是 task_struct的pid和tgid成员
局部ID属于某个特定的命名空间,不具备全局有效性。对每个ID类型,它们 在所属的命名空间内部有效,但类型相同、值也相同的ID可能出现在不同的 命名空间中
Possible process states (mutually exclusive ):
The above two states can be stored both in the state field and in the exit_state field of the process descriptor -> a process reaches one of them only when its execution is terminated.
All processes are descendents of the init process (PID=1)
Statically allocated as init_task
Struct task_struct *task;
For (task=current; task!=init_task; task=taskparent);
Kernel starts init process in the last step of the boot process
Every process has
不禁让我联想到Java的对象都继承自Object
Traditional Unix systems:
- fork(), creates a child process that is a copy of the current task
- resources owned by the parent process are duplicated in the child process (difference: PID, parent PID, certain resources and statistics, such as pending signals) ->process creation very slow and inefficient
- in many cases, child process issues an immediate execve( ) to load a new executable into the address space and begins executing it (so wipe out the address space)
Modern Unix kernels solve the problem (“process creation very slow and inefficient”) by introducing three different mechanisms:
- Copy On Write technique allows both the parent and the child to read the same physical pages. Whenever either one tries to write on a physical page, the kernel copies its contents into a new physical page that is assigned to the writing process
- Lightweight processes allow both the parent and the child to share many per-process kernel data structures
- vfork( ) system call creates a process that shares the memory address space of its parent
联想到Git上fork,原来是这个意思
Copy On Write (COW)
fork(), vfork(), clone()
线程的创建
与普通的进程创建类似,调用clone()
CLONE_THREAD
线程组:
为了控制进程的执行,内核必须有能力挂 起正在CPU上执行的进程,并恢复以前挂 起的某个进程的执行,这叫做进程切换
进程上下文
包含进程执行所需的信息 :
硬件上下文
每个进程可以有自己的地址空间,但所有的进程只能共享 CPU的寄存器。
在恢复一个进程执行之前,内核必须确保每个寄存器装入 了挂起进程时的值
硬件上下文:
进程恢复执行前必须装入寄存器的一组数据
包括通用寄存器的值以及一些系统寄存器 :
通用寄存器如eax,ebx等;系统寄存器如eip,esp,cr3等等
在linux中,
一个进程的硬件上下文(体系结构相关)主要保存在thread_struct中 ,
其他信息放在内核态堆栈中
In Linux 2.6 there are two system calls that terminate a User Mode application:
exit_group( ) :
exit( ):
进程的析构发生在调用exit()之后
可能显式调用,也可能隐式地在主函数的 返回处。
当进程接受到它既不能处理也不能忽略的信号或异常时,可能被动地终结
在调用了do_exit()之后,尽管线程已经僵死 不能再运行了,但是系统还保留了它的进 程描述符。
系统可以在子进程终结后仍能获得它的信息
进程终结所需的清理工作与进程描述符的删除工作分开。
删除进程
在父进程调用wait()类系统调用检查子进程是否合法终止以后,就可以删除这个进程
父进程终止
孤儿进程
父进程在子进程之前退出
forget_original_parent()-> find_new_reaper()寻 找一个进程作为它的父亲
!!!!!!!!!!!总结
进程的一生
2018/4/1更新
Process Descriptor
PCB以task_struct数据结构实现
对于每一个进程,linux都会保存两个不同的数据在对应的空间,一个便是thread_info 该结构是指向 task_struct的一个指针,另一个是当进程切换到内核态时候的栈空间。如图: