Linux 进程调度

Linux 进程调度,第1张

Linux的调度策略区分实时进程和普通进程,实时进程的调度策略是SCHED_FIFO和SCHED_RR,普通的,非实时进程的调度策略是SCHED_NORMAL(SCHED_OTHER)。

实时调度策略被实时调度器管理,普通调度策略被完全公平调度器来管理。实时进程的优先级要高于普通进程(nice越小优先级越高)。

SCHED_FIFO实现了一种简单的先入先出的调度算法,它不使用时间片,但支持抢占,只有优先级更高的SCHED_FIFO或者SCHED_RR进程才能抢占它,否则它会一直执行下去,低优先级的进程不能抢占它,直到它受阻塞或自己主动释放处理器。

SCHED_RR是带有时间片的一种实时轮流调度算法,当SCHED_RR进程耗尽它的时间片时,同一优先级的其它实时进程被轮流调度,时间片只用来重新调用同一优先级的进程,低优先级的进程决不能抢占SCHED_RR任务,即使它的时间片耗尽。SCHED_RR是带时间片的SCHED_FIFO。

Linux的实时调度算法提供了一种软实时工作方式,软实时的含义是尽力调度进程,尽力使进程在它的限定时间到来前运行,但内核不保证总能满足这些进程的要求,相反,硬实时系统保证在一定的条件下,可以满足任何调度的要求。

SCHED_NORMAL使用完全公平调度算法(CFS),之前的算法直接将nice值对应时间片的长度,而在CFS中,nice值只作为进程获取处理器运行比的权重,每个进程都有一个权重,nice优先级越高,权重越大,表示应该运行更长的时间。Linux的实现中,每个进程都有一个vruntime字段,vruntime是经过量化的进程运行时间,也就是实际运行时间除以权重,所以每个量化后的vruntime应该相等,这就体现了公平性。

CFS当然也支持抢占,但与实时调度算法不同,实时调度算法是根据优先级进行抢占,CFS是根据vruntime进行抢占,vruntime小就拥有优先被运行的权利。

为了计算时间片,CFS算法需要为完美多任务中的无限小调度周期设定近似值,这个近似值也称作目标延迟,指每个可运行进程在目标延迟内都会调度一次,如果进程数量太多,则时间粒度太小,所以约定时间片的默认最小粒度是1ms。

进程可以分为I/O消耗型和处理器消耗型,这两种进程的调度策略应该不同,I/O消耗型应该更加实时,给对端的感觉是响应很快,同时它一般又不会消耗太多的处理器,因而I/O消耗型需要调度频繁。相对来说,处理器消耗型不需要特别实时,应该尽量降低它的调度频度,延长其运行时间。

参考: linux内核分析——CFS(完全公平调度算法) - 一路向北你好 - 博客园

摘要: Linux , crontab

整理crontab的使用,包括cron表达式,设置和删除任务,权限管理,查看日志

crontab命令被用来提交和管理用户的需要周期性执行的任务,与windows下的计划任务类似,当安装完成 *** 作系统后,默认会安装此服务工具,并且会自动启动crond进程,crond进程 每分钟 会定期检查是否有要执行的任务,如果有要执行的任务,则自动执行该任务

使用如下命令进行crontab服务的开启,关闭,重启,和状态查看

其中启动关闭需要root权限,所有用户可以查看状态例如

crontab通过设置cron表达式来让计算机识别如何定时调度任务,cron表达式的基本语法

前五个 * 分别代表 分钟 , 小时 , 当月第几日 , 当年第几月 , 周几 ,最后的 command 是要执行的任务语句,详情和取值范围见下图

其中* 代表所有值,比如以下代表在每年9月27日17点07分执行一个命令,不限制周几

可见crontab最小颗粒度是以 分钟级别 进行调度, 最粗级别是月 ,不限制年,因此 至少每年执行一次 。

在cron表达式中最后一个*代表周几,如果前面的*限死了日期, 且周几这个位置不为*为一个给定值 ,必定可能和周几冲突,当发生冲突时两者为 OR 的关系,即如果时间满足指定的日期,或者时间满足指定的周几,都会执行任务,举个例子

