《linux内核设计与实现》读书笔记linux进程管理
Posted 东皇※太一
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《linux内核设计与实现》读书笔记linux进程管理相关的知识,希望对你有一定的参考价值。
进程与线程
①进程就是处于执行期的程序,通常进程还包含挂起的信号,内核内部数据,处理器状态,一个或多个具有内存映射的内存地址空间及一个或多个执行线程,还包含存放全局变量的数据段等。
②线程是进程中活动的对象,每个线程都拥有一个独立的程序计数器、进程栈和一组进程寄存器。进程和线程是程序运行时状态,是动态变化的,进程和线程的管理操作(比如,创建,销毁等)都是有内核来实现的。
③我们使用操作系统就是为了运行用户程序,而内核调度的对象是线程,对linux而言,不严格区分进程和线程,线程就是一种特殊的进程,或轻量级进程。
④进程提供两种虚拟机制:虚拟处理器和虚拟内存,这两种虚拟机制给进程一种假象,让这些进程觉得:
每个进程拥有独立的虚拟处理器和虚拟内存
每个线程拥有各自的虚拟处理器,同一个进程中的线程可以共享虚拟内存。
注:程序本身并不是进程,进程是处于执行期的程序以及相关资源的总称。
进程描述符及结构
内核把进程的列表存放在任务队列的双向循环链表中(task list),链表中每一项都是类型为task_struct、这就是进程描述符的结构,在<linux/sched.h>文件中定义。
进程描述符中包含了一个具体进程的所有信息。
进程分配描述符
Linux通过slab分配器分配task_struct结构,只需创建一个新的结构struct thread_info,该结构在<thread_info.h>中定义:
struct thread_info
struct task_struct *task; //指向当前进程内核栈对应的进程的进程描述符
struct exec_domain *exec_domain;
__u32 flags;
__u32 status;
__u32 cpu;
int preempt_count;
mm_segment_t addr_limit;struct restart_block
restart_block;void *sysenter_return;
int uacc ess_err;
;
每个人物的thread_info结构在它的内核栈的尾端分配,结构中task域中存放的是指向该任务实际task_struct的指针。
内核中通过PID来标识每个进程,PID的类型为pid_t,实际是int类型,最大值在<linux/threads.h>中定义,也可通过修改/proc/sys/kernel/pid_max的值来提高。
进程的状态
系统中的每个进程都必然处于五种进程状态中的一种。
进程的各个状态之间的转化构成了进程的整个生命周期,如下:
该域的值也必为下列五种状态标志之一:
TASK_RUNNING:等待执行或者正在执行
TASK_INTERRUPTIBLE:正在睡眠或者被阻塞
TASK_UNINTERRUPTIBLE:不可中断
__TASK_TRACED:被其它进程跟踪的进程
__TASK_STOPPED:进程停止执行
内核中调整某个进程的状态,通过set_task_state(task,state)函数,在<linux/sched.h>中。
系统调用和异常处理程序是对内核明确定义的接口,进程只有通过这些接口才能陷入内核执行,对内核的所有访问都必须通过这些接口。
进程的创建
①首先调用fork(),通过拷贝当前进程创建一个子进程。
②然后exec()函数族负责读取可执行文件并将其载入地址空间开始运行。
③因为子进程几乎是父进程的完全复制,进程在创建它的时刻开始存活,所以父子两个进程会运行同一个程序。因此需要用一种方式来区分它们,否则这两个进程只能做同样的事。
④父子进程最重要的区别是:fork()的返回值不同。父进程中的返回值是子进程的进程号(PID),而子进程中返回值是0,因此可通过返回值来判断该进程是父进程还是子进程。
⑤ linux中的fork使用了写时拷技术,就是平时父子进程共享同一个拷贝,只有在需要写入的时候数据才会被复制,从而使各个进程拥有各自的拷贝。避免了拷贝大量根本就不会使用的数据。
创建过程:
fork()——》clone() ——》do_fork() ——》copy_process() ——》dup_task_struct()
新创建的子进程被唤醒并让其投入运行,因为一般子进程都会马上调用exec()函数。
进程的终止
子进程中通过调用do_exit()退出,或者 内核的其它部分调用kthread_stop()退出。
子进程上的操作(do_exit)
- 设置task_struct中的标识成员设置为PF_EXITING
- 调用del_timer_sync()删除内核定时器, 确保没有定时器在排队和运行
- 调用exit_mm()释放进程占用的mm_struct
- 调用sem_exit(),使进程离开等待IPC信号的队列
- 调用exit_files()和exit_fs(),释放进程占用的文件描述符和文件系统资源
- 把task_struct的exit_code设置为进程的返回值
- 调用exit_notify()向父进程发送信号,并把自己的状态设为EXIT_ZOMBIE
- do_exit()调用schedule()切换到新进程继续执行
子进程进入EXIT_ZOMBIE之后,虽然永远不会被调度,关联的资源也释放掉了,但是它本身占用的内存还没有释放,比如创建时分配的内核栈,task_struct结构等。这些由父进程来释放。
如果父进程在子进程之前退出,则需给子进程在当前线程组内找一个线程作为父亲,如果不行,就让init做为它们的父进程。这样就保证了所有进程都有父进程.
以上是关于《linux内核设计与实现》读书笔记linux进程管理的主要内容,如果未能解决你的问题,请参考以下文章