操作系统原理Linux篇 读书笔记(2)——Linux进程管理

本文详细探讨了Linux操作系统中的进程管理,涵盖了进程的组成、执行状态、进程空间与系统空间、上下文切换、进程标识哈希表、调度策略、创建与撤销、管道通信以及IPC(信号量、消息队列和共享内存)机制。通过深入解析Linux内核如何管理进程,阐述了进程的生命周期、状态转换、资源分配和同步通信等方面的知识。
摘要由CSDN通过智能技术生成
  • 1.Linux进程的组成
    Linux进程组成:由 正文段(text)、用户数据段(user segment)和系统数据段。
    ➢ 正文段:存放进程要执行的指令代码。Linux中正文段具有只读属性。
    ➢ 用户数据段:进程运行过程中处理数据的集合,它们是进程直接进行操作的所有数据,包括进程运行处理的数据段和进程使用的堆栈。
    ➢ 系统数据段:存放反映一个进程的状态和运行环境的所有数据。这些数据只能由内核访问和使用。 在系统数据段中包括进程控制块PCB.。
    在Linux中,PCB是一个名为task_struct的结构体,称为任务结构体。

  • 2.Linux进程在处理机上的执行状态
    在Linux系统中,用户不能直接访问系统资源,如处理机、寄存器、存储器和各种外围设备。因此提供了两种不同指令:
    ➢ 一般指令:供用户和系统编程使用,不能直接访问系统资源;
    ➢ 特权指令:供操作系统使用,可以直接访问和控制系统资源;
    为了区分处理机在执行那种指令,通常将处理机的执行状态又分为两种:
    ➢ 管态(内核态、系统)
    ➢ 目态(用户态)
    由目态转变为管态的情况可能有:
    ➢ 进程通过系统调用向系统提出服务请求
    ➢ 进程执行某些不正常的操作时,如除数为0、超出权限的非法访问、用户堆栈溢出等。
    ➢ 进程使用设备进行I/O操作时,当操作完成或设备出现异常情况时。

  • 3.进程空间和系统空间
    进程的虚拟地址空间:Linux操作系统运行在多道环境下,多个进程能够同时在系统中并发活动。为了防止进程间的干扰,系统为每个进程都分配了一个相对独立的虚拟地址空间,又称为进程的虚拟内存空间。
    进程的虚拟地址空间包含进程本身的代码和数据等,同时还包括操作 系统的代码和数据。故进程的虚拟地址空间分两部分:进程空间和系统空间。
    进程空间(如图):
    注:
    ➢ 内核堆栈:进程在需要使用内核功能而通过系统调用运行内核代码时,需要使用堆栈保存数据。这个堆栈是由系统内和使用的,故称内核堆栈。
    ➢ 其中系统数据段,只能在内核态执行
    系统空间:操作系统的内核映射到进程的虚拟地址空间。(可供多个进程共享,只能内核态执行)
    进程的虚拟空间
  • 4.进程上下文和系统上下文
    进程上下文:进程的运行环境动态变化,在linux中把进程的动态变化的环境总和称为进程上下文。
    ➢ 当前进程
    ➢ 进程切换
    ➢ 上下文切换
    ➢ 进程通过系统调用执行内核代码时,内核运行是为进程服务,所以此时内核运行在进程上下文中。
    系统上下文: 内核除了为进程服务,也需要完成操作系统本身任务,如响应外部设备的中断、更新有关定时器、重新计算进程优先级等。 故把系统在完成自身任务是的运行环境称为系统上下文(system context).

  • 5.Linux进程的状态及转换
    linux中进程状态分为5种。
    每个进程在系统中所处的状态记录在它的任务结构体的成员项state中。进程的状态用符号常量表示,它们定义在/include/linux/sched.h下:

 - #define TASK_RUNNING           0          可运行态(运行态、就绪态)
 - #define TASK_INTERRUPTIBLE     1          可中断的等待态
 - #define TASK_UNINTERRUPTIBLE  2          不可中断的等待态
 - #define TASK_ZOMBLE            3          僵死态
 - #define TASK_STOPPED            4          暂停态
  • a.运行态(running)——运行,该进程称为当前进程(current process)
    实际上linux并没有该状态,而是将其归结在可运行态。系统中设置全局指针变量current,指向当前进程。
  • b.可运行态(Running)——就绪
    Linux中把所有处于运行、就绪状态的进程链接成一个双向链表,称为可运行队列(run_queue)。使用任务结构体中的两个指针:
    Struct task_struct next_run;/指向后一个任务结构体的指针*/
    Struct task_struct prev_run;/指向前一个任务结构体的指针*/
    该链表的首结点为init_task。系统设置全局变量nr_running记录处于运行、就绪态的进程数。
  • c.等待态(wait)——阻塞
    在linux中将该状态进一步划分为:可中断的等待态(interruptible)和不可中断的等待状态(uninterruptible)。
    ➢ 可中断的等待态的进程可以由信号(signal)来解除其等待态,收到信号后进程进入可运行态。
    ➢ 不可中断的等待状态的进程,一般都是直接或间接在等待硬件条件,只能用特定的方式来解除其等待状态,如是用wakeup()。
    处于等待态的进程根据其等待的事件排在不同的等待队列中。Linux中等待队列是由一个wait_queue结构体组成的单向循环链表。该结构体定义在include/linux/wait.h中,如下所示:
