进程描述符task_struct

task_struct

{

  //进程基本信息

  pid 进程id号

  tgid 线程组id号,与线程组领头线程pid号相同   getpid()返回该值

  tasks init_struct链接所有task_struct结构

  run_list; //当前进程所处的运行链表

  array 指向与进程相关的prio_array_t结构

  real_parent当前进程的父进程,没有的话将会变成进程1(init)的描述符

  parent  被执行跟踪时的跟踪父进程(ptrace)

  children  链接所有子进程

  sibling  兄弟进程链表

  group_loader  进程组领头进程的描述符指针

  signal->pgrp  进程组领头进程的PID

  signal->session  登录会话领头进程的pid

  ptrace_children  所有被本进程跟踪的子进程链表

  ptrace_list    指向所跟踪进程其实际父进程的链表的前一个和下一个元素????

  pid pids[4];    //四个结构用于查找指定 进程id、进程组id、线程组id、会话id。每个结构用于分别保存相同散列值的相同或不同pid列表

  //资源限制

  signal->rlim[rLIMIT_CPU...].rlim_cur;  //当前资源限制

  signal->rlim[rLIMIT_CPU...].rlim_max;  //普通用户最大权限

  //进程切换

  thread_struct thread;//保存进程切换时内核硬件上下文

  进程优先级

  进程运行状态(可运行、可中断等待、不可中断等待、暂停、跟踪、僵死、僵死撤销)

  //进程地址空间 mm_struct

  //当前目录 fs_struct

  //进程访问文件 files_struct

  //所接收的信号 signal_struct

  //相关的tty  tty_struct

}

不可中断状态:驱动在进行一些不能被中断的操作,因此进程处于该状态。

暂停状态:进程收到SIGSTOP SIGTSTP SIGTTIN SIGTTOU进入暂停状态

跟踪状态:进程执行处于被debugger程序暂停的状态

僵死状态:进程执行终止,等待返回进程信息时。

僵死撤销状态:为防止多线程同时等待进程终止时的信息,信息被获取后的状态

线程描述符thread_info

thread_info与内核态堆栈放在一起占用一个(4K)或两个页(8k)(用户态堆栈不在这里)

内核态时可以通过堆栈寄存器获取当前thread_info结构的地址。

thread_info

{

  cpu//当前CPU

}

prio_array_t

{

  int nr_active;        链表中进程描述符的数量

  unsigned long[5] bitmap;    当某个优先权链表不为空时对应位为1

  struct list_head[140]  queue;  140个优先权队列

}

将不同优先权的进程排入不同链表

进程组和线程组的概念

进程组:表示一个作业(job),例如 ls|sort|more三个进程处于一个进程组

进程组:进程描述符中signal->pgrp相同的所有进程处于一个进程组

线程组:进程描述符中tgid相同的所有进程处于一个线程组。

getpid() kill() _exit()对线程组整体起作用。

线程组所有成员死亡后才会产生一个信号通知线程组的领头进程的父进程

通过pid快速查找进程

为了能快速找到对应进程描述符,内核引入四个散列表(保存在pid_hash数组)

进程pid散列表

线程组tgid散列表

进程组pgrp散列表

会话session散列表

pid

{

  int nr;//对应类型的pid数值

  struct hlist_node pid_chain;//相同散列值但pid不同的链表 链接pid结构

  struct list_head  pid_list;//相同散列值相同pid的进程双向链表  链接pid结构

}

对于每一个散列表,进程描述符有一个pid数据结构对应

进程的组织

运行状态有对应链表

停止、僵死、僵死撤销 状态没有对应的链表

可中断等待和不可中断等待有多种独立的等待队列

等待队列

struct __wait_queue_head

{

  spinlock_t lock;    //自旋锁

  struct list_head task_list;//非互斥进程从第一个位置放,互斥进程放在最后一个

}

wait_queue_t

{

  unsigned int flags;//1表示等待队列是互斥资源的访问,0等待队列是非互斥资源

  struct task_struct *task;  //对应的进程描述符

  wait_queue_func_t func;//表示等待队列的唤醒函数

  struct list_head list;//所有排入等待队列的wait_queue_t

}

sleep_on类函数在一些条件不能使用:必须测试条件并且当条件还没得到验证时又紧接着让进程去睡眠????