当天为9月27日,星期一,以上两条语句都会被执行,一个在当天的17点13分,一个在当天的17点17分,在/tmp/cron.txt文件中会先后写入两条记录

当周几这个位置设置为*时,无需关注冲突问题,即不对周几做限制

除此之外cron表达式支持一些特征符号配合阿拉伯数字来设置每个*,这样可以实现 或 , 每隔 , 从,到 的逻辑,分别对应三种常用符号 , , / , - ,分别解释如下

分别举几个例子

(1)17点43分和44分执行任务

(2)每隔2分钟执行任务

(3)从52分带56分一直执行任务,一共执行5次

下面来整理一波常用的cron表达式

目前已知的需要转义的是百分号 % ,尤其是在使用日期命令date做日期格式化时,否则无法在crontab中运行,因为%是crontab中的一个有意义的符号,应该这样设置带有百分号的command例如

如果不这样设置,也可以单独写在一个shell脚本中使用bash解释执行也能够正常运行

在当前用户下使用 crontab -e 来编辑添加定时任务,第一次使用会显示no crontab for用户,然后选择一种编辑方式选择输入3回车,如果首次没有设置后期也可以在终端输入 select-editor 进行设置

设置完成之后输入 crontab -e 进入,该方法进入的是当前用户的crontab界面,新起一行即可写入cron表达式进行设置,例如

其中可以使用 # 注释掉某行,则该cron表达式不生效,编辑完成后输入 wq! 进行保存关闭

一种直接的方式是使用 crontab -r ,这种方法删除该用户下所有crontab任务,其他用户的任务不影响都不会被删除,慎用,如果只是要删除其中某一条任务,则直接进入编辑删除这一行即可

任务查看使用 crontab -l 命令,该命令可以查看当前用户的crontab任务

一台linux机器上每个不同用户都有自己的crontab -e文件设置自己的定时任务,这些任务互不干涉运行在一台机器上,其中root用户可以在使用crontab -e的时候可以切换其他用户进行设定,比如

以上语句是在root下给test用户设置定时任务,如果是普通用户则报错没有-u权限

crontab -e是 用户级别 的,设置每个用户的自己的定时任务,/etc/crontab是 系统级别 的,设置系统例行的任务,/etc/crontab只有root用户有读写权限,普通用户只有读权限。

/etc/crontab相比于crontab -e多出环境变量的配置和用户名配置,由root用户统一给各个任务配置用户名,设置的环境变量对/etc/crontab下所有人任务生效,比如修改他的python的搜索路径为anaconda

这个设置只对/etc/crontab有效,对crontab -e的每个用户设置的任务无效

/etc/crontab中设置系统例行的定时任务

表示分别在小时,每天,每周,每月定时执行系统任务,其中 test -x /usr/sbin/anacron 代表条件测试文件是否可执行,如果可执行忽略||后面的命令内容,这种情况下系统走 anacron ,会读取 /etc/anacrontab 下的内容进行替代

可见机器调用 run-parts 命令和 /etc/cron.daily , /etc/cron.weekly , /etc/cron.monthly 执行定时任务

既然linux支持不同用户设置自己的任务,则在一个地方 /var/spool/cron/crontabs 看到所有用户设置的任务,这个目录只有root有权限进入

目录下有三个文件,对应三个用户,root用户对这些文件可读可写,相当于跟crontab -e可以在这设置和修改任务,直接在里面编辑等同于切到某用户执行crontab -e

ubuntu下crontab默认没有日志文件,需要在配置中设置日志文件位置,配置文件位于 /etc/rsyslog.d/50-default.conf 将这一行注释取消

设置后重启rsyslog服务

下面看下一crontab日志的格式

日志中包括 执行时间 , 主机名 , 用户 ,以及 执行的任务信息 ,任务信息包括执行的用户cron的command以及设置编辑crontab的记录,表明在 哪个时间 执行了 哪个用户 的 哪个命令语句 。

其中这两行反应了报错信息

crontab执行脚本时是不会直接错误的信息输出,而是会以邮件的形式发送到你的邮箱里,这时候就需要邮件服务器了,如果你没有安装邮件服务器,它就会报这个错: No MTA installed, discarding output ,解决方案是将错误内容写入文件,比如在command后面添加 >>/var/log/cron.log 2>&1 追加,如果是 >就是覆盖了,则crontab历史日志每次都会被删除,例如