Struct wait_queue   {
Struct task_struct *task/*指向一个等待态的进程的任务结构体*/
Struct wait_queue *next;/*指向下一个wait_queue结构体*/
}

注:
➢ 与可运行队列不同,等待队列不是直接由进程的任务结构体组成队列,而是由于任务结构体对应的wait_queue构成。
➢ 每个等待队列都有一个指向该队列的队首指针,它一般是个全局指针变量。

  • d.暂停态(stopped)
    暂停态:进程由于需要接受某种特殊处理而暂时停止运行所处的状态。通常,进程在接受到外部进程的某个信号(SIGSTOP、SIGSTP、SIGTTOU)而进入暂停态。通常正在接受调试的进程就处于暂停态。
  • e.僵死态(zombie)
    僵死态:进程的运行已经结束,但是由于某种原因它的进程结构体仍在系统中。
  • 6.进程标识哈希表
    Linux的进程表示哈希表提供了按照哈希算法从进程PID快速查找对应任务结构体的方法,实现哈希算法的哈希函数定义为带参数的宏pid_hashfn(x),如下所示:
#define pid_hashfn(x) ((((x)>>8)^(x))&(PIDHASH_SZ-1))???

其中:参数x就是进程的标识PID,计算结果的哈希值用于检索对应的任务结构体。例如PID为228的哈希值是100,PID为27536的哈希值是123,PID为 27535的哈希值是100。
为了解决哈希值冲突的问题,Linux把具有相同哈希值的PID对应的进程组成一个个双向循环链表。在task_struct中的两个成员项:
Struct task_struct * pidhash_next;/指向后一个任务结构体的指针/
Struct task_struct * pidhash _prev;/指向前一个任务结构体的指针/
1.进程标识哈希表
Linux 使用一个称为pidhash[]的指针数组管理这些链表,称为进程标识哈希表。该表中记录各个链表首结点地址,数组元素的下标与链表的哈希值相同。在include/linux/sched.h中pidhash[]数组定义如下:

Struct task_struct * pidhash[PIDHASH_SZ]

从定义中可以看出,
➢ pidhash数组由PIDHASH_SZ个元素组成,每个元素是指向一个进程任务结构体的指针。
➢ 数组元素个数PIDHASH_SZ是系统中最多可容纳的进程数NR_TASKS除以4,定义如下:

#define PIDHASH_SZ(NR_TASKS>>2)???

