linux进程调度(一)-进程概述

一、什么是进程
进程是指计算机已运行的程序。程序本身只是指令、数据及其组织形式的描述。进程就是一个程序的执行实例,也就是正在执行的程序。在linux操作系统的中,进程就是一个担当分配系统资源CPU时间、内存的实体。进程控制的主要功能是对系统中的所有进程实施有效的管理,它具有创建新进程、撤销已有进程、实现进程状态之间的转换等功能。进程在运行中不断地改变其运行状态。一个进程在运行期间,不断地从一种状态转换到另一种状态,它可以多次处于就绪状态和执行状态,也可以多次处于阻塞状态。
目前,只有CPU的资源管理器能主动发起调度,而其他资源像内存,网络,显卡等资源管理器都是提供申请服务调度的,所以要必须理解我们常说的进程调度都是指调度CPU。

进程发展史的主要内容:

  1. 单道批处理系统时期:早期的计算机只支持运行单个程序,从磁带或卡片读入程序,由操作员操作控制运行。这种系统被称为单道批处理系统,没有多个程序共同运行的概念,也没有进程的概念。

  2. 多道批处理系统时期:20世纪60年代早期,随着计算机技术的发展,多道批处理操作系统应运而生,允许多个程序同时进入内存并在CPU上运行。这里的每个程序称为一个作业(Job),但是没有进程的概念。

  3. 分时操作系统时期:20世纪60年代中期,分时操作系统应运而生,引入了多用户的概念,这为进程的发展铺平了道路。在分时系统中,操作系统可以运行多个进程并为它们提供时间片,每个进程只有在它的时间片内获得CPU的控制权。这一时期UNIX系统的开发者对进程进行了深入的研究。

  4. 多任务操作系统时期:20世纪80年代,多任务操作系统开始出现,这种操作系统允许多个进程同时运行,每个进程都有自己的地址空间、寄存器和堆栈等。操作系统必须管理和控制各个进程的运行状态,这为进程的实现和管理提供了更大的灵活性和可控性。

  5. 现代操作系统时期:21世纪以来,随着计算机技术的不断发展,操作系统和进程的概念也在不断更新和完善。现代操作系统支持多核处理器、多线程和分布式系统等技术,允许进程在不同的计算机上运行,进一步增强了进程的灵活性和可扩展性。同时,在容器技术的发展下,进程管理变得更加高效和简单,如Docker等。

二、进程的生命周期
Linux操作系统是一个多任务操作系统。 系统中的各个进程可以分时复用CPU时间片,通过有效的进程调度策略,实现多任务并行执行。进程并不总是可以立即运行。有时候它必须等待来自外部信号源、不受其控制的事件,例如在文本编辑器中等待键盘输入。在事件发生之前,进程无法运行。进程可能有以下几种状态:
1.创建状态:创建新进程。
2.等待状态:进程能够运行,但没有得到许可,因为CPU分配给另一个进程。调度器可以在下一次任务切换时选择该进程。
3.运行状态:进程正在cpu中执行。
4.睡眠状态:进程正在睡眠无法运行,因为它在等待一个外部事件。
5.终止状态:进程消亡。

系统将所有进程保存在一个进程表中,无论其状态是运行、睡眠或等待。但睡眠进程会特别标记出来,调度器会知道它们无法立即运行。睡眠进程会分类到若干队列中,因此它们可在适当的时间唤醒,例如在进程等待的外部事件已经发生时。详细的进程状态间关系图如下所示:

在这里插入图片描述
Linux内核在include/linux/sched.h文件中也为进程定义了多种状态:

/* Used in tsk->state: */
#define TASK_RUNNING                    0x0000
#define TASK_INTERRUPTIBLE              0x0001
#define TASK_UNINTERRUPTIBLE            0x0002
#define __TASK_STOPPED                  0x0004
#define __TASK_TRACED                   0x0008
/* Used in tsk->exit_state: */
#define EXIT_DEAD                       0x0010
#define EXIT_ZOMBIE                     0x0020
#define EXIT_TRACE                      (EXIT_ZOMBIE | EXIT_DEAD)
/* Used in tsk->state again: */
#define TASK_PARKED                     0x0040
#define TASK_DEAD                       0x0080
#define TASK_WAKEKILL                   0x0100
#define TASK_WAKING                     0x0200
#define TASK_NOLOAD                     0x0400
#define TASK_NEW                        0x0800
#define TASK_STATE_MAX                  0x1000