或者就写入其他目录下

上回书说到 Linux进程的由来 和 Linux进程的创建 ,其实在同一时刻只能支持有限个进程或线程同时运行(这取决于CPU核数量,基本上一个进程对应一个CPU),在一个运行的 *** 作系统上可能运行着很多进程,如果运行的进程占据CPU的时间很长,就有可能导致其他进程饿死。为了解决这种问题, *** 作系统引入了 进程调度器 来进行进程的切换,轮流让各个进程使用CPU资源。

1)rq: 进程的运行队列( runqueue), 每个CPU对应一个 ,包含自旋锁(spinlock)、进程数量、用于公平调度的CFS信息结构、当前运行的进程描述符等。实际的进程队列用红黑树来维护(通过CFS信息结构来访问)。

2)cfs_rq: cfs调度的进程运行队列信息 ,包含红黑树的根结点、正在运行的进程指针、用于负载均衡的叶子队列等。

3)sched_entity: 把需要调度的东西抽象成调度实体 ,调度实体可以是进程、进程组、用户等。这里包含负载权重值、对应红黑树结点、 虚拟运行时vruntime 等。

4)sched_class:把 调度策略(算法)抽象成调度类 ,包含一组通用的调度 *** 作接口。接口和实现是分离,可以根据调度接口去实现不同的调度算法,使一个Linux调度程序可以有多个不同的调度策略。

1) 关闭内核抢占 ,初始化部分变量。获取当前CPU的ID号,并赋值给局部变量CPU, 使rq指向CPU对应的运行队列 。 标识当前CPU发生任务切换 ,通知RCU更新状态,如果当前CPU处于rcu_read_lock状态,当前进程将会放入rnp->blkd_tasks阻塞队列,并呈现在rnp->gp_tasks链表中。 关闭本地中断 ,获取所要保护的运行队列的自旋锁, 为查找可运行进程做准备 。

2) 检查prev的状态,更新运行队列 。如果不是可运行状态,而且在内核态没被抢占,应该从运行队列中 删除prev进程 。如果是非阻塞挂起信号,而且状态为TASK_INTER-RUPTIBLE,就把该进程的状态设置为TASK_RUNNING,并将它 插入到运行队列 。

3)task_on_rq_queued(prev) 将pre进程插入到运行队列的队尾。

4)pick_next_task 选取将要执行的next进程。

5)context_switch(rq, prev, next)进行 进程上下文切换 。

1) 该进程分配的CPU时间片用完。

2) 该进程主动放弃CPU(例如IO *** 作)。

3) 某一进程抢占CPU获得执行机会。

Linux并没有使用x86 CPU自带的任务切换机制,需要通过手工的方式实现了切换。

进程创建后在内核的数据结构为task_struct , 该结构中有掩码属性cpus_allowed,4个核的CPU可以有4位掩码,如果CPU开启超线程,有一个8位掩码,进程可以运行在掩码位设置为1的CPU上。

Linux内核API提供了两个系统调用 ,让用户可以修改和查看当前的掩码:

1) sched_setaffinity():用来修改位掩码。

2) sched_getaffinity():用来查看当前的位掩码。

在下次task被唤醒时,select_task_rq_fair根据cpu_allowed里的掩码来确定将其置于哪个CPU的运行队列,一个进程在某一时刻只能存在于一个CPU的运行队列里。

在Nginx中,使用了CPU亲和度来完成某些场景的工作:

worker_processes      4

worker_cpu_affinity 0001001001001000

上面这个配置说明了4个工作进程中的每一个和一个CPU核挂钩。如果这个内容写入Nginx的配置文件中,然后Nginx启动或者重新加载配置的时候,若worker_process是4,就会启用4个worker,然后把worker_cpu_affinity后面的4个值当作4个cpu affinity mask,分别调用ngx_setaffinity,然后就把4个worker进程分别绑定到CPU0~3上。

worker_processes      2

worker_cpu_affinity 01011010

上面这个配置则说明了两个工作进程中的每一个和2个核挂钩。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/8688196.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-19
下一篇 2023-04-19

发表评论

登录后才能评论

评论列表(0条)

保存