图4.7Linux进程标识哈希表
2进程标识哈希表操作函数:
Hash_pid():进程创建时,将其任务结构体插入哈希链表
Unhash_pid():进程撤销时,将其任务结构体从哈希链表中删除。
Find_task_by_pid():根据PID相应进程的任务结构体。

  • 7.Linux进程调度策略
    Linux是一个同时具有分时和实时系统特征的操作系统。Linux在进程调度中采用的是可抢占的调度方式。
    Linux中的进程分为普通进程和实时进程。实时进程的优先级高于普通进程。对实时进程和普通进程采用不同的调度策略。
    Linux为每个进程都规定了一种调度策略,并记录在其任务结构体policy成员项中。Linux调度策略有3种,它们以符合常量的形式定义在/include/linux/sched.h中,其定义及意义如下所示:
#define SCHED_OTHER  0 普通进程的时间片轮转算法(根据优先权选择下一个进程)
#define SCHED_FIFO    1 实时进程的先进先出算法(适用于响应时间要求比较严格的短小进程)
#define SCHED_RR      2 实时进程的时间片轮转算法(适用于响应时间要求比较严格的较大进程)

因此在linux的可运行队列中,从调度策略来分SCHED_FIFO的实时进程具有最高优先级,其次是SCHED_RR的实时进程,而SCHED_OTHER的普通进程优先级最低。

  • 8.Linux进程调度方法
    实时进程的优先级大于普通进程的优先级,故只有当可运行队列的所有实时进程都运行完成后,普通进程才能得到运行。
    linux普通进程的优先级由Priority和counter共同决定。在进程运行过程中Priority保持不变,体现了进程的静态优先级概念;而counter不断减少,表示了进程的动态优先级。采用动态优先级的方法,使得一个进程占用CPU的时间越长,counter的值越小。这样使得每个进程都可以公平地分配到CPU。

    1. Linux进程调度时机
      Linux进程调度是由Schedule()完成的。该函数定义在/kernel/sched.c中。执行该函数 的情况可以分为两种:
      ➢ 在某些系统调用函数中直接调用Schedule()。
      ➢ 在系统运行过程中,通过检查调度标志而执行该函数。进程调度标志是一个名为need_resched的全局变量,当它的值为1时,表明需要执行调度函数。
      下面介绍几种需要执行进程调度的时机:
      1.进程状态发生变化时
      Linux进程状态不断发生变化,在下列状态转换是需要执行进程调度:
      1)当前进程进入等待状态
      例如,运行太的进程可以通过执行系统调用sleep_on()主动放弃CPU而进入等待状态。Sleep_on()的部分源代码如下:
Current->state=state;                 /* 把当前进程状态设置为等待状态*/
Save_flags(flags);
_add_wait_queue(p,&wait);           /*把当前进程加入等待队列*/
Sti();
Schedule();                        /*执行进程调度*/
Cli();

2)运行态下的进程运行结束后
一般通过调用内核函数do_exit()终止运行进程并转入僵死状态。该函数部分源码:

……
Current->state = TASK_ZOMBIE; /*把当前进程设置为僵死状态*/
……
Schedule();/*执行调度程序*/
……

3)使用wake_up_process()将处于等待状态的进程唤醒,然后将它置于可运行状态。该函数部分源码:

Save_flags(flafs);
Cli();
p->state = TASK_RUNNING; /*把进程置为可运行态*/
if(!p—>next_run)
    add_to_runqueue(p);   /*加入到可运行队列*/
restore_flags(flags);
if(p->counter>current->counter+3)
    need_resched =1;     /*调度标志置位,执行进程调度*/

4)当一个进程的程序接受调试时。
调式进程向被调试进程发送SIGSTOP信号,被调试进程处理该信号时调用内核函数do_signal()。部分源码:

……
Current->state = TASK_STOPPED   /*把当前进程置为暂停态*/
Notify_parent(current);
Schedule();/*执行进程调度*/
……

5)当被调试的进程接收到调试进程发送的SIGCONT信号时,执行send_sig(),其中使用wake_up_process()解除被调试进程的暂停态而重新进入可运行态。

If(sig==SOGKILL||sig==SIGCONT))
{
    If(p->state==TASK_STOPPED) /*若进程为暂停态*/
    wake_up_process(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值