其中比较常用的就是下面的5种:

  • TASK_RUNNING(可运行状态):对应上图的等待状态或者运行状态。它是指进程处于可运行的状态,或许正在运行,或许在就绪队列(本书中也称为调度队列)中等待运行。
  • TASK_INTERRUPTIBLE(可中断睡眠态):对应上图的睡眠状态。进程进入睡眠状态(被阻塞)来等待某些条件的达成或者某些资源的就位,一旦条件达成或者资源就位,内核就可以把进程的状态设置成TASK_RUNNING并将其加入就绪队列中。也有人将这个状态称为浅睡眠状态。
  • TASK_UNINTERRUPTIBLE(不可中断态):对应上图的睡眠状态。这个状态和上面的TASK_INTERRUPTIBLE状态类似,唯一不同的是,进程在睡眠等待时不受干扰,对信号不做任何反应,所以这个状态称为不可中断态。通常使用ps命令看到的被标记为D状态的进程,就是处于不可中断态的进程,不可以发送SIGKILL信号使它们终止,因为它们不响应信号。也有人把这个状态称为深度睡眠状态。
  • EXIT_ZOMBIE(僵尸态):对应上图的终止状态。进程已经消亡,但是task_struct数据结构还没有释放,这个状态叫作僵尸态。
  • __TASK_STOPPED(终止态):对应上图的终止状态。进程停止运行。task_struct中的资源包括task_struct数据已经释放完了。
    三、进程优先级
    并非所有进程都具有相同的重要性。除了大家熟悉的进程优先级之外,进程还有不同的关键度类别,以满足不同需求。首先要知道linux的进程分为以下几种,每一种的优先级都是不一样的:
    1.期限进程:优先级为-1。优先级最高。
    2.实时进程:优先级1-99,优先级数值越大便是优先级越高。
    3.普通进程:优先级为100-139,优先级数值越小便是优先级越高。可以通过nice值改变普通进程优先级。
    4.空闲进程:优先级为140。优先级最低。

四、进程表示
Linux内核涉及进程和程序的所有算法都围绕一个名为 task_struct 的数据结构建立,该结构定义在 include/linux/sched.h 中。这是系统中主要的一个结构。task_struct 包含很多成员,将进程与各个内核子系统联系起来,我们看看代码:


struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK
	/*
	 * For reasons of header soup (see current_thread_info()), this
	 * must be the first element of task_struct.
	 */
	struct thread_info		thread_info;//把thread_info从栈中抽出来,方便current宏的实现
#endif
	/* -1 unrunnable, 0 runnable, >0 stopped: */
	volatile long			state; //表示进程的状态标志

	/*
	 * This begins the randomizable portion of task_struct. Only
	 * scheduling-critical items should be added above here.
	 */
	randomized_struct_fields_start

	void				*stack;//指向内核栈的指针
	refcount_t			usage;//进程描述符使用计数,被置为2时,表示进程描述符正在被使用而且其相应的进程处于活动状态
	/* Per task flags (PF_*), defined further below: */
	unsigned int			flags;//标记,表示进程的类型
	unsigned int			ptrace;

#ifdef CONFIG_SMP
	struct llist_node		wake_entry;
	int				on_cpu;
#ifdef CONFIG_THREAD_INFO_IN_TASK
	/* Current CPU: */
	unsigned int			cpu;//当前运行的cpuid
#endif
	unsigned int			wakee_flips;
	unsigned long			wakee_flip_decay_ts;
	struct task_struct		*last_wakee;

	/*
	 * recent_used_cpu is initially set as the last CPU used by a task
	 * that wakes affine another task. Waker/wakee relationships can
	 * push tasks around a CPU where each wakeup moves to the next one.
	 * Tracking a recently used CPU allows a quick search for a recently
	 * used CPU that may be idle.
	 */
	int				recent_used_cpu;//上一次调度时使用的cpuid
	int				wake_cpu;
