进程管理基础

1、概述

1.1 进程相关

1.1.1 进程和线程的区别

进程
1.进程是处于执行期的程序,进程=程序+执行
2.进程是资源封装的单位,拥有独立的资源空间,包含很多资源:打开的文件、挂起的信号量、内存管理、处理器状态、一个或多个执行线程或数据段等
3.进程通常通过fork系统调用来创建;
4.新创建的进程可以通过exec创建地址空间(用户栈初始化等),并载入新的可执行程序
5.进程退出可以自愿退出或非自愿退出。

线程
1.线程是轻量级进程,是操作系统调度的最小单位
2.一个进程可以有多个线程线程共享进程的资源空间
3.线程通过clone方法来创建,会确定哪些资源与父进程共享,哪些资源线程独享

1.1.2 获取当前进程

在内核态,ARM64运行级别为EL1,SP_EL0寄存器在EL1上下文没有使用,利用SP_EL0寄存器存放当前进程描述符**task_struct**的地址。

1.1.3 进程的3个数据结构

进程是资源封装的单位,linux有3个数据结构来组织进程描述符:
(1) 链表:所有task_struct形成一张链表
(2) 树:所有task_struct指向它的父、兄弟、子,所有task_struct形成树,pstree可以看出进程树情况,父进程类似于子进程的监视器,子进程死父进程通过查看子进程的尸体(task_struct)可以知道子进程死亡原因,并将其清理
(3) 哈希表,通过PID可以快速检索出task_struct

1.1.4 进程的生命周期

在这里插入图片描述
TASK_RUNNING(运行和就绪):进程要么在CPU上执行,要么准备执行;
TASK_INTERRUPTIBLE(浅度睡眠):除了等IO ready唤醒,信号也可以唤醒,进程状态修改为TASK_RUNNING;
TASK_UNINTERRUPTIBLE(深度睡眠):一定要等到IO ready唤醒,不能被异步信号打断的挂起状态, 对于不可中断的执行流程会用到这个状态;
TASK_STOPPED(暂停):当进程收到SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU信号会暂停;
TASK_ZOMBLE(僵尸状态): 所有的资源都被free 了,只剩下task_struct了。因为linux认为应用态有很多不可以预知的行为导致进程挂掉,因此它会在进程挂掉时自动释放进程资源,只保留task_struct来给父进程看原因,一旦父进程通过waitpid返回则task_struct也被释放。

1.2 进程调度相关

1.2.1 调度器

通过调度策略来选用调度器,每个调度器都定义了一个调度类,每个调度类中实现了本调度器的关键算法。

所有的调度类链接在一起并通过优先级排列,大内核调度器(scheduler)调度的时候将按照调度类的优先级进行调度。

调度器是一个操作系统的核心部分。可以比作是CPU时间的管理员。调度器主要负责选择某些就绪的进程来执行。不同的调度器根据不同的方法挑选出最适合运行的进程。目前Linux支持的调度器主要包括如下:

Stop scheduler、Deadline scheduler、RT scheduler、CFS scheduler、Idle scheduler

优先级从高到低排列为:

stop_sched_class -> dl_sched_class->rt_sched_class->fair_sched_class->idle_sched_class

1.2.2 进程抢占

(1)设置TIF_NEED_RESCHED标志
(2)对于某些情况下,不需要触发抢占,即不需要设置TIF_NEED_RESCHED标志,直接通过调用schedule 执行抢占,如:mutex, semaphore, waitequeue等执行阻塞操作时会直接调用schedule执行抢占。

2、进程调度

首先简单提一下这个宏和函数的被调用关系:

schedule() --> context_switch() --> switch_to() --> __switch_to()

这里面,schedule是主调度函数,当schedule()需要暂停A进程的执行而继续B进程的执行时,就发生了进程之间的切换。进程切换主要有两部分:
1、切换全局页表项;
2、切换内核堆栈和硬件上下文。
这个切换工作由context_switch()完成。其中switch_to__switch_to()主要完成第二部分。更详细的,__switch_to()主要完成硬件上下文切换,switch_to主要完成内核堆栈切换

阅读switch_to时请注意:这是一个宏,不是函数,它的参数prev, next, last不是值拷贝,而是它的调用者context_switch()的局部变量。局部变量是通过%ebp寄存器来索引的,也就是通过n(%ebp),n是编译时决定的,在不同的进程的同一段代码中,同一局部变量的n是相同的。在switch_to中,发生了堆栈的切换,即ebp发生了改变,所以要格外留意在任一时刻的局部变量属于哪一个进程。关于__switch_to()这个函数的调用,并不是通过普通的call来实现,而是直接jmp,函数参数也并不是通过堆栈来传递,而是通过寄存器来传递。

2.1 __schedule

在进程管理概述部分介绍过,进程的抢占分为触发抢占(设置TIF_NEED_RESCHED标记)和执行抢占,执行抢占的过程就是调用了schedule,其核心函数为__schedule

2.1.1 schedule调度方法

__schedule()是调度器的主函数,让调度器执行调度,并进入到__schedule()函数的方法主要有:

1.调用block的函数,如: mutex, semaphore, waitqueue等等。

2.在中断时返回用户空间时会检查TIF_NEED_RESCHED标志。例如:可以参考arch/x86/entry_64.S:
为了触发抢占,在定时器中断处理函数scheduler_tick()中,调度器会设置TIF_NEED_RESCHED 抢占标志;

3.唤醒一个进程并不真正会引发执行schedule()。唤醒只是添加一个进程到run-queue,仅此而已;假设,如果一个新的进程被添加到run-queue, 如果抢占当前运行的进程,唤醒操作会设置当前进程的TIF_NEED_RESCHED 标志,在第一次发生如下情形时,schedule()会被调用:
(1)如果kernel允许抢占(CONFIG_PREEMPT=y)

  • 在系统调用或异常上下文,preempt_enable()中会执行schedule()抢占调度(这种情形一般是在wake_up()后执行spin_unlock()时);
  • 在中断上下文,从中断处理函数返回到被中断的上下文

(2)如果kernel抢占被禁用 (CONFIG_PREEMPT is not set),那么在下一次遇到如下情形时会执行schedule()抢占调度

  • cond_resched()调用
  • schedule()调用
  • 从系统调用或异常返回到用户空间时
  • 从中断处理函数返回到用户空间时

2.2 switch_to

switch_to函数的精妙之处在于,switch_to执行的前半部分在旧进程上下文(记为进程A)执行,switch_to的后半部分在新选出进程的上下文(记为进程B)执行。准确的说,切换点位于cpu_switch_to。

3、debug

Linux工具快速教程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值