博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux进程管理
阅读量:4286 次
发布时间:2019-05-27

本文共 2807 字,大约阅读时间需要 9 分钟。

Linux系统中进程的四要素:

①有一段程序供其执行。这段程序不一定是某个进程所专有,可以与其他进程共用。
②有进程专用的内核空间堆栈。
③在内核中有一个task_struct数据结构,即通常所说的“进程控制块”。有了这个数据结构,进程才能成为内核调度的一个基本单位接受内核的调度。
④有独立的用户空间。(有独立的用户空间的是进程,没有独立的用户空间但有共享的用户空间的是用户线程,既没有独立的用户空间也没有共享的用户空间的是内核线程)


Linux中进程的状态不仅仅有就绪、执行、阻塞这三大基本状态,如图:

这里写图片描述

比较重要的几个状态是:

①TASK_RUNNING
进程正在被CPU执行,或者已经准备就绪,随时可以执行。当一个进程刚被创建时,就处于TASK_RUNNING状态。

②TASK_INTERRUPTIBLE

处于等待中的进程,待等待条件为真时被唤醒,也可以被信号或者中断唤醒。

③TASK_UNINTERRUPTIBLE

处于等待中的进程,待资源有效时唤醒,但不可以由其它进程通过信号(signal)或中断唤醒。

④TASK_KILLABLE

Linux2.6.25新引入的进程睡眠状态,原理类似于TASK_UNINTERRUPTIBLE,但是可以被致命信号(SIGKILL)唤醒。

⑤TASK_TRACED

正处于被调试状态的进程。

⑥TASK_DEAD

进程退出时(调用do_exit),所处的状态。

在Linux内核代码中,线程、进程都使用结构task_struct来表示,该结构定义在sched.h。它包含了大量描述进程/线程的信息,其中比较重要的有:

pid_t pid; //进程号
vlong state; //进程状态
vint prio; //进程优先级


进程调度

进程调度就是从就绪的进程中选一个进程出来执行。主要的知识点有三个:
①调度策略
②调度时机
③调度步骤

Linux支持丰富的调度策略,常见的有:

  • SCHED_NORMAL(SCHED_OTHER):普通的分时进程
  • vSCHED_FIFO :先入先出的实时进程
  • vSCHED_RR:时间片轮转的实时进程
  • vSCHED_BATCH:批处理进程
  • vSCHED_IDLE: 只在系统空闲时才能够被调度执行的进程

实时进程一般优先级较高。


调度时机,也就是什么时候进行进程调度,更具体的说法是什么时候调用schedule()函数。调度时机分为主动式和被动式。

主动式:当进程需要等待资源等而暂时停止运行时,会把自己的状态置于挂起(睡眠),并主动请求调度,让出CPU。

current->state = TASK_INTERRUPTIBLE; schedule();

current是一个task_struct结构的指针,该指针指向当前正在运行的进程的进程控制块(task_struct),通过该指针可以修改进程控制块中进程的状态。

被动式调度又被称为抢占式调度,分为用户态抢占(Linux2.4、Linux2.6支持)和内核态抢占(Linux2.6支持)。

用户态抢占可能发生在:

①从系统调用返回用户空间。
②从中断处理程序返回用户空间。

这并不是表示一定会发生用户抢占。从内核返回用户空间的时候,它会检查need_resched标志的值,此时如果need_resched标志为1,会导致schedule()被调用,即发生用户抢占。need_resched被设置的时机为:

①当进程耗尽它的时间片时,会设置need_resched标志为1
②当一个优先级更高的进程进入可执行状态的时候,也会设置need_resched标志。

用户态抢占存在缺陷。当进程/线程一旦运行到内核态,就可以一直执行,直到它主动放弃或时间片耗尽为止。这样会导致一些非常紧急的进程或线程将长时间得不到运行,降低整个系统的实时性。改进方式是允许系统在内核态也支持抢占,更高优先级的进程/线程可以抢占正在内核态运行的低优先级进程/线程,这就是内核抢占。

内核抢占可能发生在:

