《linux内核精髓》-记录-资源管理
资源管理
资源管理
本节主要内容:通过cgroup和namespace实现容器功能
容器
cgroup:
cgroup是将任意进程进行分组化管理的linux内核功能。cgroup本身提供将进程进行分组化管理的功能和接口的基础结构,I/O或内存的分配控制等具体资源管理通过这个功能来实现。这些资源管理功能称为cgroup子系统或控制器。
- cgroup子系统有内存的memory控制器、控制进程调度的cpu控制器。由/proc/cgroup确定内核可以使用的cgroup子系统。
- cgroup提供一个cgroup虚拟文件系统,作为分组管理和子系统设置的用户接口。(虚拟文件系统指不具有物理设备的文件系统。)
- 文件名前缀为cgroup及没有前缀的文件由cgroup的基础结构提供的特殊文件。前缀为debug的文件由debug子系统提供的特殊文件。特殊文件分为只读和可读写两种。最重要的是tasks特殊文件(tasks文件记录线程id)。
- tasks中包含shell的TID(也是pid,即进程ID)
文件名 | r/w | 用途 |
---|---|---|
release_agent | rw | 删除分组时执行的命令。这个文件只存在于根分组 |
notify_on_release | rw | 设置是否执行release_agent。为1时执行 |
tasks | rw | 属于分组的线程TID列表 |
cgroup.procs | r | 属于分组的进程PID列表。仅包括多线程进程的线程leader的TID,与tasks不同 |
cgroup.event_control | rw | 监视状态变化和分组删除事件的配置文件 |
实际使用cgroup时,应将线程添加到分组后,在分组内的特殊文件中设置值,来控制系统的运行。
namespace
namespace,可以让每个进程组拥有独立的PID、IPC和网络空间。
名称 | 说明 |
---|---|
clone_newipc | 划分IPC(进程间通信)命名空间。信号量(semaphre)、共享内存、消息队列等进程间通信资源 |
clone_newnet | 划分网络命名空间、分配网络接口 |
clone_newns | 划分挂载命名空间,与chroot同样分配新的根文件系统 |
clone_newpid | 划分PID命名空间,分配新的进程ID空间 |
clone_newuts | 划分UTS命名空间,分配新的UTS空间 |
LXC(linux container) linux容器,在启动lxc之前必须启用cgroup文件系统,挂在cgroup文件系统。
mount -t cgroup cgroup/cgroup
创建根目录,挂载如:lib、etc、bin等目录。
创建网络,创建用来连接分配到容器的网络接口的网桥。
之后可以通过lxc启动容器
调度策略
本部分主要内容: linux调度策略
调度策略
调度策略可以分为TSS(time sharing system,分时系统)和实时系统两种。
- 一般进程通过分时运行,通过时间片的方式,将cpu分配给各个进程。
- 实时调度策略适合优先级要求很高的进程。相比于TSS调度策略。cpu将优先分配给实时调度策略的进程。
linxu 进程静态优先级为0-99。TSS调度策略优先级为0,实时调度策略优先级为1-99.
- TSS调度策略有动态优先级。长时间持续使用cpu进程,其动态优先级会逐渐降低。优先级更高更容易分配到cpu。
- linux的五种调度策略:sched_other、sched_fifo、sched_rr、sched_batch、sched_idle。
- sched_other: 标准调度策略,也是TSS分时系统调度策略,2.6.23之前时间片由优先级决定。
- sched_fifo: 实时调度策略,优先级指定为1-99,除等待i/io完成时休眠、自发休眠或优先级更高的实时进程获得优先权外,不会释放执行权。经常不会主动释放cpu。
- sched_rr: 也是实时调度策略,与sched_fifo不同的是,它具有时间片,时间片使用完后会转移到其他进程。时间片固定值100毫秒
- sched_batch: 此策略不会根据休眠时间更改优先级,不能作为会话调度策略。使用了此策略的进程会被识别为0的cpu bound进程。因此,优先级必然会比会话型shell进程低。对非会话型进程使用这个调度策略,可以使会话保持高优先级,保持应答。
- sched_IDLE: cpu空闲时,即sched_idle等级以外处于可执行状态的进程消失时,将被赋予执行权,它将成为优先级最低的进程。
- 特殊标志:sched_reset_on_fork 为限制实时调度策略,为调度策略添加标志flag。在执行fork()时,新生成的子进程就称为sched_other策略的进程。
- 关于调度策略的系统调用
sched_setscheduler() 更改调度策略和进程优先级 sched_getscheduler() 获取当前调度策略与进程优先级 sched_setparam() 更改调度参数(进程优先级) sched_getparam() 获取当前调度参数 sched_get_priority_max() 获取调度策略的进程静态优先级范围 sched_get_priority_min() 获取调度策略的进程静态优先级范围 sched_rr_get_interval() 获取当前时间片
- chart命令
- chart命令可以简单更改调度策略。
- 更改实时调度策略必须具有root权限。
RT group scheduling 与 RT Throttling
本节主要内容: 对实时进程所使用的cpu时间进行限制的功能RT group scheduling和RT Throttling.
在内核2.6.25后的版本中加入此功能。
实时调度策略哟静态优先级,调度优先级比其他一般进程高,需要执行时一定会分配cpu时间。如果实时进程陷入无限循环,就会造成系统卡死。
该功能通过是实现限制实时进程的cpu时间使执行权在实时进程无限循环的情况下也能切换到其他进程,避免系统死机。
- 实时
- 实时功能的目的使实现满足实时限制的处理。在有限时间内得到处理结果。即使内核运行过程中,也能迅速切换到要处理事件的进程。内核内部设置了优先权点(reemption point),可以根据事件立刻切换到实时进程。
- 要求实时性的处理,如图形处理。分配时间提高吞吐量的目的和实时的目的是不同的。
- RT Throtting
- 通过限制分配给实时进程的每个单位时间的cpu时间,就可以防止实时调度策略 进程出现bug导致系统死机。
- 可以指定单位时间内分配多少cpu时间给实时进程。标准设置为1秒,cpu分配时间是0.95秒,非实时进程1秒可以使用cpu0.05秒。
- 实时:在一定时间内完成处理。
- 整个系统的cpu时间可以使用sysctl来获取、设置。
- 当CONFIG_RT_GROUP_SCHED有效时,受到cgroup设置值的限制,不能设置与cgroup的有效值相矛盾的设置。将sched_rt_runtime_us设置为-1,是用来使RT Throttling失效的设置。
- sysctl的设置仅用于有效与无效的切换,单个设置使用cgroup进行。RT group scheuling是cgroup的子系统。要使用RT Group Scheduling,必须启用CONFIG_RT_GROUP_SCHED。
cpu_rt_period_us 单位时间 cpu_rt_runtime_us 分配时间
Fair Group Scheduling
本节内容: cgroup之管理cpu资源的Fair group scheduling。
- fair group scheduling
- 是cgroup资源管理器之一,用来控制linux内核的进程调度程序进行cpu时间分配。可以对每个特定进程组进行资源管理。
- Fair Group scheduling 使用的是linux2.6.23之后引入的cfs的cpu时间分配控制功能。
- 需要挂载cgroup文件系统。挂载时需要启用cpu资源控制。
- cpu.shares特殊文件,可以对进程调度程序所处理的进程组设置cpu时间分配的比重。通过修改这个值,可以调整cpu时间比例。默认1024.
- 能以进程组为单位控制cpu资源分配。
cpuset
本节内容:控制物理cpu分配的cpuset
cpuset时linux控制组cgroup之一,其功能指定特定的进程或线程所使用的cpu组。还能指定内存节点的分配。
- 用法
- 使用cpuset前,必须通过内核config启用cpuset功能。最近的发布版本在标准中已启用。
- 也需要挂载cgroup。通过向cpuset.cpus特殊文件写入要分配的cpu编号控制分组。之后往task特殊文件中添加pid($$表示shell本身的pid)。此后shell启动的进程全部在这个组下,使用的cpu为cpuset.cpus中的cpu。
通过固定使用cpu,可以提高缓存的利用效率和性能。
使用memory cgroup限制内存使用量
memory cgroup 是cgroup之一,用来控制进程所使用的内存(lru管理的缓存)数量。
-
memory cgroup
- 控制进程使用的内存数量,避免因处理大文件或大量文件导致页面缓存增大,内存资源紧张,可在多用户环境中限制各用户可以使用的的内存量。
- memory cgroup 是 cgroup的一种,因此必须挂载cgroup系统。
-
用法
文件名 说明 memory.usage_in_bytes 显示当前内存(进程内存+页面缓存)的使用量 memory.memsw.usage_in_bytes 显示当前内存(进程内存+页面缓存)+交换区使用量 memory.limit_in_bytes 设置、显示内存(进程内存+页面缓存) 使用量的限制值 memory.memsw.limit_in_bytes 设置、显示内存(进程内存+页面缓存)+交换区使用量的限制 memory.falicnt 显示内存(进程内存+页面缓存) 达到限制的次数 memory.memsw.failcnt 显示内存(进程内存+页面缓存) +交换区达到限制值的次数 memory.max_usage_in_bytes 显示记录的内存(进程内存+页面缓存)使用量的最大值 memory.memsw.max_usage_in_bytes 显示记录的内存(进程内存+页面缓存)+交换区使用量的最大值 memory.stat 输出统计信息 memory.force_empty 强制释放分配给分组的内容 memory.use_hierarchy 设置、显示层次结构的使用 memory.swappiness 设置、显示针对分组的swappiness(相当于sysctl的vm.swappiness) -
限制内存使用量
内存使用量可以使用memory.limit_in_bytes进行限制。echo 10M > /cgroup/groupa/memory.limit_in_bytes
-
层次结构
通过Memory cgroup控制的分组可以采用层次结构。可以在memory.use_hierarchy中写入1,启用分组的层次结构。echo 1 > /cgroup/memory.use_hierarchy
-
显示统计信息
关于各分组内存使用量信息可以从memory.stat文件读取名称 说明 cache 页面缓存量(字节数) rss 匿名页面与交换区缓存的内存量(字节数) mappend_file 指向进程空间的文件映射所使用的内存量(字节数) pgpgin 页面换入次数 pgpgout 页面换出次数 swap 交换区使用量(字节数) inactive_anon LRU列表中无效的匿名页面(字节数) active_anon LRU列表中有效的匿名页面(字节数) inactive_file LRU列表中无效的文件缓存(字节数) active_file LRU列表中有效的文件缓存(字节数) unevictable 不能用mlock等回收的内存量(字节数) 下列内容在使用层次结构时有效,将显示层次结构的上层的分组限制值。
名称 说明 hierarchical_memory_limit 上层分组对内存(进程内存+页面缓存)的限制值 hierarchical_memsw_limit 上层分组对内存(进程内存+页面缓存)+交换区的限制值 层次结构分组下的合计值
名称 说明 total_cache 本分组下所有页面缓存量(字节数)的合计值 total_rss 本分组下所有匿名页与交换区缓存内存量(字节数)的合计值 total_mappend_files 本分组下所有指向进程空间的文件映射所使用的内存量(字节数)的合计值 total_pgpgin 本分组下所有页面换入次数的合计值 total_pgpgout 本分组下所有页面换出次数的合计值 total_swap 本分组下所有交换区使用量(字节数)的合计值 total_inactive_anon 本分组下所有LRU列表中无效的匿名页面(字节数)的合计值 total_active_anon 本分组下所有LRU列表中有效的匿名页面(字节数)的合计值 total_inactive_file 本分组下所有LRU列表中无效的文件缓存(字节数)的合计值 total_active_file 本分组下所有LRU列表中有效的文件缓存(字节数)的合计值 total_unevictable 本分组下所有不能用molock等回收的内存量(字节数)的合计值
使用block I/O控制器设置I/O优先级
本节主要内容: 通过block I/O控制将进程分组,并对该分组设置I/O的优先级。
-
使用Block I/O控制器的前提条件
- block I/O时cgroup的子系统之一,时作为I/O调度程序之一的CFQ的一部分安装。使用Block I/O控制器时,必须使用启用了config选型编译的内核。
- 如果有/proc/cgroups,运行中的内核就可以支持cgroup。文件中显示了blkio,且enbable为1则表示支持并开启。内核参数为 cgroup_disable=blkio
- 确认CFQ作为I/O调度程序使用。
cat /sys/class/block/sdb/queue/scheduler
- I/O调度程序时对一般块设备使用的,比如:loopback设备loop0不会显示cfq,sda、sdb等一般块设备才可能显示。
-
尝试使用Blocki/O控制器
- 控制器通过cgroup文件系统进行,需要先噶在blkio作为子系统使用。
- blkio.weigh中写入范围在100-1000的Weight值。初始值为500,值越大表示优先级越高。仅根分组的weight初始值为1000。
-
Block I/O控制器提供的的特殊文件
文件名 r/w 用途 blkio.wight rw 设置分组weight值的文件。weight值可以设置100-1000.weight越大,优先级越高 blkio.weight_device rw 按照<设备主号码>:<设备副号码><weight值>的格式 blkio.io_merged r 合并的I/O数 blkio.io_queued r 当前保留的I/O数 blkio.io_service_bytes r I/O请求总字节数 blkio.io_serviced r I/O请求数 blkio.io_service_time r 从I/O亲求设备到完成所花费的总时间,单位为纳秒 blkio.io_wait_time r I/O亲求到达设备之前保留在等待队列的总时间。单位为纳秒 blkio.reset_stats w 写入后,统计信息被清除 blkio.sectors r I/O请求的总扇区数 blkio.time r 目前为止分配给分组的时间片的长度,单位为纳秒 I/O合并是指将应用程序发出的多个I/O请求合并为1个。提高I/O处理效率。
-
关于Block I/O控制器的CFQ设置用虚拟文件
- /sys/block/
/queue/iosched中CFQ的设置用虚拟文件,会对Block I/O控制器的运行产生影响。
group_isolation rw 用来设置在I/O性能和分组间优先级控制二者中优先切换到哪一个,为0时,为了实现I/O性能最大化,会适度降低一些分组优先级的兼容性
- /sys/block/
-
限制事项
- Block I/O控制器不支持非同步I/O,即支持初次读入和Direct I/O的读写。普通的写入经过页面缓存的非同步I/O,因此不属于优先级控制对象,都被看做根分组发出的I/O.
- 不支持分组层次化,分组层次限制仅为1层,无法创建从根分组开始有两层次以上的分组。
- 根分组与子分组作同等处理
- block I/O 控制器对各分组的I/O请求分配时间片。仅允许各分组在这个时间片内执行I/O操作。分组的weight值越大,时间片越长,通过这个方式来指定分组间的优先级。
- block I/O 控制器只有在针对设备的I/O发生竞争时,才根据优先级对I/O进行控制。
虚拟子系统存储的调整
主要内容: 使用/proc进行虚拟存储子系统的调整
- 虚拟空间存储方式
在linux上分配内存时,通过页面为单位的虚拟存储方式进行。可以不用担心内存碎片。- 程序使用的页面是在应用程序最初访问时由内核分配的。
- 如果分配的页面为程序文本、有初始值的数据(.data)区域或(被mmap的)数据文件区域,则在页面分配的同时从对应的文件读取数据,页面通过这些数据初始化。
- 如果不存在初始值的数据区域,则只进行页面分配处理。这种页面作为匿名(anonymous)页面处理。
- 通过malloc()等分配可用的存储区时,不会离开向该区域(空间)分配实际的页面。而是在必要的时候分配需要用到的页面。
- 多数应用程序一般都不会使用分配的所有存储区。有分配最大数据量大小相等的缓冲区以及散列表这种没有实体的空间。使用虚拟存储方式就不需要向未使用的空间分配内存。
- 虚拟空间超额使用量的调整
可以使用ulimiti命令对进程的最大虚拟空间进行限制。- 在linux中有“允许超过物理内存量分配多少虚拟空间”的参数,通过:
/proc/sys/vm/overcommit_memory #控制虚拟空间分配策略的参数 /proc/sys/vm/overcommit_ratio #指定允许过量使用的虚拟空间占物理内存总量的百分比默认50%
- overcommit_memory包含三种值:
OVERCOMMIT_GUESS(0) #默认 预测将空闲内存、页面缓存量、空闲交换区量、可回收slab(长字节)量等回收的页面数,虚拟空间要求分配的量比这个数小时,分配成功。多进程同时要求大量虚拟空间时无法正确预测的。可分配的虚拟空间大小基本是物理内存大小和交换分区大小的合计值。 OVERCOMMIT_ALWAYS(1) 虚拟空间分配总是成功。 OVERCOMMIT_NEVER(2) 会记录下整个系统内已分配的虚拟空间量。严格由系统管理,在分配和释放虚拟空间时重新计算。这个值为/proc/meminfo的Committed_AS
- overcommit_guess的情况下,对mmap系统调用设置了map_noreserve的虚拟空间量不添加到committed_as种,overcommit_never会将map_noreserver添加到committed_as。
- 所有物理内存量+总交换区量+通过/proc/sys/vm/overcommit_ratio指定的比例得到值,作为可分配虚拟空间总量。这个值在/proc/meminfo的commitLimit查看。commitLimit仅在利用overcommit_never时有效。在overcommit_guess、overcommit_always的情况下,这个项目没有意义。
ramzswap
主要内容: 将一部分内存作为交换设备使用的ramzswap。
将一部分内存空间作为交换设备使用的基于RAM的块设备。对要换出(swapout)的页面压缩后,写入内存。压缩使用LZO。运转速度高于一般磁盘设备。可以避免内存不足时,内存回收处理导致心能下降,或抑制oom Killer的运作。
在内存容量小,单cpu性能高的情况下可以提高性能
- ramzswap disk的使用方法
- 首先需要将用来压缩/解压缩数据的LZO模块按照到内核中。
对于没有静态安装到内核的情况下,可以使用 insmod ramzswap.ko进行安装。自己构建内核时,将config_lzo_decompress设置为y或m。 安装模块后,还需要对ramzswap设备进行初始化,启用交换功能。modprobe lzo_compress modprobe lzo_decompress
- backing swap的使用方法
另一种使用方法:将部分内存作为ramzswap disk使用,再将交换文件或交换块设备作为backing swap使用。如果内存不足则仅使用ramzswap disk进行处理,如果内存更少,则页面的内容存放到backing swap中。- 默认为所有内存的大小的15%。设置为1024kb。内存使用量一旦增加,首先压缩的页会写入ramzswap disk区域。未压缩到50%以下的页面写入backing swap,超过--memlimit_kb选项的写入backing swap。
oom killer的运行于结构
out of Memory (oom) killer 作为确保内存的最终手段,可以再耗尽内存或交换区后,向进程发送终止信号,强制终止进程。即使再无法释放内存的情况下,也能重复进行确保内存的处理过程。
- 进程的选定
oom killer在内存耗尽时,会查看所有进程,斌分别未每个进程计算分数。将信号发送给分数最高的进程。 - 计算分数的方法
- 计算分数以进程虚拟内存大小为基准,虚拟内存大小可以使用ps命令的vsz或/proc/
/status的vmsize。对于正在消耗虚拟内存的进程,其最初的分较高。单位为1kb为1分。小号1gb内存的进程,得分约为1000000. - 如果进程正在执行swapoff系统调用,则得分设置为最大值(unsignedlong的最大值),禁用swap的行为与消除内存不足是相反的,会立刻将其做为oom Killer的对象进程。
- 如果是母进程则将所有子进程内存大小的一半作为分数
- 根据进程的cpu使用实际和启动实际调整得分。认为越长时间运行或从事越多的进程越重要,保持得分较低。
用得分除以cpu使用时间(10秒为单位)的平方根。如果cpu使用时间为90秒,则结果为3。根据进程启动时间也可以调整得分。用得分除以启动时间(1000秒为单位)的平方根的平方根。如果是持续运行16000秒的进程,则用得分除以16的平方根“4”的平方根“2”。越长时间的进程就越重要。 - 通过nice命令等将优先级设置较低的进程,将得分翻倍。nice-n中设置为1-19的命令的得分翻倍。
- 特权进程普遍较为重要,因此将其得分设置为1/4。
- 通过capset(3)等设置了功能(capability)CAP_SYS_RAWIO的进程,其得分为1/4。将直接对硬件进行操作的进程判断为重要进程。
- 关于cgroup,如果进程只允许与促使OOM killer运行的进程所允许的内存节点完全不同的内存节点,则其得分为1/8.
- 通过proc文件系统oom_adj的值调整得分。
在设置了CAP_SYS_RAWIO的情况下,发送SIGTERM,在没有设置的情况下,发送SIGKILL。各进程的得分可以在/proc//oom_score来确认
init(pid为1的)进程不能成为oom killer的对象。当成为对象的进程包含子进程时,先向子进程发送信号。
向成为对象的进程发送信号后,对于引用系统的全线程,即使线程组(TGID)不同,如果存在与对象进程共享相同内存空间的进程,则也向这些进程发送信号。
- 计算分数以进程虚拟内存大小为基准,虚拟内存大小可以使用ps命令的vsz或/proc/
关于OOM Killer的proc文件系统
主要内容: 关于oom killer相关的proc文件系统。
/proc/
如果设置为-17,就是禁止oom killer 发出的信号。
- /proc/sys/vm/panic_on_oom设置为1时,在OOM killer 允许时可以不发送进程信号,只是内核产生重大故障。如果oom_kill_allocation_task设置除0以外的值,则促使oom killer运行的进程自身将接收到信号。
- oom_dump_tasks设置除0以外的值时,在oom killer运行时的输出中会增加进程的列表信息。可以使用dmesg或syslog来确认。
- linux2.6.36 以后的版本为oom_score_adj替换oom_adj,范围为-1000~1000,设置-1000时,该进程就被排除在OOM killer强制终止的对象外。
rhel5特征
- oom killer从上次调出到下次调出之间超过5秒时,调用次数重新开始计算。避免仅仅因为突发性的内存负载就终止进程。
- 在技术变成0后的1秒内调出时,不计入调用的次数。
- oom killer调用次数不足10次时,实际不会运行。 oom killer调用10次时才开始认为内存不足。
- 最后oom killer运行不到5秒的话,oom killer不会再次运行。这是为防止不必要地连续终止多个进程。也有等待接收到oom killer发出信号的进程终止(释放内存)的意思。
- oom killer一旦运行,调用的次数就重新回到0.
- rhel6不会慎重运行,得分机制与rhel5基本相同