#endif
	int				on_rq;

	int				prio;//进程的当前优先级。一般等于normal_prio,但是当进程A占有互斥锁并且进程B在等待锁的时候,会把A进程的优先级临时提高
	int				static_prio;//普通进程的静态优先级,就是120+nice,若是限期进程或者实时进程则为0
	int				normal_prio;//普通进程的正常优先级,一般等于static_prio,若是限期进程则为-1,若是实时进程,则为99-rt_priority
	unsigned int			rt_priority;//实时进程的优先级,数值越大优先级越高,如果是限期进程或者普通进程,则为0

	const struct sched_class	*sched_class;//进程的调度类
	struct sched_entity		se;//普通进程的调度实体
	struct sched_rt_entity		rt;//实时进程的调度实体
#ifdef CONFIG_CGROUP_SCHED
	struct task_group		*sched_task_group;//指向进程所在的cgroup
#endif
	struct sched_dl_entity		dl;//deadline进程的调度实体

#ifdef CONFIG_UCLAMP_TASK
	/* Clamp values requested for a scheduling entity */
	struct uclamp_se		uclamp_req[UCLAMP_CNT];
	/* Effective clamp values used for a scheduling entity */
	struct uclamp_se		uclamp[UCLAMP_CNT];
#endif

#ifdef CONFIG_PREEMPT_NOTIFIERS
	/* List of struct preempt_notifier: */
	struct hlist_head		preempt_notifiers;
#endif

#ifdef CONFIG_BLK_DEV_IO_TRACE
	unsigned int			btrace_seq;
#endif

	unsigned int			policy;//进程调度策略
	int				nr_cpus_allowed;//该进程允许使用的cpu的数量
	const cpumask_t			*cpus_ptr;//表示该进程允许在哪个cpu上运行
	cpumask_t			cpus_mask;//表示该进程不允许在哪个cpu上运行

...

	struct sched_info		sched_info;//记录进程运行时的实时信息

	struct list_head		tasks;//进程链表
#ifdef CONFIG_SMP
	struct plist_node		pushable_tasks;
	struct rb_node			pushable_dl_tasks;
#endif

	struct mm_struct		*mm;//指向内存描述符,如果是内核进程,则为空
	struct mm_struct		*active_mm;//指向内存描述符,如果是内核进程,则指向从进程借用的内存描述符

	/* Per-thread vma caching: */
	struct vmacache			vmacache;

#ifdef SPLIT_RSS_COUNTING
	struct task_rss_stat		rss_stat;
#endif
	int				exit_state;//进程的退出状态,
	int				exit_code;//进程的终止代号
	int				exit_signal;//进程接受到的退出信号
	/* The signal sent when the parent dies: */
	int				pdeath_signal;//表示当父进程死亡时要发送的信号,然后托管给0进程
	/* JOBCTL_*, siglock protected: */
	unsigned long			jobctl;
...
	unsigned long			atomic_flags; /* Flags requiring atomic access. */

	struct restart_block		restart_block;

	pid_t				pid;//表示该进程的进程号
	pid_t				tgid;//表示该进程的线程号

#ifdef CONFIG_STACKPROTECTOR
	/* Canary value for the -fstack-protector GCC feature: */
	unsigned long			stack_canary;
#endif
	/*
	 * Pointers to the (original) parent process, youngest child, younger sibling,
	 * older sibling, respectively.  (p->father can be replaced with
	 * p->real_parent->pid)
	 */

	/* Real parent process: */
	struct task_struct __rcu	*real_parent;//指向创建进程的进程描述符(生父进程),如果生父进程不存在了,指向进程1(systemd进程)

	/* Recipient of SIGCHLD, wait4() reports: */
	struct task_struct __rcu	*parent;//指向进程的当前父进程,一般和real_parent一致,只有在调试的时候指向调试的进程

	/*
	 * Children/sibling form the list of natural children:
	 */
	struct list_head		children;//指向该进程的子进程链表
	struct list_head		sibling;//指向进程的兄弟进程
	struct task_struct		*group_leader;//一般是指向自己,如果该进程是用户创建的线程,则指向创建线程的进程

...
	/* Process credentials: */

	/* Tracer's credentials at attach: */
	const struct cred __rcu		*ptracer_cred;

	/* Objective and real subjective task credentials (COW): */
	const struct cred __rcu		*real_cred;//指向客观和真实的主观任务凭证

	/* Effective (overridable) subjective task credentials (COW): */
	const struct cred __rcu		*cred;//指向有效的主观任务凭证