sleep_on(wait_queue_head_t)  (非互斥:一旦条件满足则所有非互斥等待都会唤醒!!!)

interruptible_sleep_on()      (非互斥)

sleep_on_timeout()        (非互斥)

interruptible_sleep_on_timeout()  (非互斥)

prepare_to_wait()       //(非互斥)需要自己调用schedule()或者schedule_timeout()

prepare_to_wait_exclusive()  //(互斥)需要自己调用schedule()或者schedule_timeout()

finish_wait()

唤醒函数

wake_up           不带nr和all则只唤醒一个互斥进程

wake_up_nr          nr代表唤醒互斥进程的数量

wake_up_all          all代表唤醒所有互斥进程

wake_up_interruptible      interruptible代表只唤醒可中断睡眠,不带该后缀唤醒两种睡眠

wake_up_interruptible_nr    

wake_up_interruptible_all

wake_up_interruptible_sync    sync代表如果唤醒 进程优先级更高**不会**立即执行该高优先级进程

wake_up_locked        当等待队列中的自旋锁已经被持有时使用

进程资源限制

地址空间最大数        RLIMIT_AS

内存信息转储空间大小     RLIMIT_CORE

进程使用CPU的最长时间    RLIMIT_CPU

堆大小的最大值        RLIMIT_DATA

文件大小最大值        RLIMIT_FSIZE

文件锁数量最大值       RLIMIT_LOCKS

非交换内存的最大值      RLIMIT_MEMLOCK

消息队列中的最大字节数    RLIMIT_MSGQUEUE

打开文件描述符的最大数    RLIMIT_NOFILE

用户拥有进程最大数      RLIMIT_NPROC

进程拥有页框最大数      RLIMIT_RSS

进程挂起信号的最大数       RLIMIT_SIGPENDING

栈大小的最大数        RLIMIT_STACK

进程切换

thread_struct

{

  eip//进程恢复执行后需要执行的首地址(保存+加载)

  esp//进程切换时内核态指针(保存+加载)

  esp0//内核态初始指针(仅加载)

  tls_array[3];//线程局部存储段(仅加载)

  fs  gs;段寄存器(保存+加载)

  debugreg;调试寄存器dr0-dr3 dr6-dr7(仅加载)

  io_bitmap_ptr//表示IO权限位图是否有数据

}

硬件上下文:进程恢复执行前必须装入寄存器的一组数据

硬件上下文的一部分放在TSS段中,剩余部分在内核态堆栈中

可执行上下文:进程执行时需要的所有信息

可执行上下文包含硬件上下文

进程切换只发生在内核态,在切换之前,用户态进程使用的所有寄存器内容已保存在内核态堆栈上(包括用户态堆栈信息)

任务状态段(TSS):保存内核态堆栈地址,检查in out指令执行时是否有IO许可权,linux中每个CPU只有一个TSS段

thread_struct:在任务描述符中在进程切换时保存内核硬件上下文(包含大部分CPU寄存器,不包括eax、ebx这些通用寄存器(这些在内核堆栈中))

switch_to函数执行步骤(主要堆栈切换、执行指针切换)

1.将prev和next分别存入eax和edx防止堆栈切换导致指针变化

2.保存需要保存的寄存器信息eflag和ebp(pushfl ;pushl ebp)

3.将原堆栈指针esp保存在prev的结构中

4.从next中将新堆栈指针写到esp中

5.将原进程恢复后需要执行的地址存入prev中

6.将新进程next的eip压入到新进程的栈中(栈已经切换完成)

7.调用__switch_to,该函数引用eax和edx获得两个进程的其他硬件上下文并进行切换(FPU、MMX、XMM寄存器)

8.恢复堆栈中的寄存器eflag和ebp

9.从eax寄存器拷贝到last变量(本次进程切换被切出的进程描述符地址)

__switch_to函数执行步骤(引用eax和edx获得本次切出和切入进程的进程描述符,栈顶保存了需要恢复执行的地址)

1.__unlazy_fpu()保存切出进程的FPU、MMX和XMM寄存器

2.获得cpu下标(内核栈切换完毕,通过栈指针找到thread_info,内部的cpu字段)

3.将thread.esp0装入对应本地CPU的TSS的esp0字段(用户态切内核态后内核态的堆栈指针)

