Linux课堂笔记 | 进程1——进程管理

复制粘贴老师课件,边看边复制;后面有更新

Introduction of Process

What is process?

“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 》

Process and Thread

Similarities:

  • Like processes threads share CPU and only one thread active (running) at a time.
  • like process, if one thread is blocked, another thread can run

Differences:

  • Unlike processes, threads are not independent of one another.
  • Unlike processes, all threads can access every address in the task .
  • Unlike processes, thread are design to assist one other. Note that processes might or might not assist one another because processes may originate from different users.

主要把握一点,进程是资源分配的最小单位,线程是CPU调度的最小单位。

Process in Linux

Linux has a unique implementation of threads.

  • The Linux kernel does not provide any special scheduling semantics or data structures to represent threads.
  • A thread is merely a process that shares certain resources with other processes.

进程四要素:
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内核中没有线程的概念

  • 没有针对所谓线程的调度策略
  • 没有数据结构用来表示一个线程
  • 一般线程的概念在linux中只是表现为一组共享资源的进程(每个这样的进程都有自己的进程描述符)

用户线程:通过POSIX兼容的Pthreads线程库实现
内核线程:由内核创建和撤销的

POSIX表示可移植操作系统接口(Portable Operating System Interface of UNIX,缩写为 POSIX ),POSIX标准定义了操作系统应该为应用程序提供的接口标准,是IEEE为要在各种UNIX操作系统上运行的软件而定义的一系列API标准的总称,其正式称呼为IEEE 1003,而国际标准名称为ISO/IEC 9945。

内核线程

系统把一些重要的任务委托给周期性执行的进程

  • 刷新磁盘高速缓存
  • 交换出不用的页框
  • 维护网络链接等待
  • ……

内核线程与普通进程的差别

  • 由内核创建和撤销
  • 只运行在核心态
  • 只使用大于PAGE_OFFSET的线性地址空间
  • 没有用户地址空间

Process Descriptor

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
Linux课堂笔记 | 进程1——进程管理_第1张图片

分配进程描述符
当创建一个新的进程时,内核为其分配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可能出现在不同的 命名空间中

task_struct
Linux课堂笔记 | 进程1——进程管理_第2张图片

Possible process states (mutually exclusive ):

  • TASK_RUNNING :The process is either executing on a CPU or waiting to be executed
  • TASK_INTERRUPTIBLE:The process is suspended (sleeping) until some condition becomes true or it receives a signal (back to TASK_RUNNING)
  • TASK_UNINTERRUPTIBLE : Like TASK_INTERRUPTIBLE, except that delivering a signal to the sleeping process leaves its state unchanged.
  • TASK_STOPPED : Process execution has been stopped
  • TASK_TRACED : Process execution has been stopped by a debugger.
  • EXIT_DEAD : The final state: the process is being removed by the system
  • EXIT_ZOMBIE : Process execution is terminated, but the kernel cannot discard the data contained in the dead process descriptorbecause the parent might want to access it(the parent process has not yet issued a wait4( ) or waitpid( ) system call)

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.

Linux课堂笔记 | 进程1——进程管理_第3张图片

Process Family Tree

  • 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=taskparent);

    Kernel starts init process in the last step of the boot process

  • Every process has

    • One parent: struct task_struct *parent
      • Real_parent: process created P or process 1 (init) if the parent process no longer exists
      • Parent: current parent of P (the process that must be signaled when the child process terminates)
    • Zero or more children: struct list_head children
  • Siblings: parent’s direct children
    • struct list_head sibling
  • The relationships stored in the process descriptor

不禁让我联想到Java的对象都继承自Object

Process Creation

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的实际开销就是复制父进程的页表以及给子 进程创建唯一的进程描述符
  • fork()后立即调用exec():免除拷贝

fork(), vfork(), clone()

  • fork()
    复制全部资源
    clone(SIGCHLD, 0)
  • vfork()
    复制除task_struct和系统堆栈之外的资源(产 生的是线程)
    clone(CLONE_VFORK | CLONE_VM | SIGCHLD, 0)

