Linux IO Scheduler--Noop
linux系统io故障排查文档
linux系统io故障排查文档Linux系统IO故障排查文档一、引言在Linux系统中,IO故障是常见的问题之一。
当系统出现IO故障时,可能会导致应用程序响应缓慢、数据丢失或系统崩溃等严重后果。
因此,及时排查和解决IO故障是维护系统稳定性和性能的关键。
二、排查步骤1. 观察系统行为我们需要观察系统的行为,包括应用程序的响应时间、磁盘IO负载、系统日志等信息。
通过观察系统行为,我们可以初步判断是否存在IO故障。
2. 检查磁盘状态接下来,我们需要检查磁盘的状态。
可以使用命令"df -h"查看磁盘空间使用情况,以及"fdisk -l"查看磁盘分区信息。
如果磁盘空间已满或分区有问题,可能会导致IO故障。
3. 检查磁盘驱动程序磁盘驱动程序是控制磁盘IO的关键组件。
我们需要检查磁盘驱动程序是否正常工作。
可以使用命令"lsmod | grep <driver>"查看磁盘驱动程序是否加载。
如果磁盘驱动程序未加载或存在异常,可能会导致IO故障。
4. 检查文件系统文件系统是管理磁盘上数据的重要组件。
我们需要检查文件系统是否存在问题。
可以使用命令"fsck"检查文件系统并修复错误。
如果文件系统损坏或存在错误,可能会导致IO故障。
5. 检查磁盘IO性能磁盘IO性能是评估系统IO是否正常的重要指标。
我们可以使用命令"iostat"或"iotop"来监控磁盘的IO性能。
如果磁盘IO性能异常低下或存在高负载,可能会导致IO故障。
6. 检查磁盘设备我们需要检查磁盘设备本身是否存在问题。
可以使用命令"smartctl"检查磁盘设备的健康状态。
如果磁盘设备存在硬件故障或损坏,可能会导致IO故障。
三、解决方案针对不同的IO故障,我们可以采取相应的解决方案。
例如,如果是磁盘空间不足,可以清理不必要的文件或扩展磁盘容量;如果是磁盘驱动程序异常,可以重新加载驱动程序或更新驱动程序版本;如果是文件系统损坏,可以使用fsck命令修复文件系统;如果是磁盘设备故障,可能需要更换磁盘设备。
linux sched_setscheduler函数解析
linux sched_setscheduler函数解析sched_setscheduler函数是一个Linux系统调用函数,用于修改进程的调度策略和优先级。
函数原型为:```cint sched_setscheduler(pid_t pid, int policy, const structsched_param *param);```参数说明:- pid:要修改调度策略和优先级的进程ID。
如果pid为0,则表示修改当前进程。
- policy:要设置的调度策略。
可以取以下值:- SCHED_OTHER:普通进程调度策略,即默认策略。
这是一个非实时调度策略,由时间片轮转算法控制。
- SCHED_FIFO:先进先出调度策略。
使用FIFO调度策略的进程优先级比其他普通进程高。
- SCHED_RR:轮转调度策略。
与FIFO策略类似,但进程会在使用完时间片后轮转到等待队列的末尾。
- SCHED_BATCH:批处理调度策略。
适合批处理作业,将进程聚集在一起批量执行。
- SCHED_IDLE:空闲调度策略。
只有在没有其他优先级较高的进程运行时,才会执行该进程。
- SCHED_DEADLINE:截止时间调度策略。
用于实时系统,根据任务的截止时间进行调度。
- param:一个指向sched_param结构体的指针,用于设置进程的优先级和其他调度参数。
sched_param结构体包含一个int类型的成员sched_priority,表示进程的优先级。
函数返回值为0表示成功,返回-1表示失败并设置errno。
sched_setscheduler函数用于修改进程的调度策略和优先级。
在修改调度策略之前,需要获得相应的权限。
调用该函数后,进程会立即按照新的调度策略和优先级进行调度。
注意事项:- 一些调度策略(如SCHED_FIFO和SCHED_RR)需要root权限,因此需要以root用户身份运行程序或具有相应的权限。
- 修改调度策略和优先级可能会影响系统的整体性能和稳定性,需要谨慎使用。
linux的任务调度机制
linux的任务调度机制摘要:1.Linux任务调度机制简介2.Linux任务调度器的工作原理3.调度策略和队列4.进程优先级和调度算法5.总结正文:Linux任务调度机制是操作系统中负责分配处理器时间片给各个进程的核心组件。
它依据特定的策略和算法,确保公平、高效地管理进程的执行。
本文将详细介绍Linux任务调度机制的各个方面。
1.Linux任务调度机制简介Linux采用基于优先级的抢占式调度算法,以确保处理器资源得到充分利用。
调度器通过周期性地在就绪队列中选择一个或多个进程,将它们分配给处理器执行。
调度器主要依据进程的优先级和当前的负载情况来决定哪个进程获得处理器资源。
2.Linux任务调度器的工作原理Linux任务调度器的核心组件是调度实体(scheduler entity),它包括进程队列、调度策略和调度算法。
调度实体根据系统的当前状态,按照策略和算法来选择下一个要执行的进程。
调度实体的工作过程分为以下几个步骤:- 进程创建:当一个新进程被创建时,调度器会为其分配一个初始优先级,并将其加入就绪队列。
- 进程执行:调度器从就绪队列中选择一个或多个进程,将它们分配给处理器执行。
执行过程中,进程可能因时间片用完或被阻塞而放弃处理器资源。
- 进程更新:调度器周期性地更新进程的优先级和状态,以反映其当前的执行情况。
- 进程退出:当进程完成执行或被终止时,调度器会将其从进程队列中移除。
3.调度策略和队列Linux调度器支持多种调度策略,如FIFO(先进先出)、SJF(短作业优先)和RR(时间片轮转)。
调度策略决定了进程在队列中的排列顺序,从而影响了调度器选择下一个进程的依据。
Linux中有两个主要的进程队列:就绪队列和运行队列。
就绪队列包含了所有等待处理器资源的进程,而运行队列则存放了当前正在执行的进程。
调度器会根据策略从就绪队列中选择一个或多个进程,将其加入运行队列。
4.进程优先级和调度算法Linux中的进程优先级是一个0-139的整数,优先级数值越低,进程获得处理器资源的机会越高。
NUMA架构的优缺点
NUMA架构的优缺点numa把⼀台计算机分成多个节点(node),每个节点内部拥有多个CPU,节点内部使⽤共有的内存控制器,节点之间是通过互联模块进⾏连接和信息交互。
因此节点的所有内存对于本节点所有的CPU都是等同的,对于其他节点中的所有CPU都不同。
因此每个CPU可以访问整个系统内存,但是访问本地节点的内存速度最快(不经过互联模块),访问⾮本地节点的内存速度较慢(需要经过互联模块),即CPU访问内存的速度与节点的距离有关,该距离成为Node Distance。
查看当前numa的节点情况:numactl --hardware节点之间的距离(Node Distance)指从节点1上访问节点0上的内存需要付出的代价的⼀种表现形式。
Numa内存分配策略有⼀下四种:缺省default:总是在本地节点分配(当前进程运⾏的节点上)。
绑定bind:强制分配到指定节点上。
交叉interleavel:在所有节点或者指定节点上交叉分配内存。
优先preferred:在指定节点上分配,失败则在其他节点上分配。
查看当前系统numa策略:numactl --show因为numa默认的内存分配策略是优先在进程所在CPU的本地内存中分配,会导致CPU节点之间内存分配不均衡,当某个CPU节点内存不⾜时,会导致swap产⽣,⽽不是从远程节点分配内存,这就是swap insanity现象。
MySQL服务器为什么需要关闭numa?MySQL是单进程多线程架构数据库,当numa采⽤默认内存分配策略时,MySQL进程会被并且仅仅会被分配到numa的⼀个节点上去。
假设这个节点的本地内存为10GB,⽽MySQL配置20GB内存,超出节点本地内存部分(20GB-10GB)Linux会使⽤swap⽽不是使⽤其他节点的物理内存。
在这种情况下,能观察到虽然系统总的可⽤内存还未⽤完,但是MySQL进程已经开始在使⽤swap了。
如果单机只运⾏⼀个MySQL实例,可以选择关闭numa,关闭nuam有两种⽅法:1.硬件层,在BIOS中设置关闭;2.OS内核,启动时设置numa=off。
深入解读Linux进程调度Schedule【转】
深⼊解读Linux进程调度Schedule【转】调度系统是现代操作系统⾮常核⼼的基础⼦系统之⼀,尤其在多任务并⾏操作系统(Multitasking OS)上,系统可能运⾏于单核或者多核CPU上,进程可能处于运⾏状态或者在内存中可运⾏等待状态。
如何实现多任务同时使⽤资源并且提供给⽤户及时的响应实现实时交互以及提供⾼流量并发等对现代操作系统的设计实现带来了巨⼤挑战,⽽Linux调度⼦系统的设计同样需要实现这些看似⽭盾的要求,适应不同的使⽤场景。
我们看到Linux是⼀个复杂的现在操作系统,各个⼦系统之间相互合作才能完成⾼效的任务。
本⽂从围绕调度⼦系统,介绍了调度⼦系统核⼼的概念,并且将其与Linux各个相关组件的关系进⾏探讨,尤其是与调度⼦系统息息相关的中断(softirq和irq)⼦系统以及定时器Timer,深⼊⽽全⾯地展⽰了调度相关的各个概念以及相互联系。
由于笔者最近在调试PowerPC相关的芯⽚,因此相关的介绍会以此为例提取相关的内核源代码进⾏解读展⽰。
涉及的代码为Linux-4.4稳定发布版本,读者可以查看源码进⾏对照。
1. 相关概念要理解调度⼦系统,⾸先需要总体介绍调度的流程,对系统有⼀个⾼屋建瓴的认识之后,再在整体流程中对各个节点分别深⼊分析,从⽽掌握丰富⽽饱满的细节。
在系统启动早期,会注册硬件中断,时钟中断是硬件中断中⾮常重要的⼀种,调度过程中需要不断地刷新进程的状态以及设置调度标志已决定是否抢占进程的执⾏进⾏调度。
时钟中断就是周期性地完成此项⼯作。
这⾥⼜引出另外⼀个现代OS的调度设计思想即抢占(preempt),⽽与其对应的概念则为⾮抢占或者合作(cooperate),后⾯会给出两者的详细区别。
时钟中断属于硬件中断,Linux系统不⽀持中断嵌套,所以在中断发⽣时⼜会禁⽌本地中断(local_irq_disable),⽽为了尽快相应其他可能的硬件事件,必须要尽快完成处理并开启中断,因此引出了中断下半部,也就是softirq的概念。
linux磁盘调度策略
linux磁盘调度策略Linux磁盘调度策略磁盘调度策略是操作系统中的一个重要组成部分,它决定了磁盘上的数据访问顺序。
Linux操作系统提供了多种磁盘调度策略,以满足不同场景下的需求。
本文将介绍Linux中常见的磁盘调度策略,包括CFQ、Deadline和NOOP。
1. CFQ磁盘调度策略CFQ(Completely Fair Queuing)是Linux内核默认的磁盘调度策略。
它采用了时间片轮转的方式,为每个进程提供公平的磁盘访问机会。
CFQ会将磁盘请求按照优先级进行分类,并为每个进程分配一定数量的时间片进行磁盘访问。
优先级高的进程会获得更多的时间片,从而获得更快的磁盘响应速度。
CFQ适用于大多数常规应用场景,能够保证公平性和稳定性。
2. Deadline磁盘调度策略Deadline磁盘调度策略以最小化磁盘请求的响应时间为目标。
它将磁盘请求分为两类:实时请求和普通请求。
实时请求具有更高的优先级,需要在规定时间内完成。
而普通请求则在规定时间内按照先进先出的原则进行处理。
Deadline通过维护两个队列,分别处理实时请求和普通请求,以保证实时请求的响应时间。
3. NOOP磁盘调度策略NOOP磁盘调度策略是一种简单的FIFO(先进先出)调度策略。
它不对磁盘请求进行排序,直接按照请求的先后顺序进行处理。
NOOP适用于低负载的系统,可以减少磁盘调度的开销,提高系统的响应速度。
4. 其他磁盘调度策略除了CFQ、Deadline和NOOP,Linux还提供了其他一些磁盘调度策略,如Anticipatory、AS(Anticipatory Scheduler)和BFQ (Budget Fair Queuing)等。
这些策略在特定场景下有着不同的表现和优势。
例如,Anticipatory策略在读取大文件时表现较好,而AS策略则适用于多媒体和数据库等需要低延迟的应用。
5. 磁盘调度策略的选择选择合适的磁盘调度策略需要根据具体的应用场景和需求来决定。
Linux命令技巧优化磁盘IO性能和文件系统速度
Linux命令技巧优化磁盘IO性能和文件系统速度在Linux系统中,优化磁盘IO性能和文件系统速度是提高系统运行效率和响应速度的关键。
本文将介绍一些常用的Linux命令技巧,帮助您进行磁盘IO性能和文件系统速度的优化。
一、优化磁盘IO性能1. 使用iostat命令监测磁盘IO状况iostat命令可以用来监测系统的磁盘IO情况,通过观察硬盘的平均响应时间、平均等待时间等参数,可以判断磁盘IO是否存在性能瓶颈。
使用iostat命令的示例如下:```shelliostat -d -x 1 5```其中,“-d”参数表示只显示设备信息,“-x”参数表示显示扩展信息,“1”表示每秒刷新一次,“5”表示总共刷新5次。
2. 调整读写策略Linux系统对于磁盘IO有三种基本的读写策略,分别为同步IO、异步IO和直接IO。
同步IO适用于对数据完整性要求较高的场景,但会降低性能;异步IO可以提高性能,但可能会导致数据丢失;直接IO 可以绕过文件缓存,减少IO延迟,提高性能。
我们可以根据具体需求,选择适合的IO策略。
在进行IO操作时,可以使用以下命令设置IO策略:```shellecho "策略" > /sys/block/设备名/queue/scheduler```其中,“策略”可以是以下几种:cfq、noop、deadline。
3. 提高文件系统的读写性能文件系统的读写性能也对系统的整体性能有影响。
可以通过以下方法来提高文件系统的读写性能:(1)选择适当的文件系统类型:不同的文件系统类型对于读写性能有所差异。
较新的文件系统(如ext4、xfs)通常性能更好。
(2)调整文件系统的挂载选项:可以通过修改/etc/fstab文件来调整文件系统的挂载选项,包括atime、noatime、barrier等。
(3)使用延迟写入:通过将文件系统挂载为“writeback”模式,可以将数据写入缓存,减少磁盘IO的频繁操作,提高性能。
linux系统io故障排查文档
linux系统io故障排查文档全文共四篇示例,供读者参考第一篇示例:Linux系统的IO故障排查是系统管理员在日常工作中经常遇到的问题之一。
当服务器出现IO故障时,会严重影响系统的稳定性和性能,甚至导致系统崩溃。
及时发现并解决IO故障至关重要。
本文将从IO故障的常见原因、排查方法和解决方案等方面进行详细介绍,希望能够帮助读者更好地处理Linux系统中的IO故障。
一、IO故障的常见原因1. 硬件故障:硬件故障是导致IO故障的主要原因之一。
硬盘、网卡、数据线等硬件设备出现故障或损坏会导致IO操作异常,例如读写速度变慢、文件丢失等问题。
2. 系统配置错误:系统配置不当也会引发IO故障。
比如磁盘分区设置错误、驱动程序版本不匹配、缓冲区设置不当等都可能引发IO故障。
3. 软件故障:软件程序的bug或者不稳定版本也可能导致IO故障。
比如IO操作频繁、文件读写不规范等都可能引发IO故障。
4. 网络故障:网络连接不稳定或者网络带宽不足也会导致IO故障。
特别是在云计算环境下,网络故障可能会更加严重。
二、IO故障的排查方法1. 查看系统日志:系统日志是排查IO故障的重要参考信息。
可以通过查看/var/log/messages文件或者dmesg命令获取系统日志信息,从中找到IO故障的线索。
2. 使用IO性能工具:Linux系统提供了一些IO性能工具,比如iostat、iotop等,可以用来查看系统的IO性能指标,帮助发现IO故障的原因。
3. 检查硬件设备:如果怀疑是硬件设备故障引起的IO故障,可以通过检查硬盘、网卡、数据线等硬件设备的状态和连接情况来确认问题所在。
4. 测试软件程序:如果怀疑是软件程序引起的IO故障,可以通过测试软件程序的读写性能、查看程序日志等方式来定位问题。
5. 检查网络连接:如果怀疑是网络故障引起的IO故障,可以通过ping命令、traceroute命令等工具来检查网络连接是否正常。
1. 修复硬件故障:如果确定是硬件故障引起的IO故障,需要及时更换或修复受损的硬件设备,确保系统正常运行。
linux常见io调度算法
linux常见io调度算法在Linux操作系统中,IO调度算法被用来优化磁盘IO的性能和效率。
当多个进程同时发起IO请求时,IO调度算法决定了这些IO请求的处理顺序,以提高系统的整体性能。
常见的Linux IO调度算法包括:1. Completely Fair Queuing (CFQ):CFQ是Linux内核默认的IO调度算法。
它将IO请求放入不同的队列中,并根据进程的优先级和历史IO行为,以公平的方式分配磁盘IO资源。
它相对于其他调度算法来说,更适用于多任务环境,能够保证每个进程都能够获得公平的IO延迟。
2. Deadline:Deadline算法将IO请求放入读队列和写队列,并根据截止期限来决定哪个请求先被处理。
读请求的截止期限相对较短,写请求的截止期限相对较长。
这种算法能够确保IO 请求在一定时间内得到满足,同时提供更好的响应时间和吞吐量。
3. Noop:Noop算法是一种简单的IO调度算法,它不进行任何调度,只是按照请求的顺序进行处理。
这种算法适用于那些不需要复杂调度的高性能存储系统,如固态硬盘(Solid State Drive, SSD)。
4. Anticipatory:Anticipatory算法通过预测进程的IO行为来进行调度。
当一个请求到达时,它会估计下一个请求的位置,并尝试将磁盘头移动到正确的位置,以减少寻道时间。
这种算法适用于那些读写访问比较复杂的应用,如数据库系统。
5. Budget Fair Queuing (BFQ):BFQ是一种较新的IO调度算法,它在CFQ的基础上进行了改进。
它通过调度进程级IO请求而不是单个进程的请求,以实现更好的公平性和延迟保证。
BFQ 算法与CFQ算法相比,能够更好地应对高吞吐量和低延迟要求。
选择适合的IO调度算法需要考虑系统的具体需求和硬件环境。
一般来说,CFQ算法适用于大多数使用场景,但对于高吞吐量和低延迟要求的应用,可以考虑使用Deadline或BFQ算法。
Mysql怎么运行环境优化_Mysql运行环境有哪些优化
Mysql怎么运行环境优化_Mysql运行环境有哪些优化你知道Mysql是怎么运行的吗?知道运行环境怎么样吗?下面由店铺为大家整理的Mysql运行环境优化,希望大家喜欢!Mysql运行环境优化一、修改Linux默认的IO调度算法.linux默认的IO调度算法为cfq,需要修改为dealine,如果是SSD 或者PCIe-SSD设备,需要修改为noop,可以使用下面两种修改方式。
1、在线动态修改,重启失效。
代码如下:echo “deadline” > /sys/block/sda/queue/schedulertips:这里的sda代表你需要修改的硬盘,根据你实际情况修改。
2、修改/etc/grub.conf,永久生效。
修改/etc/grub.conf配置文件,在kernel那行增加一个配置,例如:代码如下:kernel /vmlinuz-2.6.32-279.el6.x86_64 ro root=UUID=e01d6bb4-bd74-404f-855a-0f700fad4de0rd_NO_LUKS rd_NO_LVM LANG=en_US.UTF-8 rd_NO_MD SYSFONT=latarcyrheb-sun16 crashkernel=auto KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM elevator=deadline rhgb quiet主要关注elevator这个参数,设置内核的话需要重启系统才能生效。
最后可以通过cat /sys/block/sda/queue/scheduler 观察一下,修改前和修改后的区别。
二、扩大文件描述符这个是经常修改的参数,高并发的程序都会修改。
1、动态修改,重启失效,只能使用root,并且当前session有效。
代码如下:ulimit -n 512002、修改配置文件,永久生效。
在/etc/security/limits.conf配置文件中增加一行代码如下:* hard nofile 51200扩大可开启进程数 nprocess /etc/security/limits.conf在/etc/security/limits.conf配置文件中增加一行代码如下:* hard nproc 51200最后修改/etc/pam.d/login文件添加代码如下:session required /lib64/security/pam_limits.so重启系统以后使用 ulimit -a 命令查看是否生效。
IO_SCHEDULER
I/O 调度算法I/O 调度算法再各个进程竞争磁盘I/O的时候担当了裁判的角色。
他要求请求的次序和时机做最优化的处理,以求得尽可能最好的整体I/O性能。
在linux下面列出4种调度算法CFQ (Completely Fair Queuing 完全公平的排队)(elevator=cfq):这是默认算法,对于通用服务器来说通常是最好的选择。
它试图均匀地分布对I/O带宽的访问。
在多媒体应用, 总能保证audio、video及时从磁盘读取数据。
但对于其他各类应用表现也很好。
每个进程一个queue,每个queue按照上述规则进行merge和sort。
进程之间round robin调度,每次执行一个进程的4个请求。
可以调queued 和quantum 来优化Deadline (elevator=deadline):这个算法试图把每次请求的延迟降至最低。
该算法重排了请求的顺序来提高性能。
可以调队列的过期的读写过程,如read_expire 和write_expire 二个参数来控制多久内一定要读到数据,超时就放弃排序。
比较合适小文件。
还可以使用打开front_merges 来进行合并邻近文件。
NOOP (elevator=noop):I/O请求被分配到队列,调度由硬件进行,只有当CPU时钟频率比较有限时进行。
Noop对于I/O不那么操心,对所有的I/O请求都用FIFO队列形式处理,默认认为I/O不会存在性能问题。
这也使得CPU也不用那么操心。
当然对于复杂一点的应用类型使用这个调度器,用户自己就会非常操心。
Noop调度算法指的是当请求被存储到队列并交由I/O子系统处理时由磁盘硬件对其进行优化。
该算法一般只对一些特定的硬件(例如RAM disk和TCQ disk等)。
现代磁盘控制器都具备通过tagged command queuing进行优化的功能。
Tagged command queuing(TCQ)可以通过由磁盘控制器对I/O请求进行重新排序来减少磁头的动作。
IO调度算法
在Linux 2.6中,有四种关于IO的调度算法,下面综合小结一下:1) NOOPNOOP算法的全写为No Operation。
该算法实现了最最简单的FIFO队列,所有IO请求大致按照先来后到的顺序进行操作。
之所以说“大致”,原因是NOOP在FIFO的基础上还做了相邻IO请求的合并,并不是完完全全按照先进先出的规则满足IO请求。
NOOP假定I/O请求由驱动程序或者设备做了优化或者重排了顺序(就像一个智能控制器完成的工作那样)。
在有些SAN环境下,这个选择可能是最好选择。
Noop 对于 IO 不那么操心,对所有的 IO请求都用 FIFO 队列形式处理,默认认为 IO 不会存在性能问题。
这也使得 CPU 也不用那么操心。
当然,对于复杂一点的应用类型,使用这个调度器,用户自己就会非常操心。
2) Deadline schedulerDEADLINE在CFQ的基础上,解决了IO请求饿死的极端情况。
除了CFQ本身具有的IO排序队列之外,DEADLINE额外分别为读IO和写IO提供了FIFO队列。
读FIFO队列的最大等待时间为500ms,写FIFO队列的最大等待时间为5s。
FIFO队列内的IO 请求优先级要比CFQ队列中的高,,而读FIFO队列的优先级又比写FIFO队列的优先级高。
优先级可以表示如下:FIFO(Read) > FIFO(Write) > CFQdeadline 算法保证对于既定的 IO 请求以最小的延迟时间,从这一点理解,对于 DSS 应用应该会是很适合的。
3) Anticipatory schedulerCFQ和DEADLINE考虑的焦点在于满足零散IO请求上。
对于连续的IO请求,比如顺序读,并没有做优化。
为了满足随机IO和顺序IO混合的场景,Linux还支持ANTICIPATORY调度算法。
ANTICIPATORY的在DEADLINE的基础上,为每个读IO 都设置了6ms 的等待时间窗口。
Linux系统定时任务脚本使用Shell脚本实现对Linux系统的定时任务调度和执行
Linux系统定时任务脚本使用Shell脚本实现对Linux系统的定时任务调度和执行在Linux系统中,我们经常需要执行定时任务来完成一些自动化的工作,例如定期备份数据、定时清理临时文件等。
而在Linux系统中,我们可以使用Shell脚本来实现对定时任务的灵活控制和调度。
本文将介绍如何使用Shell脚本在Linux系统中实现定时任务的调度和执行。
一、Shell脚本基础在开始介绍定时任务的使用之前,我们首先需要了解一些Shell脚本的基础知识。
Shell脚本是一种以Shell(命令行解释器)为解释器的脚本语言,用于批处理任务和自动化操作。
在Linux系统中,我们可以使用各种Shell脚本编写工具,例如Bash、Sh、Csh等。
Shell脚本主要由命令、变量、条件判断、循环等组成。
我们可以使用Shell脚本来执行各种操作,例如创建文件、修改文件权限、运行程序等。
而对于定时任务,我们可以使用Shell脚本来编写一段特定的代码,然后在指定的时间点进行执行。
二、定时任务的调度在Linux系统中,我们可以通过使用crontab命令来实现定时任务的调度。
crontab是一个用于设置定时任务的命令,它可以让我们方便地进行任务的调度和执行。
1. 编写定时任务脚本首先,我们需要编写一个定时任务脚本。
这个脚本可以包含我们想要执行的一系列任务,例如备份数据、清理临时文件等。
下面是一个简单的定时任务脚本示例:```shell#!/bin/bash# 备份数据cp /data/*.txt /backup# 清理临时文件rm -rf /tmp/*```在这个脚本中,我们使用cp命令来将`/data`目录下的所有txt文件复制到`/backup`目录中,然后使用rm命令来清空`/tmp`目录下的所有文件。
2. 编辑crontab任务表接下来,我们需要编辑crontab任务表,将我们编写的定时任务脚本添加到任务列表中。
我们可以使用以下命令来编辑crontab任务表:```shellcrontab -e```编辑任务表时,我们可以按照一定的格式来设置定时任务的执行时间和任务命令。
linux io写入过慢 排查思路
linux io写入过慢排查思路当在Linux系统中遇到IO写入过慢的情况时,可以采用以下一些思路来进行排查:检查磁盘性能:使用工具如iostat或iotop来监视磁盘的读写性能,以确定是否存在磁盘瓶颈。
检查磁盘的读写速度、IOPS(每秒的IO操作数)等参数。
查看系统负载:使用top或htop查看系统的负载情况,确保系统资源(CPU、内存)没有明显的瓶颈,以防影响IO性能。
检查文件系统:确保文件系统没有出现异常或损坏。
使用df -h检查磁盘空间的使用情况,使用fsck工具检查并修复文件系统。
调整文件系统挂载参数:在挂载文件系统时,可以使用不同的挂载参数来优化IO性能。
例如,使用noatime参数可以减少对文件访问时间的记录,提高性能。
检查IO调度器:Linux系统使用IO调度器来决定磁盘上的IO请求顺序。
可以考虑尝试不同的IO调度算法,如deadline或cfq,以找到最适合系统特性的调度器。
查看IO等待:使用iowait参数查看系统中IO等待的情况。
如果iowait占用过高,可能是IO瓶颈的一个指标。
使用strace工具:使用strace跟踪程序的系统调用,查看IO写入的具体过程,以确定是否有异常或慢速的系统调用。
检查文件操作方式:如果是在程序中进行IO操作,确保文件的打开、写入等操作使用了合理的缓冲机制。
使用O_DIRECT标志可以绕过文件缓存,直接进行磁盘IO。
查看硬件问题:如果硬件问题导致IO性能下降,可以通过检查硬盘状态、RAID 配置、电缆连接等来排查问题。
以上只是一些常见的排查思路,具体的排查步骤需要根据具体情况进行调整。
如果问题仍然存在,可能需要更深入的分析,例如使用性能分析工具、监控系统日志等。
linux测试磁盘iops方法详解
linux测试磁盘iops⽅法详解⼀、FIO安装yum -y install libaio-devel wget wget1 net-tools bind-utilswget http://brick.kernel.dk/snaps/fio-2.0.7.tar.gztar -zxvf fio-2.0.7.tar.gzcd fio-2.0.7make && make install⼆、随机读测试:[root@localhost ~]# fio -filename=/dev/sdb1 -direct=1 -iodepth 1 -thread -rw=randrw -rwmixread=70 -ioengine=psync -bs=16k -size=15G -numjobs=20 -runtime=60 -group_reporting -name=mytest说明:filename=/dev/sdb1 测试⽂件名称,通常选择需要测试的盘的data⽬录。
direct=1 测试过程绕过机器⾃带的buffer。
使测试结果更真实。
rw=randwrite 测试随机写的I/Orw=randrw 测试随机写和读的I/Obs=16k 单次io的块⽂件⼤⼩为16kbsrange=512-2048 同上,提定数据块的⼤⼩范围size=5g 本次的测试⽂件⼤⼩为5g,以每次4k的io进⾏测试。
numjobs=30 本次的测试线程为30.runtime=1000 测试时间为1000秒,如果不写则⼀直将5g⽂件分4k每次写完为⽌。
ioengine=psync io 引擎使⽤pync⽅式rwmixwrite=30 在混合读写的模式下,写占30%group_reporting 关于显⽰结果的,汇总每个进程的信息。
此外lockmem=1g 只使⽤1g内存进⾏测试。
zero_buffers ⽤0初始化系统buffer。
linux使用FIO测试磁盘的iops
linux使⽤FIO测试磁盘的iopsFIO是测试IOPS的⾮常好的⼯具,⽤来对硬件进⾏压⼒测试和验证,⽀持13种不同的I/O引擎,包括:sync,mmap, libaio, posixaio, SG v3, splice, null, network, syslet, guasi, solarisaio 等等。
fio 官⽹地址:⼀,FIO安装wgetyum install libaio-develtar -zxvf fio-2.2.5.tar.gzcd fio-2.2.5makemake install⼆,FIO⽤法:随机读:(可直接⽤,向磁盘写⼀个2G⽂件,10线程,随机读1分钟,给出结果)fio -filename=/tmp/test_randread -direct=1 -iodepth 1 -thread -rw=randread -ioengine=psync -bs=16k -size=2G -numjobs=10 -runtime=60 -group_reporting -name=mytest说明:filename=/dev/sdb1 测试⽂件名称,通常选择需要测试的盘的data⽬录。
direct=1 测试过程绕过机器⾃带的buffer。
使测试结果更真实。
rw=randwrite 测试随机写的I/Orw=randrw 测试随机写和读的I/Obs=16k 单次io的块⽂件⼤⼩为16kbsrange=512-2048 同上,提定数据块的⼤⼩范围size=5g 本次的测试⽂件⼤⼩为5g,以每次4k的io进⾏测试。
numjobs=30 本次的测试线程为30.runtime=1000 测试时间为1000秒,如果不写则⼀直将5g⽂件分4k每次写完为⽌。
ioengine=psync io引擎使⽤pync⽅式rwmixwrite=30 在混合读写的模式下,写占30%group_reporting 关于显⽰结果的,汇总每个进程的信息。
上海三思情报板ip地址编写
上海三思情报板IP地址编写用串口线(交叉线2、3交叉)连接上位机的CONSOLE\口;通过串口转USB连接电脑。
打开超级终端,新建连接----(连接时使用)选择COM口-----端口设置:设置完毕后。
关闭上位机电源;5秒后开机中间屏幕上会出现:ing the kernel.Linux version 2.6.17 (**************.com)(gcc version 4.0.0 (DENX ELDK 4.0 4.0.0)) #9 W ed Dec 13 13:50:31 CST 2006CPU: ARM920Tid(wb) [41129200] revision 0 (ARMv4T) Machine: Atmel A T91RM9200-DKMemory policy: ECC disabled, Data cache writebackClocks: CPU 59 MHz, master 59 MHz, main 18.432 MHzCPU0: D VIVT write-back cacheCPU0: I cache: 16384 bytes, associativity 64, 32 byte lines, 8 sets CPU0: D cache: 16384 bytes, associativity 64, 32 byte lines, 8 sets Built 1 zonelistsKernel command line: console=ttyS0,115200 root=/dev/mtdblock0 rwA T91: 128 gpio irqs in 4 banksPID hash table entries: 128 (order: 7, 512 bytes)Console: colour dummy device 80x30Dentry cache hash table entries: 2048 (order: 1, 8192 bytes)Inode-cache hash table entries: 1024 (order: 0, 4096 bytes) Memory: 16MB = 16MB totalMemory: 14480KB available (1344K coMount-cache hash table entries: 512CPU: Testing write buffer coherency: okNET: Registered protocol family 16NET: Registered protocol family 2IP route cache hash table entries: 128 (order: -3, 512 bytes)TCP established hash table entries: 512 (order: -1, 2048 bytes)TCP bind hash table entries: 256 (order: -2, 1024 bytes)TCP: Hash tables configured (established 512 bind 256)TCP reno registeredNetWinder Floating Point Emulator V0.97 (double precision)yaffs Dec 12 2006 16:02:14 Installing.io scheduler noopio scheduler anticipatory registered (default)at91_usart.0: ttyS0 at MMIO 0xfefff200 (irq = 1) is a A T91_SERIAL at91_usart.1: ttyS1 at MMIO 0xfffc0000 (irq = 6) is a A T91_SERIALat91_usart.2: ttyS2 at MMIO 0xfffc4000 (irq = 7) is a A T91_SERIALat91_usart.3: ttyS3 at MMIO 0xfffcc000 (irq = 9) is a A T91_SERIALat91_ether: Y our bootloader did not configure a MAC address.eth0: Link down.eth0: A T91 ethernet at 0xfefbc000 int=24 10-HalfDuplex (00:00:00:00:00:00)eth0: VIA VT6103 PHYNo SmartMedia card inserted.NAND device: Manufacturer ID: 0xec, Chip ID: 0xf1 (Samsung NAND 128MiB 3,3V 8-bit)Scanning device for bad blocksBad eraseblock 147 at 0x01260000Bad eraseblock 200 at 0x01900000Bad eraseblock 229 at 0x01ca0000Bad eraseblock 256 at 0x02000000Bad eraseblock 330 at 0x02940000Bad eraseblock 586 at 0x04940000Bad eraseblock 744 at 0x05d00000Bad eraseblock 807 at 0x064e0000Bad eraseblock 862 at 0x06bc0000Bad eraseblock 873 at 0x06d20000Bad eraseblock 893 at 0x06fa0000Bad eraseblock 965 at 0x078a0000Bad eraseblock 997 at 0x07ca0000Creating 1 MTD partitions on "NAND 128MiB 3,3V 8-bit": 0x00180000-0x08000000 : "Partition 1"mice: PS/2 mouse device common for all miceat91_rtc at91_rtc: rtc intf: sysfsat91_rtc at91_rtc: rtc intf: procat91_rtc at91_rtc: rtc intf: dev (254:0)at91_rtc at91_rtc: rtc core: registered at91_rtc as rtc0A T91 Real Time Clock driver.i2c /dev entries driverat91_i2c at91_i2c: A T91 i2c bus driver.I2C: RX-8025 RTC driver successfully loaded VRAM and watchdog driver 1.05 successfully l TCP bic registeredNET: Registered protocol family 1NET: Registered protocol family 17NTCIP transportation transport protocol v1.00 NET: Registered protocol family 29yaffs: dev is 32505856 name is "mtdblock0" yaffs: Attempting MTD mount on 31.0, "mtdblock0" yaffs: auto selecting yaffs2block 136 is badblock 189 is badblock 218 is badblock 245 is badblock 319 is badblock 575 is badblock 733 is badblock 796 is badblock 851 is badblock 862 is badblock 882 is badblock 954 is badblock 986 is badVFS: Mounted root (yaffs filesystem).Freeing init memory: 72Keth0: Setting MAC address to 00:12:03:12:00:26eth0: Link down.Please press Enter to activate this console.BusyBox v1.01 (2006.04.20-05:46+0000) Built-in shell (ash)Enter 'help' for a list of built-in commands./ #输入ls (查看文件名)回车后会出现:bin etc linuxrc proc signaler usrdev lib lost+found sbin sys输入cd etc (进入etc文件)回车后会出现:/etc #输入ls (查看文件名)回车后会出现:brightness.tab init.d signaler.conf输入cd init.d (进入init.d文件)回车后会出现:/etc # cd init.d/etc/init.d #输入ls (查看文件名)回车后会出现:/etc/init.d # lsrcS输入vi rcS 回车后会出现:(注意:S是大写)"rcS" [modified] line 6 of 8 --75%--"rcS" 8L, 233C#!/bin/shmount -n -t proc /proc /procmount -n -t sysfs /sys /sysifconfig eth0 hw ether 00:12:03:12:00:26ifconfig eth0 10.142.84.67 netmask 255.255.255.224route add default gw 10.142.84.65telnetd -l /bin/sh/signaler/signaler &~按(i)开始输入:在出现IP的地方修改为自己需要的IP 子网掩码网关注意:不要多或少输入不要多输入空格等输入完毕后按键Esc长按键Shift +:输wq 回车出现/etc/init.d #输入reboot(重启)重启后,再执行一次查看IP,看是否更改正确,用comdebug2.7 也就是情报板调试2.7 用网络模式输入IP 查看命令等测试是否工作正常。
Linux命令技巧实现自动化任务调度
Linux命令技巧实现自动化任务调度自动化任务调度是现代IT运维中十分重要的一环,它可以提高工作效率,降低人为操作带来的错误风险。
而在Linux系统中,我们可以通过一些命令技巧来实现自动化任务调度,本文将介绍其中几个常用的方法。
1. crontab命令crontab命令是Linux系统下的定时任务管理工具。
通过编辑crontab 文件,我们可以配置需要定时执行的任务。
具体操作步骤如下:首先,打开终端,输入命令 crontab -e 打开当前用户的crontab配置文件。
然后,在文件的末尾添加需要定时执行的任务,格式为:分钟小时日期月份星期要执行的命令例如,如果我们想每天的早上7点执行一个备份脚本,可以添加如下行:0 7 * * * /path/to/backup.sh最后,保存并退出文件。
crontab将会自动生效。
2. at命令at命令是另一个常用的任务调度工具,它可以用于在指定的时间执行一次性任务。
与crontab命令不同,at命令适合于那些仅需执行一次的任务。
以下是使用at命令执行任务的步骤:在终端中输入命令 at 时间,其中时间可以是绝对时间,也可以是相对时间。
按照提示,输入要执行的命令或脚本。
按Ctrl+D保存并退出。
任务会在指定的时间自动执行。
3. systemd定时任务对于使用systemd作为init系统的Linux发行版,我们可以利用systemd的timer来实现任务调度。
systemd的timer是基于systemd unit 的一种定时调度机制。
以下是配置systemd定时任务的步骤:首先,在指定的目录下创建一个.timer后缀的文件,例如mytask.timer。
在该文件中,定义任务的时间间隔和要执行的命令或脚本。
再创建一个与.timer文件同名的.service文件,用于指定要执行的任务。
最后,使用systemctl命令启动timer并使其生效。
4. shell脚本的定时任务除了使用系统工具外,我们还可以编写Shell脚本,并通过命令行工具进行调度。
linux IO优化磁盘读写参数设置
linux I/O优化磁盘读写参数设置关于页面缓存的信息,可以用cat /proc/meminfo看到。
其中的Cached 指用于pagecache的内存大小(diskcache-SwapCache)。
随着写入缓存页,Dirty 的值会增加。
一旦开始把缓存页写入硬盘,Writeback的值会增加直到写入结束。
Linux 用pdflush进程把数据从缓存页写入硬盘,查看有多少个pdflush进程cat /proc/sys/vm/nr_pdflush_threadspdflush的行为受/proc/sys/vm中的参数的控制/proc/sys/vm/dirty_writeback_centisecs (default 500):1/100秒, 多长时间唤醒pdflush将缓存页数据写入硬盘。
默认5秒唤醒2个(更多个)线程。
如果wrteback的时间长于dirty_writeback_centisecs的时间,可能会出问题。
pdflush的第一件事是读取/proc/sys/vm/dirty_expire_centiseconds (default 3000)1/100秒。
缓存页里数据的过期时间(旧数据),在下一个周期内被写入硬盘。
默认30秒是一个很长的时间。
第二件事是判断内存是否到了要写入硬盘的限额,由参数决定:/proc/sys/vm/dirty_background_ratio (default 10)百分值,保留过期页缓存(脏页缓存)的最大值。
是以MmeFree+Cached-Mapped的值为基准的pdflush写入硬盘看两个参数:1 数据在页缓存中是否超出30秒,如果是,标记为脏页缓存;2 脏页缓存是否达到工作内存的10%;以下参数也会影响到pdflush/proc/sys/vm/dirty_ratio (default 40)总内存的最大百分比,系统所能拥有的最大脏页缓存的总量。
超过这个值,开启pdflush写入硬盘。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Linux I/O Scheduler--Noop分类:Linux驱动程序2012-12-10 21:24 2437人阅读评论(2) 收藏举报每个块设备或者块设备的分区,都对应有自身的请求队列(request_queue),而每个请求队列都可以选择一个I/O调度器来协调所递交的request。
I/O调度器的基本目的是将请求按照它们对应在块设备上的扇区号进行排列,以减少磁头的移动,提高效率。
在前面讨论递交I/O请求的时候可以发现,每个request_queue都有一个request的队列,队列里的请求将按顺序被响应。
实际上,除了这个队列,每个调度器自身都维护有不同数量的队列,用来对递交上来的request进行处理,而排在队列最前面的request将适时被移动到request_queue中等待响应。
内核中实现的IO调度器主要有四种--Noop,Deadline,CFG以及最复杂的as.我们不妨从最简单的noop开始研究,顺便看一下调度器是如何与request_queue联系上的。
首先要了解描述elevator的数据结构。
和elevator相关的数据结构有个,一个是elevator_type,一个是elevator_queue,前者对应一个调度器类型,后者对应一个调度器实例,也就说如果内核中只有上述四种类型的调度器,则只有四个elevator_type,但是多个块设备(分区)可拥有多个相应分配器的实例,也就是elevator_queue。
两个数据结构中最关键的元素都是struct elevator_ops,该结构定义了一组操作函数,用来描述请求队列的相关算法,实现对请求的处理。
[cpp]view plaincopy1.struct elevator_type2.{3.struct list_head list;4.struct elevator_ops ops;5.struct elv_fs_entry *elevator_attrs;6.char elevator_name[ELV_NAME_MAX];7.struct module *elevator_owner;8.};[cpp]view plaincopy1.struct elevator_queue2.{3.struct elevator_ops *ops;4.void *elevator_data;5.struct kobject kobj;6.struct elevator_type *elevator_type;7.struct mutex sysfs_lock;8.struct hlist_head *hash;9.};函数elevator_init()用来为请求队列分配一个I/O调度器的实例[cpp]view plaincopy1.int elevator_init(struct request_queue *q, char *name)2.{3.struct elevator_type *e = NULL;4.struct elevator_queue *eq;5.int ret = 0;6.void *data;7.8.9./*初始化请求队列的相关元素*/10. INIT_LIST_HEAD(&q->queue_head);11. q->last_merge = NULL;12. q->end_sector = 0;13. q->boundary_rq = NULL;14.15./*下面根据情况在elevator全局链表中来寻找适合的调度器分配给请求队列*/16.17.if (name) {//如果指定了name,则寻找与name匹配的调度器18. e = elevator_get(name);19.if (!e)20.return -EINVAL;21. }22.23.//如果没有指定io调度器,并且chosen_elevator存在,则寻找其指定的调度器24.if (!e && *chosen_elevator) {25. e = elevator_get(chosen_elevator);26.if (!e)27. printk(KERN_ERR "I/O scheduler %s not found\n",28. chosen_elevator);29. }30.31.//依然没获取到调度器的话则使用默认配置的调度器32.if (!e) {33. e = elevator_get(CONFIG_DEFAULT_IOSCHED);34.if (!e) {//获取失败则使用最简单的noop调度器35. printk(KERN_ERR36."Default I/O scheduler not found. " \37."Using noop.\n");38. e = elevator_get("noop");39. }40. }41.42.//分配并初始化elevator_queue43. eq = elevator_alloc(q, e);44.if (!eq)45.return -ENOMEM;46.47.//调用ops中的elevator_init_fn函数,针对调度器的队列进行初始化48. data = elevator_init_queue(q, eq);49.if (!data) {50. kobject_put(&eq->kobj);51.return -ENOMEM;52. }53.54.//建立数据结构的关系55. elevator_attach(q, eq, data);56.return ret;57.}所有的I/O调度器类型都会通过链表链接起来(通过struct elevator_type 中的list元素),elevator_get()函数便是通过给定的name,在链表中寻找与name匹配的调度器类型。
当确定了I/O调度器的类型后,便要通过elevator_alloc()为等待队列分配一个调度器的实例--struct elevator_queue,并进行初始化;其后,由于每个调度器根据自身算法的不同,都会拥有不同的队列结构,在elevator_init_queue()中会调用特定于调度器的初始化函数针对这些队列进行初始化,并且返回特定于调度器的数据结构,最后再elevator_attach()中建立相关结构的关系。
[cpp]view plaincopy1.static struct elevator_queue *elevator_alloc(struct request_queue *q,2.struct elevator_type *e)3.{4.struct elevator_queue *eq;5.int i;6.7.//为eq分配内存8. eq = kmalloc_node(sizeof(*eq), GFP_KERNEL | __GFP_ZERO, q->node);9.if (unlikely(!eq))10.goto err;11.12.//根据之前确定的elevator_type初始化eq13. eq->ops = &e->ops;14. eq->elevator_type = e;15. kobject_init(&eq->kobj, &elv_ktype);16. mutex_init(&eq->sysfs_lock);17.18.//分配elevator的哈希表内存19. eq->hash = kmalloc_node(sizeof(struct hlist_head) * ELV_HASH_ENTRIES,20. GFP_KERNEL, q->node);21.if (!eq->hash)22.goto err;23.24.//初始化哈希表25.for (i = 0; i < ELV_HASH_ENTRIES; i++)26. INIT_HLIST_HEAD(&eq->hash[i]);27.28.return eq;29.err:30. kfree(eq);31. elevator_put(e);32.return NULL;33.}[cpp]view plaincopy1.<span style="font-size:12px;">static void *elevator_init_queue(struct request_queue *q,2.struct elevator_queue *eq)3.{4.return eq->ops->elevator_init_fn(q);5.}</span>[cpp]view plaincopy1.<span style="font-size:12px;">static void elevator_attach(struct request_queue *q, struct elevator_queue *eq,2.void *data)3.{4. q->elevator = eq;5. eq->elevator_data = data;6.}</span>下面就来看一下elevator_ops中定义了哪些操作[cpp]view plaincopy1.<span style="font-size:12px;">struct elevator_ops2.{3. elevator_merge_fn *elevator_merge_fn;4. elevator_merged_fn *elevator_merged_fn;5. elevator_merge_req_fn *elevator_merge_req_fn;6. elevator_allow_merge_fn *elevator_allow_merge_fn;7.8. elevator_dispatch_fn *elevator_dispatch_fn;9. elevator_add_req_fn *elevator_add_req_fn;10. elevator_activate_req_fn *elevator_activate_req_fn;11. elevator_deactivate_req_fn *elevator_deactivate_req_fn;12.13. elevator_queue_empty_fn *elevator_queue_empty_fn;14. elevator_completed_req_fn *elevator_completed_req_fn;15.16. elevator_request_list_fn *elevator_former_req_fn;17. elevator_request_list_fn *elevator_latter_req_fn;18.19. elevator_set_req_fn *elevator_set_req_fn;20. elevator_put_req_fn *elevator_put_req_fn;21.22. elevator_may_queue_fn *elevator_may_queue_fn;23.24. elevator_init_fn *elevator_init_fn;25. elevator_exit_fn *elevator_exit_fn;26.void (*trim)(struct io_context *);27.};28.</span>我们这里只关注几个主要的操作函数,其中前面加了*号的表示这些函数是每个调度器都必须实现的elevator_merge_fn查询一个request,用于将bio并入elevator_merge_req_fn将两个合并后的请求中多余的那个给删除*elevator_dispatch_fn将调度器的队列最前面的元素取出,分派给request_queue中的请求队列以等候响应**elevator_add_req_fn将一个新的request添加进调度器的队列elevator_queue_empty_fn检查调度器的队列是否为空elevator_set_req_fn和elevator_put_req_fn分别在创建新请求和将请求所占的空间释放到内存时调用*elevator_init_fn用于初始化调度器实例一个请求在创建到销毁的过程遵循下面三种流程set_req_fn ->i. add_req_fn -> (merged_fn ->)* -> dispatch_fn -> activate_req_fn -> (deactivate_req_fn -> activate_req_fn ->)* -> completed_req_fnii. add_req_fn -> (merged_fn ->)* -> merge_req_fniii. [none]-> put_req_fn我们在分析调度器的实现时,不妨也以此为依据,选择i或者ii来作为分析的流程。