①中断处理程序完成,返回内核空间之前。
②当内核代码再一次具有可抢占性的时候,如解锁及使能软中断等。

但在支持内核抢占的系统中,某些特例下是不允许抢占的:

①内核正在运行中断处理。
②内核正在进行中断上下文的Bottom Half(中断的底半部)处理。硬件中断返回前会执行软中断,此时仍然处于中断上下文中。
③进程正持有spinlock自旋锁、writelock/readlock读写锁等,当持有这些锁时,不应该被抢占,否则由于抢占将可能导致其它进程长期得不到锁,而让系统处于死锁状态。
④内核正在执行调度程序Scheduler。

为保证Linux内核在以上情况下不会被抢占,抢占式内核使用了一个变量preempt_count,称为内核抢占计数。这一变量被设置在进程的thread_info结构中。每当内核要进入以上几种状态时,变量preempt_count就加1,指示内核不允许抢占。每当内核从以上几种状态退出时,变量preempt_count就减1,同时进行可抢占的判断与调度。


调度步骤,即schedule()函数的工作流程如下:

①保存程序计数器以及其他寄存器;
②更新当前处于“执行态”的进程的进程控制块,把进程状态改为相应状态,更新其他相关域;
③把被切换进程的进程控制块移到相关状态的队列;
④选择另外一个进程开始执行,把该进程进程控制块的状态改为“执行态”;
⑤恢复被选择进程在最近一次被切换出执行态时的上下文,比如载入程序计数器以及其他处理器的值。


在Linux里面,线程就是轻量级的进程,只不过进程里面的线程共享一部分资源,如地址空间、文件句柄、信号量等。线程的调度也按照进程的调度方式进行。

Linux的线程实现是在核外进行的,核内提供的是创建进程的接口do_fork()。内核提供了两个系统调用__clone()和 fork (),最终都用不同的参数调用do_fork()核内API。当然,要想实现线程,没有核心对多进程(其实是轻量级进程)共享数据段的支持是不行的,因此,do_fork()提供了很多参数,包括CLONE_VM(共享内存空间)、CLONE_FS(共享文件系统信息)、CLONE_FILES(共享文件描述符表)、CLONE_SIGHAND(共享信号句柄表)和CLONE_PID(共享进程ID,仅对核内进程,即0号进程有效)。当使用fork系统调用时,内核调用do_fork()不使用任何共享属性,进程拥有独立的运行环境;而使用pthread_create()来创建线程时,则最终设置了所有这些属性来调用__clone(),而这些参数又全部传给核内的do_fork(),从而创建的”进程”拥有共享的运行环境,只有栈是独立的,由 __clone()传入。

你可能感兴趣的文章
spring cloud 远程仓库配置文件(yml或properties)中有server.port参数就无法访问,这是一个大坑吗?
查看>>
es 2.3.3 向es添加数据报NoNodeAvailableException[None of the configured nodes are available
查看>>
springboot 的@RequestBody JSONObject 与@RequestBody Object 的区别
查看>>
springboot pom文件设置<packaging>pom</packaging> 对于application.yml无法加载读取的问题
查看>>
springboot加载resouce下面的静态文件,templates目录的访问,以及经过controller后跳转页面问题
查看>>
shiro的通过md5+salt+hash散列进行注册操作
查看>>
springboot整合applicationContext实现上下文获取实例bean
查看>>
shiro目前问题记录
查看>>
shiro实现本地内存Ehcache实现将菜单权限进行缓存
查看>>
shiro使用redis实现将菜单权限进行缓存
查看>>
cmd窗口下执行jar包 logger.info输出乱码,out语句输出正常解决办法
查看>>
springboot启动初始化实例,后面进行使用
查看>>
shiro实现加载验证码
查看>>
springboot 搭建多模块调用以及打包执行
查看>>
shiro实现不使用密码加密器进行登录
查看>>
权限管理系统笔记
查看>>
java8 新特性 拼接字符串
查看>>
springboot中mybaits自动返回新增数据的主键
查看>>
shiro的使用freemark实现前端控制权限
查看>>
权限管理系统中功能权限&数据权限以及权限模块的实现
查看>>