线程的创建

与普通的进程创建类似,调用clone()
CLONE_THREAD

线程组:

  1. 以标志CLONE_THREAD来调用clone
  2. 所有子进程都有统一的TGID
  3. 线程组中的主进程为group leader,所有通过 clone创建的线程的task_struct的group_leader 都指向主进程的task_struct

Process Switch

为了控制进程的执行,内核必须有能力挂 起正在CPU上执行的进程,并恢复以前挂 起的某个进程的执行,这叫做进程切换

进程上下文

包含进程执行所需的信息 :

  • 用户地址空间 : 包括程序代码,数据,用户堆栈等
  • 控制信息 : 进程描述符,内核堆栈等
  • 硬件上下文

硬件上下文
每个进程可以有自己的地址空间,但所有的进程只能共享 CPU的寄存器。
在恢复一个进程执行之前,内核必须确保每个寄存器装入 了挂起进程时的值

硬件上下文:

  • 进程恢复执行前必须装入寄存器的一组数据

  • 包括通用寄存器的值以及一些系统寄存器 :
    通用寄存器如eax,ebx等;系统寄存器如eip,esp,cr3等等

在linux中,
一个进程的硬件上下文(体系结构相关)主要保存在thread_struct中 ,
其他信息放在内核态堆栈中

Process Termination

In Linux 2.6 there are two system calls that terminate a User Mode application:
exit_group( ) :

  • terminates a full thread group, that is, a whole multithreaded application
  • Corresponding main kernel function is do_group_exit( ).
  • should be invoked by the exit() C library function

exit( ):

  • terminates a single process, regardless of any other process in the thread group
  • Corresponding main kernel function is do_exit( )
  • invoked, for instance, by the pthread_exit( ) function of the LinuxThreads library

进程的析构发生在调用exit()之后

可能显式调用,也可能隐式地在主函数的 返回处。

当进程接受到它既不能处理也不能忽略的信号或异常时,可能被动地终结

在调用了do_exit()之后,尽管线程已经僵死 不能再运行了,但是系统还保留了它的进 程描述符。

系统可以在子进程终结后仍能获得它的信息

进程终结所需的清理工作与进程描述符的删除工作分开。

删除进程
在父进程调用wait()类系统调用检查子进程是否合法终止以后,就可以删除这个进程
父进程终止

孤儿进程
父进程在子进程之前退出
forget_original_parent()-> find_new_reaper()寻 找一个进程作为它的父亲


!!!!!!!!!!!总结

进程的一生

  1. 随着一句fork,一个新进程呱呱落地,但它这时只是老进程的一个克隆
  2. 然后随着exec,新进程脱胎换骨,离家独立,开始了为人 民服务的职业生涯。
  3. 人有生老病死,进程也一样
    自然死亡,即运行到main函数的最后一个“}”,从容地离我们而去
    自杀(自杀有2种方): 调用exit函数;在main函数内使用return.无论哪一种方式,它都可以留下遗书,放在返回值里保留下来
    被谋杀,被其它进程通过另外一些方式结束他的生命
  4. 进程死掉以后,会留下一具僵尸,wait充当了殓尸工,把 僵尸推去火化,使其最终归于无形。

2018/4/1更新

Process Descriptor
PCB以task_struct数据结构实现
Linux课堂笔记 | 进程1——进程管理_第4张图片

对于每一个进程,linux都会保存两个不同的数据在对应的空间,一个便是thread_info 该结构是指向 task_struct的一个指针,另一个是当进程切换到内核态时候的栈空间。如图:
Linux课堂笔记 | 进程1——进程管理_第5张图片

process链表将所有存在的进程描述符进行链接
Linux课堂笔记 | 进程1——进程管理_第6张图片

父子关系,通过双向链表
Linux课堂笔记 | 进程1——进程管理_第7张图片

你可能感兴趣的:(Linux课堂笔记 | 进程1——进程管理)