主要介紹Linux下, 如果對程序的CPU和記憶體資源的使用情況進行控制的方法。
CPU資源控制
每個程序能夠占用CPU多長時間, 什麼時候能夠占用CPU是和系統的排程密切相關的.
Linux系統中有多種排程政策, 各種排程政策有其适用的場景, 也很難說哪種排程政策是最優的.
Linux的排程政策可以參見代碼: include/linux/sched.h
/*
* Scheduling policies
*/
#define SCHED_NORMAL 0
#define SCHED_FIFO 1
#define SCHED_RR 2
#define SCHED_BATCH 3
/* SCHED_ISO: reserved but not implemented yet */
#define SCHED_IDLE 5
/* Can be ORed in to make sure the process is reverted back to SCHED_NORMAL on fork */
#define SCHED_RESET_ON_FORK 0x40000000
Linux 系統也提供了修改排程政策的指令和系統調用接口.
調用接口請查詢相關文檔, 這裡主要介紹一下修改排程政策的指令 - chrt.
# 在一個終端中執行
sleep 1000
# 打開另一個終端
ps -ef | grep sleep # 找出 sleep 1000 的pid, 這裡假設是 1234
chrt -p 1234 # 可以檢視 pid=1234 的程序的 排程政策, 輸入如下:
pid 1234's current scheduling policy: SCHED_OTHER
pid 1234's current scheduling priority: 0
chrt -p -f 10 1234 # 修改排程政策為 SCHED_FIFO, 并且優先級為10
chrt -p 1234 # 再次檢視排程政策
pid 1234's current scheduling policy: SCHED_FIFO
pid 1234's current scheduling priority: 10
補充:
- chrt 也可以直接指定一條指令, 并設定這條指令的優先級的排程政策, 具體檢視 chrt --help
- 檢視一個程序的排程政策, 除了使用 chrt 指令之外, 還可以 cat /proc/<PID>/sched
實時程序的CPU控制
所謂的實時程序, 也就是那些對響應時間要求比較高的程序.
這類程序需要在限定的時間内處理使用者的請求, 是以, 在限定的這段時間内, 需要占用所有CPU資源, 并且不能被其它程序打斷.
在這種情況下, 如果實時程序中出現了類似死循環之類的情況, 就會導緻整個系統無響應.
因為實時程序的CPU優先級高, 并且未處理完之前是不會釋放CPU資源的.
是以, 核心中需要有一種方式來限制實時程序的CPU資源占用.
系統整體設定
1. 擷取目前系統的設定
sysctl -n kernel.sched_rt_period_us # 實時程序排程的機關CPU時間 1 秒
1000000
sysctl -n kernel.sched_rt_runtime_us # 實時程序在 1 秒中實際占用的CPU時間, 0.95秒
950000
這個設定說明實時程序在運作時并不是完全占用CPU的, 每1秒中有0.05秒的時間可以給其它程序運作.
這樣既不會對實時程序的響應時間造成太大的影響, 也避免了實時程序卡住時導緻整個系統無響應.
2. 設定實時程序占用CPU時間
上面的預設設定中, 實時程序占用 95% 的CPU時間. 如果覺得占用的太多或太少, 都是可以調整的.比如:
sysctl -w kernel.sched_rt_runtime_us=900000 # 設定實時程序每1秒中隻占0.9秒的CPU時間
kernel.sched_rt_runtime_us = 900000
sysctl -n kernel.sched_rt_runtime_us
900000
cgroup 中的設定
整體設定是針對整個系統的, 我們也可以通過 cgroup 來對一組程序的CPU資源進行控制.
如果想在 cgroup 中對 sched_rt_period_us 和 sched_rt_runtime_us 進行控制, 需要核心編譯選項 CONFIG_RT_GROUP_SCHED=y
檢視目前系統的核心編譯選項方法如下: (debian 7.6 系統)
cat /boot/config-`uname -r`
檢視 CONFIG_RT_GROUP_SCHED 是否啟用
cat /boot/config-`uname -r` | grep -i rt_group
# CONFIG_RT_GROUP_SCHED is not set
debian 7.6 預設沒有啟動這個選項, 是以挂載cgroup之後, 沒有設定 sched_rt_period_us 和 sched_rt_runtime_us 的檔案
mkdir /mnt/cgroup
mount -t cgroup cgroup /mnt/cgroup/
cd /mnt/cgroup/
ls -l
total 0
-r--r--r-- 1 root root 0 Aug 28 09:06 blkio.io_merged
-r--r--r-- 1 root root 0 Aug 28 09:06 blkio.io_queued
-r--r--r-- 1 root root 0 Aug 28 09:06 blkio.io_service_bytes
-r--r--r-- 1 root root 0 Aug 28 09:06 blkio.io_serviced
-r--r--r-- 1 root root 0 Aug 28 09:06 blkio.io_service_time
-r--r--r-- 1 root root 0 Aug 28 09:06 blkio.io_wait_time
--w------- 1 root root 0 Aug 28 09:06 blkio.reset_stats
-r--r--r-- 1 root root 0 Aug 28 09:06 blkio.sectors
-r--r--r-- 1 root root 0 Aug 28 09:06 blkio.time
-rw-r--r-- 1 root root 0 Aug 28 09:06 blkio.weight
-rw-r--r-- 1 root root 0 Aug 28 09:06 blkio.weight_device
-rw-r--r-- 1 root root 0 Aug 28 09:06 cgroup.clone_children
--w--w--w- 1 root root 0 Aug 28 09:06 cgroup.event_control
-rw-r--r-- 1 root root 0 Aug 28 09:06 cgroup.procs
-r--r--r-- 1 root root 0 Aug 28 09:06 cpuacct.stat
-rw-r--r-- 1 root root 0 Aug 28 09:06 cpuacct.usage
-r--r--r-- 1 root root 0 Aug 28 09:06 cpuacct.usage_percpu
-rw-r--r-- 1 root root 0 Aug 28 09:06 cpuset.cpu_exclusive
-rw-r--r-- 1 root root 0 Aug 28 09:06 cpuset.cpus
-rw-r--r-- 1 root root 0 Aug 28 09:06 cpuset.mem_exclusive
-rw-r--r-- 1 root root 0 Aug 28 09:06 cpuset.mem_hardwall
-rw-r--r-- 1 root root 0 Aug 28 09:06 cpuset.memory_migrate
-r--r--r-- 1 root root 0 Aug 28 09:06 cpuset.memory_pressure
-rw-r--r-- 1 root root 0 Aug 28 09:06 cpuset.memory_pressure_enabled
-rw-r--r-- 1 root root 0 Aug 28 09:06 cpuset.memory_spread_page
-rw-r--r-- 1 root root 0 Aug 28 09:06 cpuset.memory_spread_slab
-rw-r--r-- 1 root root 0 Aug 28 09:06 cpuset.mems
-rw-r--r-- 1 root root 0 Aug 28 09:06 cpuset.sched_load_balance
-rw-r--r-- 1 root root 0 Aug 28 09:06 cpuset.sched_relax_domain_level
-rw-r--r-- 1 root root 0 Aug 28 09:06 cpu.shares
--w------- 1 root root 0 Aug 28 09:06 devices.allow
--w------- 1 root root 0 Aug 28 09:06 devices.deny
-r--r--r-- 1 root root 0 Aug 28 09:06 devices.list
-rw-r--r-- 1 root root 0 Aug 28 09:06 net_cls.classid
-rw-r--r-- 1 root root 0 Aug 28 09:06 notify_on_release
-rw-r--r-- 1 root root 0 Aug 28 09:06 release_agent
-rw-r--r-- 1 root root 0 Aug 28 09:06 tasks
果然, 隻有cpu.share, 沒有 cpu.sched_rt_period_us 和 cpu.sched_rt_runtime_us
沒辦法, 重新編譯核心, 編譯核心的具體方法參見: 編譯Linux核心
為了節約時間, 我們用 make localmodconfig 來建立 .config 檔案, 然後修改其中的 CONFIG_RT_GROUP_SCHED=y
下載下傳源碼等等參見: 編譯Linux核心, 主要步驟如下:
cd /path/to/linux-source-3.2
make localmodconfig
vim .config # 設定 CONFIG_RT_GROUP_SCHED=y 并儲存
make
make modules_install
make install
reboot # 重新開機之前看看 /boot/grub/grub.cfg 中, 預設啟動的是不是新安裝的核心
啟動到新核心, 再次檢視核心選項 CONFIG_RT_GROUP_SCHED 是否啟用
cat /boot/config-`uname -r` | grep -i rt_group
CONFIG_RT_GROUP_SCHED=y # 已啟用
再次挂載 cgroup 檔案系統, 發現多了2個配置檔案, cpu.rt_period_us 和 cpu.rt_runtime_us
mount -t cgroup cgroup /mnt/cgroup/
cd /mnt/cgroup/
ls -l
total 0
-r--r--r-- 1 root root 0 Aug 28 09:53 blkio.io_merged
-r--r--r-- 1 root root 0 Aug 28 09:53 blkio.io_queued
-r--r--r-- 1 root root 0 Aug 28 09:53 blkio.io_service_bytes
-r--r--r-- 1 root root 0 Aug 28 09:53 blkio.io_serviced
-r--r--r-- 1 root root 0 Aug 28 09:53 blkio.io_service_time
-r--r--r-- 1 root root 0 Aug 28 09:53 blkio.io_wait_time
--w------- 1 root root 0 Aug 28 09:53 blkio.reset_stats
-r--r--r-- 1 root root 0 Aug 28 09:53 blkio.sectors
-r--r--r-- 1 root root 0 Aug 28 09:53 blkio.time
-rw-r--r-- 1 root root 0 Aug 28 09:53 blkio.weight
-rw-r--r-- 1 root root 0 Aug 28 09:53 blkio.weight_device
-rw-r--r-- 1 root root 0 Aug 28 09:53 cgroup.clone_children
--w--w--w- 1 root root 0 Aug 28 09:53 cgroup.event_control
-rw-r--r-- 1 root root 0 Aug 28 09:53 cgroup.procs
-r--r--r-- 1 root root 0 Aug 28 09:53 cpuacct.stat
-rw-r--r-- 1 root root 0 Aug 28 09:53 cpuacct.usage
-r--r--r-- 1 root root 0 Aug 28 09:53 cpuacct.usage_percpu
-rw-r--r-- 1 root root 0 Aug 28 09:53 cpu.rt_period_us
-rw-r--r-- 1 root root 0 Aug 28 09:53 cpu.rt_runtime_us
-rw-r--r-- 1 root root 0 Aug 28 09:53 cpuset.cpu_exclusive
-rw-r--r-- 1 root root 0 Aug 28 09:53 cpuset.cpus
-rw-r--r-- 1 root root 0 Aug 28 09:53 cpuset.mem_exclusive
-rw-r--r-- 1 root root 0 Aug 28 09:53 cpuset.mem_hardwall
-rw-r--r-- 1 root root 0 Aug 28 09:53 cpuset.memory_migrate
-r--r--r-- 1 root root 0 Aug 28 09:53 cpuset.memory_pressure
-rw-r--r-- 1 root root 0 Aug 28 09:53 cpuset.memory_pressure_enabled
-rw-r--r-- 1 root root 0 Aug 28 09:53 cpuset.memory_spread_page
-rw-r--r-- 1 root root 0 Aug 28 09:53 cpuset.memory_spread_slab
-rw-r--r-- 1 root root 0 Aug 28 09:53 cpuset.mems
-rw-r--r-- 1 root root 0 Aug 28 09:53 cpuset.sched_load_balance
-rw-r--r-- 1 root root 0 Aug 28 09:53 cpuset.sched_relax_domain_level
-rw-r--r-- 1 root root 0 Aug 28 09:53 cpu.shares
--w------- 1 root root 0 Aug 28 09:53 devices.allow
--w------- 1 root root 0 Aug 28 09:53 devices.deny
-r--r--r-- 1 root root 0 Aug 28 09:53 devices.list
-rw-r--r-- 1 root root 0 Aug 28 09:53 net_cls.classid
-rw-r--r-- 1 root root 0 Aug 28 09:53 notify_on_release
-rw-r--r-- 1 root root 0 Aug 28 09:53 release_agent
-rw-r--r-- 1 root root 0 Aug 28 09:53 tasks
cat cpu.rt_period_us
1000000
cat cpu.rt_runtime_us
950000
通過配置 cpu.rt_period_us 和 cpu.rt_runtime_us 就可以對 cgroup 中的程序組中的實時程序進行 CPU使用時間的控制.
資源控制執行個體
上面主要介紹資源的一些理論基礎, 下面通過一些執行個體示範如果通過 cgroup 來控制程序所使用的 CPU和記憶體 資源.
Linux對CPU 和 記憶體的控制有對應的 cgroup 子系統 cpuset 和 memory
執行個體: cgroup 中對其中 *子cgroup* 的CPU資源控制
對各個 *子cgroup* 的CPU占用率進行控制主要依靠每個 *子cgroup* 的 cpu.shares 檔案
直接用實驗過程來說話, 其中加入了一些注釋.
# 安裝需要的軟體
apt-get install stress # 讓CPU達到 100% 的壓力工具
apt-get install sysstat # 檢視系統CPU, 記憶體, 磁盤, 網絡等資源使用情況的工具
執行個體1 - 預設情況, A 和 B 各占CPU總資源的 1/2
- 挂載 cgroup 檔案系統 (注意加上 -o cpu 的選項)
- 在 cgroup中建立 2個子cgroup A 和 B
- 預設情況下, cgroup A 和 cgroup B 中的 cpu.shares 中的數值都是 1024
- 在 A 和 B 中用 stress 工具使其 CPU占用率達到 100%
- top 指令檢視 A 和 B 中程序分别占用的 CPU (應該都是 50%)
# 挂載 cgroup 檔案系統
mount -t cgroup -o cpu cgroup /mnt/cgroup/
cd /mnt/cgroup
ls -l
total 0
-r--r--r-- 1 root root 0 Aug 28 11:29 blkio.io_merged
-r--r--r-- 1 root root 0 Aug 28 11:29 blkio.io_queued
-r--r--r-- 1 root root 0 Aug 28 11:29 blkio.io_service_bytes
-r--r--r-- 1 root root 0 Aug 28 11:29 blkio.io_serviced
-r--r--r-- 1 root root 0 Aug 28 11:29 blkio.io_service_time
-r--r--r-- 1 root root 0 Aug 28 11:29 blkio.io_wait_time
--w------- 1 root root 0 Aug 28 11:29 blkio.reset_stats
-r--r--r-- 1 root root 0 Aug 28 11:29 blkio.sectors
-r--r--r-- 1 root root 0 Aug 28 11:29 blkio.time
-rw-r--r-- 1 root root 0 Aug 28 11:29 blkio.weight
-rw-r--r-- 1 root root 0 Aug 28 11:29 blkio.weight_device
-rw-r--r-- 1 root root 0 Aug 28 11:29 cgroup.clone_children
--w--w--w- 1 root root 0 Aug 28 11:29 cgroup.event_control
-rw-r--r-- 1 root root 0 Aug 28 11:29 cgroup.procs
-r--r--r-- 1 root root 0 Aug 28 11:29 cpuacct.stat
-rw-r--r-- 1 root root 0 Aug 28 11:29 cpuacct.usage
-r--r--r-- 1 root root 0 Aug 28 11:29 cpuacct.usage_percpu
-rw-r--r-- 1 root root 0 Aug 28 11:29 cpuset.cpu_exclusive
-rw-r--r-- 1 root root 0 Aug 28 11:29 cpuset.cpus
-rw-r--r-- 1 root root 0 Aug 28 11:29 cpuset.mem_exclusive
-rw-r--r-- 1 root root 0 Aug 28 11:29 cpuset.mem_hardwall
-rw-r--r-- 1 root root 0 Aug 28 11:29 cpuset.memory_migrate
-r--r--r-- 1 root root 0 Aug 28 11:29 cpuset.memory_pressure
-rw-r--r-- 1 root root 0 Aug 28 11:29 cpuset.memory_pressure_enabled
-rw-r--r-- 1 root root 0 Aug 28 11:29 cpuset.memory_spread_page
-rw-r--r-- 1 root root 0 Aug 28 11:29 cpuset.memory_spread_slab
-rw-r--r-- 1 root root 0 Aug 28 11:29 cpuset.mems
-rw-r--r-- 1 root root 0 Aug 28 11:29 cpuset.sched_load_balance
-rw-r--r-- 1 root root 0 Aug 28 11:29 cpuset.sched_relax_domain_level
-rw-r--r-- 1 root root 0 Aug 28 11:29 cpu.shares
--w------- 1 root root 0 Aug 28 11:29 devices.allow
--w------- 1 root root 0 Aug 28 11:29 devices.deny
-r--r--r-- 1 root root 0 Aug 28 11:29 devices.list
-rw-r--r-- 1 root root 0 Aug 28 11:29 net_cls.classid
-rw-r--r-- 1 root root 0 Aug 28 11:29 notify_on_release
-rw-r--r-- 1 root root 0 Aug 28 11:29 release_agent
-rw-r--r-- 1 root root 0 Aug 28 11:29 tasks
# 建立 子cgroup A 和 B
mkdir {A,B}
cat A/cpu.shares
1024
cat B/cpu.shares
1024
# 在 A 和 B 中分别通過 stress 工具使其CPU使用率達到 100%
echo $$ > A/tasks # 将目前的 SHELL 加入到 cgroup A中
stress -c 2 # 這裡-c 2 是因為測試機器是雙核, 要在2個核上都産生 100% 的CPU 占用率
# 另外打開一個 shell 視窗, 并将這個shell 加入到 cgroup B中
echo $$ > B/tasks # 将目前的 SHELL 加入到 cgroup B中
stress -c 2 # 在2個核上都産生 100% 的CPU 占用率
# 再打開一個 shell 視窗, 用top指令檢視 CPU占用情況
top
top - 14:10:32 up 43 min, 3 users, load average: 2.31, 1.24, 0.62
Tasks: 78 total, 5 running, 73 sleeping, 0 stopped, 0 zombie
%Cpu(s):100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem: 1887872 total, 114744 used, 1773128 free, 10472 buffers
KiB Swap: 3982332 total, 0 used, 3982332 free, 45068 cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3350 root 20 0 6524 92 0 R 49.9 0.0 0:08.73 stress
3351 root 20 0 6524 92 0 R 49.9 0.0 0:08.67 stress
3353 root 20 0 6524 92 0 R 49.9 0.0 0:07.35 stress
3354 root 20 0 6524 92 0 R 49.9 0.0 0:07.36 stress
# 檢視這 4 個stress 程序是否分别屬于 A 和 B
cat /mnt/cgroup/A/tasks
2945
3349
3350 <-- stress 程序
3351 <-- stress 程序
cat /mnt/cgroup/B/tasks
2996
3352
3353 <-- stress 程序
3354 <-- stress 程序
可以看出, A和B組中的 2個stress 程序的CPU使用率相加都是 100%,
由于我測試的電腦是雙核, top所看到的CPU最大使用率是 200%, 是以和預期一緻, A和B組各占CPU總資源的 1/2
執行個體2 - A group 占用整體CPU資源的 2/3, B group 占用整體CPU資源的 1/3
- 環境同 執行個體1, 不再重新挂載 cgroup 檔案系統, 也不在重建 A 和 B
- A group 的 cpu.shares 檔案不變, 值為 1024
- B group 的 cpu.shares 檔案中的值改為 512, 這樣, 相當于B占用CPU總資源的 1/3 (因為 512 / (512+1024) = 1/3)
- 同執行個體1, 通過2個shell視窗, 分别是 A 和 B 的CPU使用率達到 100%, 然後通過 top 檢視CPU使用情況
# 在 B 中shell 視窗執行以下指令
cat B/cpu.shares
1024
echo 512 > B/cpu.shares
cat B/cpu.shares
512
stress -c 2
# 在 A 中 shell 視窗執行以下指令
stress -c 2
# 在第3個 shell 視窗, 也就是 非A, 非B 的那個 shell 視窗, 用 top 檢視cpu使用情況
top
top - 14:13:18 up 46 min, 3 users, load average: 2.24, 1.92, 1.01
Tasks: 78 total, 5 running, 73 sleeping, 0 stopped, 0 zombie
%Cpu(s):100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem: 1887872 total, 114744 used, 1773128 free, 10488 buffers
KiB Swap: 3982332 total, 0 used, 3982332 free, 45068 cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3376 root 20 0 6524 88 0 R 66.6 0.0 0:06.29 stress
3377 root 20 0 6524 88 0 R 66.6 0.0 0:06.30 stress
3373 root 20 0 6524 88 0 R 33.3 0.0 0:04.33 stress
3374 root 20 0 6524 88 0 R 33.3 0.0 0:04.32 stress
# 檢視這 4 個stress 程序是否分别屬于 A 和 B
cat /mnt/cgroup/A/tasks
2945
3375
3376 <-- stress 程序
3377 <-- stress 程序
cat /mnt/cgroup/B/tasks
2996
3372
3373 <-- stress 程序
3374 <-- stress 程序
很明顯, A 組中的2個程序占用了CPU總量的 2/3 左右, B組中的2個程序占用了CPU總量的 1/3 左右.
執行個體3 - 實體CPU的控制
上面的執行個體中, 雖然能夠控制每個組的CPU的總體占用率, 但是不能控制某個組的程序固定在某個實體CPU上運作.
要想将 cgroup 綁定到某個固定的CPU上, 需要使用 cpuset 子系統.
首先, 檢視系統是否支援 cpuset 子系統, 也就是看核心編譯選項 CONFIG_CPUSETS 是否設為y
cat /boot/config-`uname -r` | grep -i cpusets
CONFIG_CPUSETS=y
我的測試系統是支援的, 如果你的系統不支援, 就需要重新編譯核心了.......
然後, 用下面的例子示範将 A 和 B中的 stress 都指定到1個CPU上後的情況
- 解除安裝目前的 cgroup
- 再次挂載 cgroup 檔案系統, 并指定 -o cpuset
- 指定 A 的實體CPU為 0 (雙核CPU的每個核編号分别是 CPU0, CPU1)
- 指定 B 的實體CPU也為 0
- 重複 執行個體1 中的步驟, 觀察發生的變化
umount /mnt/cgroup
mount -t cgroup -o cpuset cgroup /mnt/cgroup/
cd /mnt/cgroup
ls -l
total 0
-rw-r--r-- 1 root root 0 Aug 28 14:39 cgroup.clone_children
--w--w--w- 1 root root 0 Aug 28 14:39 cgroup.event_control
-rw-r--r-- 1 root root 0 Aug 28 14:39 cgroup.procs
-rw-r--r-- 1 root root 0 Aug 28 14:39 cpuset.cpu_exclusive
-rw-r--r-- 1 root root 0 Aug 28 14:39 cpuset.cpus <-- 這個就是設定關聯實體CPU的檔案
-rw-r--r-- 1 root root 0 Aug 28 14:39 cpuset.mem_exclusive
-rw-r--r-- 1 root root 0 Aug 28 14:39 cpuset.mem_hardwall
-rw-r--r-- 1 root root 0 Aug 28 14:39 cpuset.memory_migrate
-r--r--r-- 1 root root 0 Aug 28 14:39 cpuset.memory_pressure
-rw-r--r-- 1 root root 0 Aug 28 14:39 cpuset.memory_pressure_enabled
-rw-r--r-- 1 root root 0 Aug 28 14:39 cpuset.memory_spread_page
-rw-r--r-- 1 root root 0 Aug 28 14:39 cpuset.memory_spread_slab
-rw-r--r-- 1 root root 0 Aug 28 14:39 cpuset.mems
-rw-r--r-- 1 root root 0 Aug 28 14:39 cpuset.sched_load_balance
-rw-r--r-- 1 root root 0 Aug 28 14:39 cpuset.sched_relax_domain_level
-rw-r--r-- 1 root root 0 Aug 28 14:39 notify_on_release
-rw-r--r-- 1 root root 0 Aug 28 14:39 release_agent
-rw-r--r-- 1 root root 0 Aug 28 14:39 tasks
# 建立子cgroup A 和 B
mkdir {A,B}
cat A/cpuset.cpus
<-- 預設是空的
echo 0 > A/cpuset.cpus
cat A/cpuset.cpus
0
echo 0 > B/cpuset.cpus # 同樣, 設定B組也綁定到CPU0
# 目前Shell加入到 A組
echo $$ > /mnt/cgroup/A/tasks
-bash: echo: write error: No space left on device
如果出現上述錯誤, 隻需要再設定 /mnt/cgroup/A/cpuset.mems 即可. (參考: http://serverfault.com/questions/579555/cgroup-no-space-left-on-device)
# 同時設定 A 的 cpuset.cpus 和 cpuset.mems
echo 0 > A/cpuset.cpus
echo 0 > A/cpuset.mems
# B組也同樣設定
echo 0 > B/cpuset.cpus
echo 0 > B/cpuset.mems
# 将目前 shell 加入到 A組
echo $$ > /mnt/cgroup/A/tasks <-- 設定過 cpuset.mems 後, 就沒有出錯了
stress -c 2
# 再打開一個Shell視窗, 并加入到 B組
echo $$ > /mnt/cgroup/B/tasks
stress -c 2
# 再打開第3個 shell 視窗, 用top指令檢視CPU使用情況
top
top - 15:13:29 up 1:46, 3 users, load average: 1.01, 0.24, 0.12
Tasks: 78 total, 5 running, 73 sleeping, 0 stopped, 0 zombie
%Cpu(s): 50.0 us, 0.0 sy, 0.0 ni, 50.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem: 1887872 total, 117216 used, 1770656 free, 11144 buffers
KiB Swap: 3982332 total, 0 used, 3982332 free, 47088 cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3830 root 20 0 6524 92 0 R 25.0 0.0 0:04.96 stress
3831 root 20 0 6524 92 0 R 25.0 0.0 0:04.97 stress
3834 root 20 0 6524 92 0 R 25.0 0.0 0:03.56 stress
3833 root 20 0 6524 92 0 R 24.6 0.0 0:03.56 stress
從上面的結果可以看出, 雖然 stress 指令指定了 -c 2(意思是在2個CPU上運作), 但是由于A和B都隻綁定了CPU0,
是以雖然是雙核的機器, 它們所占用的CPU總量卻隻有 100%, 而不是執行個體1 中的 200%.
如果将B組的實體CPU綁定到CPU1, 那麼應該所有 stress 的程序都占用 50%, CPU資源的總量變為 200%.
下面将B組的實體CPU綁定為CPU1, 看看結果是否和我們的預期一樣.
# 在 B組的 shell 視窗中執行以下指令
echo 1 > /mnt/cgroup/B/cpuset.cpus
cat /mnt/cgroup/B/cpuset.cpus
1
stress -c 2
# 在 A組的 shell 視窗中執行以下指令
stress -c 2
# 在第3個shell視窗中用top指令檢視執行結果
top
top - 15:20:07 up 1:53, 3 users, load average: 0.38, 0.83, 0.56
Tasks: 78 total, 5 running, 73 sleeping, 0 stopped, 0 zombie
%Cpu(s):100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem: 1887872 total, 117340 used, 1770532 free, 11168 buffers
KiB Swap: 3982332 total, 0 used, 3982332 free, 47088 cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3854 root 20 0 6524 88 0 R 49.9 0.0 0:03.76 stress
3857 root 20 0 6524 92 0 R 49.9 0.0 0:02.29 stress
3858 root 20 0 6524 92 0 R 49.9 0.0 0:02.29 stress
3855 root 20 0 6524 88 0 R 49.6 0.0 0:03.76 stress
果然, 和預期一緻. A組中的 stress 和 B組中的 stress 在各自的實體CPU上都占用了 100% 左右的CPU使用率.
執行個體4 - cgroup 對使用的記憶體的控制
cgroup 對記憶體的控制也很簡單, 隻要挂載cgroup時, 指定 -o memory
# 首先之前挂載的 cpuset 子系統
umount /mnt/cgroup
# 挂載cgroup 檔案系統, 指定 -o memeory
mount -o memory -t cgroup memcg /mnt/cgroup/
mount: special device memcg does not exist
出現以上錯誤的原因可能是因為debian系統中, 預設沒有啟動 cgroup 的memory子系統. 可以通過以下方法确認:
cat /proc/cgroups
#subsys_name hierarchy num_cgroups enabled
cpuset 0 1 1
cpu 0 1 1
cpuacct 0 1 1
memory 1 1 0 <-- 這裡的 enabled 是 0
devices 0 1 1
freezer 0 1 1
net_cls 0 1 1
blkio 0 1 1
perf_event 0 1 1
為了預設啟用memory子系統, 可以設定 grub選項
vim /etc/default/grub
# 修改 GRUB_CMDLINE_LINUX="" ==> GRUB_CMDLINE_LINUX="cgroup_enable=memory"
# 儲存後, 更新grub.cfg
update-grub
reboot
重新開機之後, 發現 /proc/cgroups 中的memory已經 enabled, 并且也可以挂載 memcg了
cat /proc/cgroups
#subsys_name hierarchy num_cgroups enabled
cpuset 0 1 1
cpu 0 1 1
cpuacct 0 1 1
memory 1 1 1
devices 0 1 1
freezer 0 1 1
net_cls 0 1 1
blkio 0 1 1
perf_event 0 1 1
# 挂載cgroup 的memory子系統
mount -t cgroup -o memory memcg /mnt/cgroup
ls -l /mnt/cgroup/ <-- 可以看到有很多 memory 相關的配置
total 0
-rw-r--r-- 1 root root 0 Aug 28 15:54 cgroup.clone_children
--w--w--w- 1 root root 0 Aug 28 15:54 cgroup.event_control
-rw-r--r-- 1 root root 0 Aug 28 15:54 cgroup.procs
-rw-r--r-- 1 root root 0 Aug 28 15:54 memory.failcnt
--w------- 1 root root 0 Aug 28 15:54 memory.force_empty
-rw-r--r-- 1 root root 0 Aug 28 15:54 memory.limit_in_bytes <-- 限制記憶體使用的配置檔案
-rw-r--r-- 1 root root 0 Aug 28 15:54 memory.max_usage_in_bytes
-rw-r--r-- 1 root root 0 Aug 28 15:54 memory.move_charge_at_immigrate
-r--r--r-- 1 root root 0 Aug 28 15:54 memory.numa_stat
-rw-r--r-- 1 root root 0 Aug 28 15:54 memory.oom_control
-rw-r--r-- 1 root root 0 Aug 28 15:54 memory.soft_limit_in_bytes
-r--r--r-- 1 root root 0 Aug 28 15:54 memory.stat
-rw-r--r-- 1 root root 0 Aug 28 15:54 memory.swappiness
-r--r--r-- 1 root root 0 Aug 28 15:54 memory.usage_in_bytes
-rw-r--r-- 1 root root 0 Aug 28 15:54 memory.use_hierarchy
-rw-r--r-- 1 root root 0 Aug 28 15:54 notify_on_release
-rw-r--r-- 1 root root 0 Aug 28 15:54 release_agent
-rw-r--r-- 1 root root 0 Aug 28 15:54 tasks
開始實驗:
- 重新開機系統 (為了保證記憶體的幹淨)
- 挂載 memcg
- 在挂載的 /mnt/cgroup 中建立 組A
- 将目前shell 加入到 組A
- 不限制組A的記憶體, 壓縮核心源碼包, 并觀察壓縮前後記憶體的變化
- 重複步驟 1 ~ 4
- 限制組A的記憶體為 10MB, 再次壓縮核心源碼包, 并觀察壓縮前後記憶體的變化
# 重新開機系統
reboot
# 挂載 memcg
mount -t cgroup -o memory memcg /mnt/cgroup
# 建立 組A
mkdir /mnt/cgroup/A
# 将目前 shell 加入到組A
echo $$ > /mnt/cgroup/A/tasks
# 測試不限制記憶體時, 記憶體的使用情況, 這裡不用linux源碼也可以, 但最好用個大點的檔案夾來壓縮, 以便更容易看出記憶體的變化.
free -m; tar czvf linux-source-3.2.tar.gz /path/to/linux-source-3.2/ > /dev/null; free -m;
total used free shared buffers cached
Mem: 1843 122 1721 0 9 43
-/+ buffers/cache: 68 1774
Swap: 3888 0 3888
total used free shared buffers cached
Mem: 1843 1744 99 0 26 1614
-/+ buffers/cache: 104 1739
Swap: 3888 0 3888
# 重新開機系統
reboot
# 挂載 memcg
mount -t cgroup -o memory memcg /mnt/cgroup
# 建立 組A
mkdir /mnt/cgroup/A
# 将目前 shell 加入到組A
echo $$ > /mnt/cgroup/A/tasks
# 限制 組A 的記憶體使用量最大為 10MB
echo 10M > /mnt/cgroup/A/memory.limit_in_bytes
# 測試限制記憶體為 10MB 時, 記憶體的使用情況.
rm -rf linux-source-3.2.tar.gz
free -m; tar czvf linux-source-3.2.tar.gz /path/to/linux-source-3.2/ > /dev/null; free -m;
total used free shared buffers cached
Mem: 1843 122 1721 0 10 43
-/+ buffers/cache: 68 1774
Swap: 3888 0 3888
total used free shared buffers cached
Mem: 1843 194 1649 0 14 48
-/+ buffers/cache: 131 1712
Swap: 3888 0 3888
從上面的結果可以看出限制記憶體是起了作用的.
不限制記憶體時, tar 壓縮前後 buffer + cache 記憶體從 (9MB + 43MB) ==> (26MB + 1614MB) 增大了 1588MB
限制記憶體後, tar 壓縮前後 buffer + cache 記憶體從 (10MB + 43MB) ==> (14MB + 48MB) 增大了 9MB
總結
簡單的實驗就發現 cgroup 如此強大的控制能力(而且配置也很簡單), 這也就難怪LXC等容器技術能如此強大, 如此流行.
cgroup 的配置檔案很多, 上面的執行個體中隻簡單使用了其中的幾個配置檔案, 如果想深入了解 cgroup, 更好的利用cgroup的話,
還得找個介紹cgroup配置檔案的文檔來研究一下, 這篇部落格提供的内容還遠遠不夠.