4.装入线程局部存储段(TLS)thread.tls_array[0-2]

5.保存原线程的fs、gs

6.加载新线程的fs、gs(实际可能会产生无效的段寄存器值异常,并触发修正!!!!)

7.加载6个调试寄存器dr0-dr3 dr6-dr7

8.根据io_bitmap_ptr,懒惰模式设置iobitmap,需要时会产生异常,然后更新。

9.返回值为eax被切出的进程描述符,返回的执行地址为栈顶的标号1地址

考虑进程A的切出和切入

prev、next、last为局部变量(保存在堆栈中)

last用于返回被切出的进程描述符

切出A          切入A

进程A切换为进程B           进程C切换成进程A

A      B      C      A

prev=A   =>   prev=B    prev=C  =>  prev=A

next=B   =>   next=other    next=A =>  next=B

eax=prev => last=eax=A         eac=prev=C      last=eax=C

第一列和第四列能看出,切出是的堆栈状态和切入时相同

创建进程

轻量级进程:共享页表、打开文件表、信号处理。

vfork:使用clone实现,指定SIG_CHLD信号 flag为CLONE_VM CLONE_VFORK,堆栈为当前堆栈

//创建的子进程和父进程共享内存地址空间,父进程在子进程退出或运行一个新的程序前阻塞。clone

fork:使用clone实现,子进程结束给父进程发送SIG_CHLD信号,clone标志为0,堆栈为当前堆栈(依赖写时复制机制可以同时运行)

clone:(需要传递进程函数、参数、新的堆栈、线程局部存储段(TLS)、ptid、ctid)

CLONE_VM        共享页表

CLONE_FS         共享根目录和当前工作目录umask、 不能和CLONE_NEWNS同时设置!!!

CLONE_FILES        共享打开文件

CLONE_SIGHAND       共享信号处理 必须共享内存描述符CLONE_VM!!!

CLONE_PTRACE      共享被调试状态、

CLONE_VFORK      VFORK?????

CLONE_PARENT   共享父进程、

CLONE_THREAD  共享线程组 必须共享信号CLONE_SIGHAND!!!

CLONE_NEWNS    新建命名空间  不能和CLONE_FS同时设置!!!

CLONE_SYSVSEM    共享IPC取消信号量操作、

CLONE_SETTLS    新建TLS(局部存储段)

CLONE_PARENT_SETTID  将子进程PID返回给父进程ptid、?????????

CLONE_CHILD_CLEARTID  子进程退出或执行新程序时清除指定变量ctid、?????

CLONE_UNTRACED      禁止内核线程跟踪进程、

CLONE_CHILD_SETTID    将字进程PID返回给子进程ptid、??????????

CLONE_STOPPED      子进程默认停止状态

内核线程

使用dofork实现,CLONE_VM、CLONE_UNTRACED

进程0

从无到有创建的内核线程,初始化内核需要的所有数据结构,每个CPU都会启动一个进程用于空闲时运行,启动进程1

进程1

完成内核初始化,调用execve系统调用装入可执行程序init,变为普通进程,拥有自己的每进程内核数据结构???。

进程克隆过程????暂时不总结

3.5进程终止、进程删除?????暂时不总结

几个进程能并发地执行同一个程序,而同一个进程能顺序的执行几个程序??????

