《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)

  1. 设置task_struct中的标识成员设置为PF_EXITING
  2. 调用del_timer_sync()删除内核定时器, 确保没有定时器在排队和运行
  3. 调用exit_mm()释放进程占用的mm_struct
  4. 调用sem_exit(),使进程离开等待IPC信号的队列
  5. 调用exit_files()和exit_fs(),释放进程占用的文件描述符和文件系统资源
  6. 把task_struct的exit_code设置为进程的返回值
  7. 调用exit_notify()向父进程发送信号,并把自己的状态设为EXIT_ZOMBIE
  8. do_exit()调用schedule()切换到新进程继续执行

子进程进入EXIT_ZOMBIE之后,虽然永远不会被调度,关联的资源也释放掉了,但是它本身占用的内存还没有释放,比如创建时分配的内核栈,task_struct结构等。这些由父进程来释放。

如果父进程在子进程之前退出,则需给子进程在当前线程组内找一个线程作为父亲,如果不行,就让init做为它们的父进程。这样就保证了所有进程都有父进程.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东皇※太一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值