#ifdef CONFIG_KEYS
	/* Cached requested key. */
	struct key			*cached_requested_key;
#endif

	/*
	 * executable name, excluding path.
	 *
	 * - normally initialized setup_new_exec()
	 * - access it with [gs]et_task_comm()
	 * - lock it with task_lock()
	 */
	char				comm[TASK_COMM_LEN];//进程的名称

	struct nameidata		*nameidata;

#ifdef CONFIG_SYSVIPC
	struct sysv_sem			sysvsem;//用于信号量
	struct sysv_shm			sysvshm;//用于共享内存
#endif
#ifdef CONFIG_DETECT_HUNG_TASK
	unsigned long			last_switch_count;
	unsigned long			last_switch_time;
#endif
	/* Filesystem information: */
	struct fs_struct		*fs;//指向进程的当前工作目录

	/* Open file information: */
	struct files_struct		*files;//打开的文件

	/* Namespaces: */
	struct nsproxy			*nsproxy;//命名空间

	/* Signal handlers:主要用于信号处理 */
	struct signal_struct		*signal;//指向进程的信号描述符
	struct sighand_struct		*sighand;//指向进程的信号处理程序描述符
	sigset_t			blocked;//表示被阻塞信号的掩码
	sigset_t			real_blocked;//临时掩码
	/* Restored if set_restore_sigmask() was used: */
	sigset_t			saved_sigmask;//使用了set_restore_sigmask()则恢复的掩码
	struct sigpending		pending;//存放私有挂起信号的数据结构
	unsigned long			sas_ss_sp;//信号处理程序备用堆栈的地址
	size_t				sas_ss_size;//堆栈的大小
	unsigned int			sas_ss_flags;//堆栈的标志位

	struct callback_head		*task_works;
...
#ifdef CONFIG_LOCKDEP
# define MAX_LOCK_DEPTH			48UL
	u64				curr_chain_key;
	int				lockdep_depth;//表示获取大内核锁的次数
	unsigned int			lockdep_recursion;
	struct held_lock		held_locks[MAX_LOCK_DEPTH];
#endif
...
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/184591.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

接口测试学习路线

接口测试分为两种: 测试外部接口:系统和外部系统之间的接口 如:电商网站:支付宝支付 测试内部接口:系统内部的模块之间的联调,或者子系统之间的数据交互 测试重点:测试接口参数传递的正确性&…

PLC通过RS232转PROFINET与电子分析天平秤通讯案例

本案例是通过用兴达易控的XD-PNR200型RS232转Profinet网关连接电子分析天平秤与PLC通讯的配置案例,用到设备为西门子S7-1200PLC,RS232转Profinet网关,电子分析天平秤。 打开博图,添加PLC;本案例使用的是1200PLC。 添加…

精益制造中的周转箱和工具柜优势

制造业(Manufacturing industry)是指机械工业时代利用某种资源(物料、能源、设备、工具、资金、技术、信息和人力等),按照市场要求,通过制造过程,转化为可供人们使用和利用的大型工具、工业品与…

可以ping通IP但是无法远程连接-‘telnet‘ 不是内部或外部命令,也不是可运行的程序或批处理文件

起因 一开始远程连接IP,报错,怀疑是自己网络原因,但是同事依旧无法连接 怀疑是自己防火墙的原因,查看关闭依旧无法连接 问题 两个地址可以ping通排除防火墙缘故 怀疑端口,测试端口 然 解决方案 winR 输入control…

前后端性能优化实践(含Java代码部分、数据库部分、React前端部分)

最近的一个大屏报表统计的接口查询速度很慢,耗时近一分钟左右,数据量级只是700万左右,但很慢,最后优化到4秒左右,客户还能接受,但其实还可以在优化,先这样吧,简单记录下。这次主要优…

【LeetCode:2824. 统计和小于目标的下标对数目 | 模拟+二分】

🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…

分布式篇---第四篇

系列文章目录 文章目录 系列文章目录前言一、分布式ID生成有几种方案?二、幂等解决方法有哪些?三、常见负载均衡算法有哪些?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给…

【机器学习 | 白噪声检验】检验模型学习成果 检验平稳性最佳实践,确定不来看看?