深入理解linux内核-进程和程序的更多相关文章

  1. 读书笔记之Linux系统编程与深入理解Linux内核

    前言 本人再看深入理解Linux内核的时候发现比较难懂,看了Linux系统编程一说后,觉得Linux系统编程还是简单易懂些,并且两本书都是讲Linux比较底层的东西,只不过侧重点不同,本文就以Linu ...

  2. 《深入理解Linux内核》 读书笔记

    深入理解Linux内核 读书笔记 一.概论 操作系统基本概念 多用户系统 允许多个用户登录系统,不同用户之间的有私有的空间 用户和组 每个用于属于一个组,组的权限和其他人的权限,和拥有者的权限不一样. ...

  3. 【读书笔记::深入理解linux内核】内存寻址【转】

    转自:http://www.cnblogs.com/likeyiyy/p/3837272.html 我对linux高端内存的错误理解都是从这篇文章得来的,这篇文章里讲的 物理地址 = 逻辑地址 – 0 ...

  4. 【读书笔记::深入理解linux内核】内存寻址

    我对linux高端内存的错误理解都是从这篇文章得来的,这篇文章里讲的 物理地址 = 逻辑地址 – 0xC0000000:这是内核地址空间的地址转换关系. 这句话瞬间让我惊呆了,根据我的CPU的知识,开 ...

  5. 35、在编译Linux内核中增加程序需要完成以下3项工作

    在编译Linux内核中增加程序需要完成以下3项工作: 将编写的源代码拷入Linux内核源代码的相应目录. 在目录的Kconfig文件中增加关于新源代码对应项目的编译配置选项 在目录的Makefile文 ...

  6. 深入理解Linux内核-进程

    1.进程的静态特性 进程:程序执行时的一个实例 进程描述符(task_struct): 进程的基本信息(thread_info).指向内存区描述符的指针(mm_struct).进程相关的tty(tty ...

  7. 尝试理解Linux容器进程与宿主机共享内核到底是什么意思?

    背景 近期接触容器技术时,经常看到各类比较容器与虚拟机区别的文章中会提到:容器是共享宿主机的内核,而虚拟机则是拥有自己独立的内核,所以不可能在Linux上用容器运行windows,但是用虚拟机则可以. ...

  8. 《深入理解linux内核》第三章 进程

    进程的七种状态 在内核源码的 include/linux/sched.h文件中: task_struct的status可表示 #define TASK_RUNNING 0 #define TASK_I ...

  9. linux内核--进程与线程

    http://blog.csdn.net/yusiguyuan/article/details/12154823 在<linux内核设计与实现>中第三章讲解了进程管理,在关于进程和线程的概 ...

随机推荐

  1. ST表 「 从入门到入门 · 浅显理解 」

    ST 表是个好东西,虽然前些天 ldq 学长已经讲完啦,但是那天他讲了那么多,让智商受限的我完全没有全部接受,选择性的扔掉了一部分(其实不舍的扔,记不住QAQ). ST 表最简单的应用就是查询区间最大 ...

  2. Python基础之各种推导式玩法

    一.推导式套路 除了我们之前所学习的列表推导式和生成器表达式之外,还有字典推导式.集合推导式等等. 下面就是一个以列表推导式为例的推导式详细格式,同样适用于其他推导式. variable = [out ...

  3. python pillow 处理图片

    demo1 #打开图片,并随机添加一些椒盐噪声 from PIL import Image import numpy as np import matplotlib.pyplot as plt img ...

  4. base/7/x86_64/filelists_db FAILED

      解决办法: [root@localhost ~]# cd /var/lib/rpm [root@localhost rpm]# rm -rf __db.* # 清除原 rpmdb 文件 [root ...

  5. [bzoj 2768]&[bzoj 1877]

    传送门1 传送门1 Solution 两道比较裸的题... 复习一下最大流和费用流的模板. Code[bzoj 2768][JLOI 2010] 冠军调查 #include<bits/stdc+ ...

  6. 如何利用shell或者awk二维数组实现9x9乘法表?

    第一种:利用shell for循环来实现. for i in `seq 1 9`do for j in `seq 1 9` do if [ $i -ge $j ] then echo -en &quo ...

  7. Java Heap dump文件分析工具jhat简介

    jhat 是Java堆分析工具(Java heap Analyzes Tool). 在JDK6u7之后成为标配. 使用该命令需要有一定的Java开发经验,官方不对此工具提供技术支持和客户服务. 用法: ...

  8. 全局设置页面颜色 返回按钮样式 iOS

    思路 1.建个UIViewController的分类 2.hook方法viewDidLoad(Aspects是三方库 可以不用) 3.看下面蓝色部分代码 #import "UIViewCon ...

  9. 手写一个MVVM

    最近看了珠峰的架构课——实现一个MVVM. 首先,我们来了解一下什么是MVVM. MVVM是Model-View-ViewModel的简写.它本质上就是MVC 的改进版.MVVM 就是将其中的View ...

  10. xgboost 源码学习

    官方代码结构解析,README.MD XGboost 回归时,损失函数式平方误差损失 分类时,是对数自燃损失: Coding Guide ====== This file is intended to ...