Linux資源控制-使用cgroup控制CPU和記憶體
原文地址:http://www.cnblogs.com/wang_yb/p/3942208.html
主要介紹Linux下, 如果對程序的CPU和記憶體資源的使用情況進行控制的方法。
CPU資源控制
每個程序能夠佔用CPU多長時間, 什麼時候能夠佔用CPU是和系統的排程密切相關的.
Linux系統中有多種排程策略, 各種排程策略有其適用的場景, 也很難說哪種排程策略是最優的.
Linux的排程策略可以參見程式碼: include/linux/sched.h
/* * Scheduling policies */ #define SCHED_NORMAL 0 #define SCHED_FIFO 1 #defineSCHED_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 ... -rw-r--r-- 1 root root 0 Aug 28 09:06 cpu.shares ...
果然, 只有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 ... -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 cpu.shares ...
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
# 建立 子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 cpuset.cpu_exclusive -rw-r--r-- 1 root root 0 Aug 28 14:39 cpuset.cpus <-- 這個就是設定關聯物理CPU的檔案 ... # 建立子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
# 同時設定 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