🤵‍♂️ 个人主页: AI_magician 📡主页地址: 作者简介:CSDN内容合伙人,全栈领域优质创作者。 👨‍💻景愿:旨在于能和更多的热爱计算机的伙伴一起成长!!&…

java堆文件排查

技术主题 在之前的开发的一个项目中,因为程序的一个bug,导致一些引用的对象一直没有回收,从而导致堆内存一直在增大,老年代一直在增大,老年代进行堆积,后来的排查思路是通过dump堆的文件,然后对…

上门预约小程序开发优势

想要放松身心,享受按摩的舒适感?那就需要一个专业的按摩师来上门服务。我们开发的预约按摩小程序app系统,汇聚各类上门按摩服务,包括推拿SPA、小儿推拿、中医等,为您提供高价值、高标准的养生健康体验。24小时随时提供…

「树形」样式,数据关联超便捷丨三叠云

树形样式 路径 表单设计 >> 字段属性 功能简介 「表单关联」的数据列表样式支持「树形」样式功能,关联数据选择时通过「树形」的列表方式进行数据选择,提高生产效率。 使用场景: 可以通过树形列表样式展示部门、子部门、成员的树形…

鸿蒙系统使用hdc_std.exe使用身份证读卡器等外设USB获得权限方法

hdc_std.exe是OpenHarmony 的命令行工具,由于使用的开源鸿蒙开发板上面没有文件管理器,所以无法通过U盘等方式进行安装.hap应用。 下面是使用hdc_std.exe安装身份证读卡器的步骤: 1、hdc_std.exe放桌面,然后WINR,打开…

网站定制开发有哪些分类?|企业软件app小程序定制

网站定制开发有哪些分类?|企业软件app小程序定制 网站定制开发是指根据客户需求,为其量身定制设计和开发的网站服务。目前,网站定制开发主要分为以下几个分类: 1. 静态网站定制开发:静态网站是由HTML、CSS和JavaScrip…

表格视图,支持数据直接编辑丨三叠云

表格视图 路径 表单设置 >> 视图设置 功能简介 新增用户可以直接表格视图中直接点击编辑数据。管理员开启「列表编辑」后,用户无需再点击进入数据详情,可直接在列表中编辑数据,节约用户修改数据的时间。 使用场景: 通…

CMS指纹识别方式

一、手工识别 1.robots.txt文件 robots.txt文件我们写过爬虫的就知道,这个文件是告诉我们哪些目录是禁止爬取的。但是大部分的时候我们都能通过robots.txt文件来判断出cms的类型 如: 从wp路径可以看出这个是WordPress的cms 这个就比较明显了直接告诉我们是PageAdmin cms 也…

详解Java中的异常体系机构(throw,throws,try catch,finally)

目录 一.异常的概念 二.异常的体系结构 三.异常的处理 异常处理思路 LBYL:Look Before You Leap EAFP: Its Easier to Ask Forgiveness than Permission 异常抛出throw 异常的捕获 提醒声明throws try-catch捕获处理 finally的作用 四.自定义异常类 一.异…

累计定点160+车型,商汤绝影凭什么领跑规模化量产?

2023广州车展火热进行,智能化技术加速“内卷”。 商汤绝影多款合作量产车型亮相2023广州车展,包括昊铂 GT、传祺ES9、E8系列和本田雅阁、捷途旅行者、极氪X等,全方位呈现在智能驾驶和智能座舱领域的最新成果,以AI“新科技”&…

RTL8762x芯片避坑总结之1——用GPIO模拟I2C

1. 使用GPIO模拟I2C: 1.1 按常规逻辑 在输出第1个数据,切换为输入读取ACK后,切回输出无效,不能输出数据: 1.2 在切换输入输出前,需要对Pad重新进行配置 重新配置后,输入输出切换成功&#xf…

Android开发从0开始(服务)

Android后台运行的解决方案,不需要交互,长期运行。 服务基础框架: public class MyService extends Service { public MyService() { } Override public IBinder onBind(Intent intent) { //activity与service交互(需要继…

多actor实体组合并统一应用变换_vtkAssembly

开发环境: Windows 11 家庭中文版Microsoft Visual Studio Community 2019VTK-9.3.0.rc0vtk-example参考代码 demo解决问题:创建了一个球体和立方体的三维可视化,将它们组合成一个装配体,应用变换,调整不透明度&#…