一)中断的CPU亲和性
我们可以调整中断到某个CPU上,这样可以让CPU更有效的利用起来.
首先关闭掉irqbalance服务,如下:
/etc/init.d/irqbalance stop
Stopping irqbalance: [ OK ]
查看当前各种中断所使用的CPU,如下:
for f in `find . -name "smp_affinity"`; do echo -ne "$f->" && cat $f; done
./18/smp_affinity->1
./17/smp_affinity->3
./15/smp_affinity->1
./14/smp_affinity->1
./13/smp_affinity->3
./12/smp_affinity->1
./11/smp_affinity->3
./10/smp_affinity->3
./9/smp_affinity->3
./8/smp_affinity->3
./7/smp_affinity->3
./6/smp_affinity->3
./5/smp_affinity->3
./4/smp_affinity->3
./3/smp_affinity->3
./2/smp_affinity->3
./1/smp_affinity->3
./0/smp_affinity->3
查看中断使用率,如下:
cat /proc/interrupts
CPU0 CPU1
0: 137 0 IO-APIC-edge timer
1: 7 1 IO-APIC-edge i8042
3: 0 1 IO-APIC-edge
4: 1 0 IO-APIC-edge
7: 0 0 IO-APIC-edge parport0
8: 0 0 IO-APIC-edge rtc0
9: 0 0 IO-APIC-fasteoi acpi
12: 103 2 IO-APIC-edge i8042
14: 2301 628 IO-APIC-edge ata_piix
15: 116 49 IO-APIC-edge ata_piix
17: 15 0 IO-APIC-fasteoi ioc0
18: 4122 39 IO-APIC-fasteoi eth0
NMI: 0 0 Non-maskable interrupts
LOC: 18423 16772 Local timer interrupts
SPU: 0 0 Spurious interrupts
PMI: 0 0 Performance monitoring interrupts
PND: 0 0 Performance pending work
RES: 2740 2914 Rescheduling interrupts
CAL: 110 1349 Function call interrupts
TLB: 339 421 TLB shootdowns
TRM: 0 0 Thermal event interrupts
THR: 0 0 Threshold APIC interrupts
MCE: 0 0 Machine check exceptions
MCP: 3 3 Machine check polls
ERR: 0
MIS: 0
我们看一下18号中断下的相关文件,如下:
cd /proc/irq/18/ && ls
affinity_hint eth0 node smp_affinity spurious
我们看下它使用的CPU.
more smp_affinity
1
注:这里输出为1表示,它使用了第一个CPU,1对映的两进制掩码也是1.
如果它使用前两个CPU,那么这里就是3,即二进制的11.
如果我们有4个处理器,这里的值就应该是F,也就是二进制1111.
如果我们只要第2个处理器进行处理,这里的值就应该是2,因为2的二进制是10.
我们在另一台服务器ping本机,如下:
ping 192.168.75.135
64 bytes from 192.168.75.135: icmp_seq=3454 ttl=64 time=2.80 ms
64 bytes from 192.168.75.135: icmp_seq=3455 ttl=64 time=3.80 ms
64 bytes from 192.168.75.135: icmp_seq=3456 ttl=64 time=0.814 ms
64 bytes from 192.168.75.135: icmp_seq=3457 ttl=64 time=0.293 ms
64 bytes from 192.168.75.135: icmp_seq=3458 ttl=64 time=1.84 ms
64 bytes from 192.168.75.135: icmp_seq=3459 ttl=64 time=0.265 ms
64 bytes from 192.168.75.135: icmp_seq=3460 ttl=64 time=0.021 ms
64 bytes from 192.168.75.135: icmp_seq=3461 ttl=64 time=0.793 ms
64 bytes from 192.168.75.135: icmp_seq=3462 ttl=64 time=0.285 ms
64 bytes from 192.168.75.135: icmp_seq=3463 ttl=64 time=0.038 ms
64 bytes from 192.168.75.135: icmp_seq=3464 ttl=64 time=0.936 ms
64 bytes from 192.168.75.135: icmp_seq=3465 ttl=64 time=0.279 ms
64 bytes from 192.168.75.135: icmp_seq=3466 ttl=64 time=0.706 ms
在本机监控网卡中断分布,如下:
while ((1)) ; do sleep 1; cat /proc/interrupts |grep eth0; done
18: 5568 39 IO-APIC-fasteoi eth0
18: 5570 39 IO-APIC-fasteoi eth0
18: 5576 39 IO-APIC-fasteoi eth0
18: 5580 39 IO-APIC-fasteoi eth0
18: 5584 39 IO-APIC-fasteoi eth0
18: 5590 39 IO-APIC-fasteoi eth0
18: 5592 39 IO-APIC-fasteoi eth0
18: 5598 39 IO-APIC-fasteoi eth0
18: 5604 39 IO-APIC-fasteoi eth0
18: 5606 39 IO-APIC-fasteoi eth0
18: 5612 39 IO-APIC-fasteoi eth0
18: 5616 39 IO-APIC-fasteoi eth0
18: 5620 39 IO-APIC-fasteoi eth0
18: 5626 39 IO-APIC-fasteoi eth0
18: 5628 39 IO-APIC-fasteoi eth0
18: 5634 39 IO-APIC-fasteoi eth0
18: 5638 39 IO-APIC-fasteoi eth0
18: 5641 39 IO-APIC-fasteoi eth0
18: 5647 39 IO-APIC-fasteoi eth0
18: 5650 39 IO-APIC-fasteoi eth0
18: 5656 39 IO-APIC-fasteoi eth0
我们看到当前的中断(第二列)即所有的网卡中断请求都分布到了CPU0.
我们这里指定前两个CPU做为处理网卡请求,如下:
echo "3" > smp_affinity
while ((1)) ; do sleep 1; cat /proc/interrupts |grep eth0; done
18: 6430 50 IO-APIC-fasteoi eth0
18: 6433 53 IO-APIC-fasteoi eth0
18: 6439 53 IO-APIC-fasteoi eth0
18: 6441 53 IO-APIC-fasteoi eth0
18: 6443 57 IO-APIC-fasteoi eth0
18: 6444 58 IO-APIC-fasteoi eth0
18: 6447 61 IO-APIC-fasteoi eth0
18: 6449 61 IO-APIC-fasteoi eth0
18: 6453 63 IO-APIC-fasteoi eth0
18: 6459 63 IO-APIC-fasteoi eth0
18: 6459 65 IO-APIC-fasteoi eth0
18: 6462 68 IO-APIC-fasteoi eth0
18: 6463 69 IO-APIC-fasteoi eth0
18: 6467 71 IO-APIC-fasteoi eth0
18: 6469 71 IO-APIC-fasteoi eth0
18: 6472 73 IO-APIC-fasteoi eth0
注:我们看到网卡的中断请求已经平均的分配到了两个CPU.
二)isolcpus
通过在grub中设定isolcpus内核参数可以指定哪几个CPU在系统中是孤立的,也就是说默认它们将不被使用.
测试如下:
编辑/boot/grub/menu.list
在加载内核的选项后加入isolcpus=0,如下:
kernel /boot/vmlinuz-2.6.32-71.el6.i686 ro root=UUID=96262e00-91a3-432d-b225-cb35d29eec8f rhgb quiet isolcpus=0
也就是说我们在启动系统时将默认不使用CPU0,注意这里说的默认不使用并不是绝对的,操作系统仍然可以指定使用哪个CPU.对于用户而言可以通过taskset来做到这点.
重启系统后,我们查看进程的亲和性,如下:
ps -eo pid,args:50,psr
PID COMMAND PSR
1 /sbin/init 1
2 [kthreadd] 0
3 [migration/0] 0
4 [ksoftirqd/0] 0
5 [watchdog/0] 0
6 [migration/1] 1
7 [ksoftirqd/1] 1
8 [watchdog/1] 1
9 [events/0] 0
10 [events/1] 1
11 [cpuset] 0
12 [khelper] 0
13 [netns] 0
14 [async/mgr] 0
15 [pm] 0
16 [sync_supers] 1
17 [bdi-default] 0
18 [kintegrityd/0] 0
19 [kintegrityd/1] 1
20 [kblockd/0] 0
21 [kblockd/1] 1
22 [kacpid] 0
23 [kacpi_notify] 0
24 [kacpi_hotplug] 0
25 [ata/0] 0
26 [ata/1] 1
27 [ata_aux] 0
28 [ksuspend_usbd] 0
29 [khubd] 0
30 [kseriod] 0
33 [khungtaskd] 0
34 [kswapd0] 0
35 [ksmd] 0
36 [aio/0] 0
37 [aio/1] 1
38 [crypto/0] 0
39 [crypto/1] 1
45 [kpsmoused] 0
46 [usbhid_resumer] 0
75 [kstriped] 0
239 [scsi_eh_0] 0
240 [scsi_eh_1] 0
250 [mpt_poll_0] 0
251 [mpt/0] 0
252 [scsi_eh_2] 0
301 [jbd2/sda1-8] 0
302 [ext4-dio-unwrit] 0
303 [ext4-dio-unwrit] 1
324 [flush-8:0] 0
390 /sbin/udevd -d 1
643 /sbin/udevd -d 1
644 /sbin/udevd -d 1
731 [kauditd] 0
1004 auditd 1
1029 /sbin/rsyslogd -c 4 1
1062 irqbalance 1
1081 rpcbind 1
1093 mdadm --monitor --scan -f --pid-file=/var/run/mdad 1
1102 dbus-daemon --system 1
1113 NetworkManager --pid-file=/var/run/NetworkManager/ 1
1117 /usr/sbin/modem-manager 1
1124 /sbin/dhclient -d -4 -sf /usr/libexec/nm-dhcp-clie 1
1129 /usr/sbin/wpa_supplicant -c /etc/wpa_supplicant/wp 1
1131 avahi-daemon: registering [linux.local] 1
1132 avahi-daemon: chroot helper 1
1149 rpc.statd 1
1186 [rpciod/0] 0
1187 [rpciod/1] 1
1194 rpc.idmapd 1
1204 cupsd -C /etc/cups/cupsd.conf 1
1229 /usr/sbin/acpid 1
1238 hald 1
1239 hald-runner 1
1279 hald-addon-input: Listening on /dev/input/event2 / 1
1283 hald-addon-acpi: listening on acpid socket /var/ru 1
1303 automount --pid-file /var/run/autofs.pid 1
1326 /usr/sbin/sshd 1
1456 /usr/libexec/postfix/master 1
1463 pickup -l -t fifo -u 1
1464 qmgr -l -t fifo -u 1
1467 /usr/sbin/abrtd 1
1475 crond 1
1486 /usr/sbin/atd 1
1497 libvirtd --daemon 1
1553 /sbin/mingetty /dev/tty1 1
1558 /sbin/mingetty /dev/tty2 1
1561 /sbin/mingetty /dev/tty3 1
1564 /sbin/mingetty /dev/tty4 1
1567 /sbin/mingetty /dev/tty5 1
1569 /sbin/mingetty /dev/tty6 1
1586 /usr/sbin/dnsmasq --strict-order --bind-interfaces 1
1598 sshd: root@pts/0 1
1603 -bash 1
我们看到有一些内核线程比如[kblockd/0]占用了CPU0,这是因为它指定了在CPU0上执行.其余的进程占用了CPU1.
我们这里用一个简单的循环程序测试一下:
#include <stdio.h>
int
main ()
{
while(1){
}
return 0;
}
gcc test.c
./a.out&
./a.out&
./a.out&
./a.out&
查看a.out进程的程序亲和性,如下:
ps -eo pid,args:50,psr |grep a.out
1669 ./a.out 1
1670 ./a.out 1
1671 ./a.out 1
1672 ./a.out 1
1675 grep a.out 1
我们看到4个a.out进程都使用了CPU1.这正是我们想看到的.
最后要说明的是如果使用isolcpus=1,则系统默认会使用CPU0提供服务.如果我们只有两个cpu,却指定isolcpus=0,1,这时将默认使用CPU0.
三)cpu的热插拔
在操作系统层面可以对cpu进行热插拔.
动态关闭cpu1,如下:
echo "0" > /sys/devices/system/cpu/cpu1/online
此时我们在系统中,只能看到1个CPU了.
cat /proc/cpuinfo
processor : 0
vendor_id : AuthenticAMD
cpu family : 15
model : 107
model name : AMD Sempron(tm) Dual Core Processor 2300
stepping : 2
cpu MHz : 2210.053
cache size : 256 KB
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 1
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx mmxext fxsr_opt rdtscp lm 3dnowext 3dnow constant_tsc up tsc_reliable extd_apicid pni cx16 lahf_lm extapic 3dnowprefetch
bogomips : 4420.10
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management: ts fid vid ttp tm stc 100mhzsteps
注意,如果运行的程序跑在cpu1上,如果把cpu1关闭,则程序会迁移到cpu0上.另外cpu0是不可以被关闭的,在cpu0的sys文件系统中没有online文件.
四)进程/线程的亲和性和taskset的应用
1)概述:
1)CPU的亲和性分为软亲合性和硬亲合性,软亲合性是使进程并不会在处理器之间频繁迁移.硬亲合性是使进程需要在您指定的处理器上运行,Linux默认是软亲合性的,所以Linux会试图保持进程在相同的CPU上运行,因为这样再次应用TLB将成为可能.
2)Linux系统通过亲和性掩码使应用程序使用哪个CPU来运行程序,Linux默认的亲合性掩码是使用所有的CPU.
3)应用程序可以在启动时指定亲和性掩码,提交给调度系统,也可以在应用程序运行中调整它的亲和性掩码.
2)用taskset手工调整一个进程的亲和性
我们先弄一个最简单的程序,它是一个死循环,如下:
#include <stdio.h>
int
main ()
{
while(1){
}
return 0;
}
编译并运行:
gcc taskloop.c -o taskloop
./taskloop
我们在另一个终端查看该进程的状态,如下:
ps -eo pid,args:30,psr
PID COMMAND PSR
2826 ./taskloop 0
注:上面的ps输出,我们只保留了taskloop一行,PSR代表我们的程序用了哪个CPU,如果有两个CPU,就分别CPU0,CPU1,也就是从0开始.
我们中止这个程序,用taskset来指定它用CPU1来运行,如下:
taskset 2 ./taskloop
我们在另一个终端查看该进程的状态,如下:
ps -eo pid,args:30,psr
PID COMMAND PSR
2892 ./taskloop 1
注:通过taskset对亲合性掩码的设定,我们选择了CPU1来运行这个程序,这里要说明的是taskset指定的掩码是从1开始计算的,我们指定用CPU1,就得用taskset 2 COMMAND来设定.
下面是亲合性掩码与CPU的对映,如下:
0x00000001是处理器1(CPU0)
0x00000002是处理器2(CPU1)
0x00000003是处理器1和处理器2(CPU0/CPU1)
0x00000004是处理器3(CPU2)
0x0000001F是前5个处理器(CPU0,CPU1,CPU2,CPU3,CPU4)
0xFFFFFFFF是所有的处理器(即32个处理器)
以此类推,上面是十六进制的掩码方式,在taskset中可以用十六进制和十进制两种方式,我们为了方便,在这里只用十进制来表示.
同样的我们也可以在程序运行中来改变它的亲合性掩码,从而改变它使用的CPU.
上面的taskloop进程使用了CPU1,我们将它改为使用CPU0,如下:
taskset -p 1 `pgrep taskloop`
pid 2892's current affinity mask: 2
pid 2892's new affinity mask: 1
在另一个终端查看该进程的状态:
ps -eo pid,args:30,psr
PID COMMAND PSR
2892 ./taskloop 0
注:我们看到原本运行于CPU1的进程改用了CPU0,这里要说明的是如果我们程序在sleep或pause,此时改变它的亲合性掩码但它不会选用新的CPU,只有当sleep或pause结束,它才会应用亲和性掩码.
3)进程中的亲合性设置
通过sched_setaffinity函数,可以设置CPU的亲和性,让指定的进程运行于指定的CPU上,我们把之前的程序做一下变动,如下:
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
int main(){
cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(1,&mask);
if(sched_setaffinity(0, sizeof(cpu_set_t), &mask)==-1)
printf("sched_setaffinity set error!");
while(1){
}
return 0;
}
编译并运行:
gcc proaffinity.c -o proaffinity
./proaffinity
在另一个终端查看该进程的状态:
ps -eo pid,args:20,psr|grep proaffinity|grep -v grep
3088 ./proaffinity 1
注:在程序中我们用sched_setaffinity函数,设定本程序用亲合性掩码1,也就是用第一个处理器来运行此程序.
sched_setaffinity(0, sizeof(cpu_set_t), &mask)函数中的第一个参数是PID,如果是0,则代表进程自己,第二个参数是亲合性掩码的长度,第三个参数是亲合性掩码.
CPU_ZERO(&mask)宏调用是清理掩码mask,CPU_SET(1,&mask)是设定掩码mask为1.
4)线程中的亲合性设置
通过pthread_attr_setaffinity_np函数,设定线程的亲合性掩码,也就是用第几个处理器来运行该线程,如下:
#define _GNU_SOURCE
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int GetCpuCount()
{
return (int)sysconf(_SC_NPROCESSORS_ONLN);
}
void *thread_fun()
{
int i;
while(1)
{
i = 0;
}
return NULL;
}
int main()
{
int cpu_num = 0;
cpu_num = GetCpuCount();
printf("The number of cpu is %d\n", cpu_num);
pthread_t t1;
pthread_t t2;
pthread_attr_t attr1;
pthread_attr_t attr2;
pthread_attr_init(&attr1);
pthread_attr_init(&attr2);
cpu_set_t cpu_info;
CPU_ZERO(&cpu_info);
CPU_SET(0, &cpu_info);
if (0!=pthread_attr_setaffinity_np(&attr1, sizeof(cpu_set_t), &cpu_info))
{
printf("set affinity failed");
return;
}
CPU_ZERO(&cpu_info);
CPU_SET(1, &cpu_info);
if (0!=pthread_attr_setaffinity_np(&attr2, sizeof(cpu_set_t), &cpu_info))
{
printf("set affinity failed");
}
if (0!=pthread_create(&t1, &attr1, thread_fun, NULL))
{
printf("create thread 1 error\n");
return;
}
if (0!=pthread_create(&t2, &attr2, thread_fun, NULL))
{
printf("create thread 2 error\n");
return;
}
pthread_join(t1, NULL);
pthread_join(t2, NULL);
}
编译并运行:
gcc pthraffinity.c -o pthraffinity -pthread
./proaffinity
我们在另一个终端查看该进程中所有线程的状态,如下:
ps -eLo pid,lwp,args:20,psr
PID LWP COMMAND PSR
3191 3191 ./pthraffinity 1
3191 3192 ./pthraffinity 0
3191 3193 ./pthraffinity 1
注:LWP一列为线程ID,我们看到进程(PID=3191)用了CPU0,而第一个线程(LWP=3192)用了CPU1,这正是程序中设定的,而第二个线程(LWP=3193)用了CPU0,这也是程序的意图.
五)cgroup
1)概述:
cgroup它的全称为control group.cgroup可以用于对一组进程分组,每一组进程就某种系统资源实现资源管理.
cgroup即一组进程的行为控制.比如,我们限制进程/bin/sh的CPU使用为20%.我们就可以建一个cpu占用为20%的cgroup.然后将 /bin/sh进程添加到这个cgroup中.
我们知道ulimit可以对系统资源进行限制,但ulimit以用户的资源为单位的,而cgroup可以针对到某个进程.
我们也可以指定某个进程使用某个cpu.或者让某些进程使用一组CPU.
cgroup包含了多个孤立的子系统,每一个子系统代表一个单一的资源,在新的内核中cgroup控制的模块有9个子系统,分别是:
blkio 这个子系统设置限制每个块设备的输入输出控制。例如:磁盘,光盘以及usb等等。
cpu 这个子系统使用调度程序为cgroup任务提供cpu的访问。
cpuacct 产生cgroup任务的cpu资源报告。
cpuset 如果是多核心的cpu,这个子系统会为cgroup任务分配单独的cpu和内存。
devices 允许或拒绝cgroup任务对设备的访问。
freezer 暂停和恢复cgroup任务。
memory 设置每个cgroup的内存限制以及产生内存资源报告。
net_cls 标记每个网络包以供cgroup方便使用。
ns 名称空间子系统。
有RHEL6中,有libcgroup软件包,里面包含了cgroup的相关服务程序,而且cgroup在RHEL6中也做为服务出现.
同时cpuset和cgroup也做为伪文件系统出现.如下:
grep cpuset /proc/filesystems
nodev cpuset
grep cgroup /proc/filesystems
nodev cgroup
2)我们可以通过cgroup中的cpuset子系统为进程指定CPU和内存.
首先创建一个用于挂载cpuset文件系统的目录,如下:
mkdir /cpusets
挂载cpuset文件系统以/cpusets目录
mount -t cpuset nodev /cpusets/
注:我们也可以用,mount -t cgroup none /cpusets/ -o cpuset
查看
ls -l /cpusets/
total 0
-r--r--r--. 1 root root 0 Oct 22 21:52 cgroup.procs
-rw-r--r--. 1 root root 0 Oct 22 21:52 cpuset.cpu_exclusive
-rw-r--r--. 1 root root 0 Oct 22 21:52 cpuset.cpus
-rw-r--r--. 1 root root 0 Oct 22 21:52 cpuset.mem_exclusive
-rw-r--r--. 1 root root 0 Oct 22 21:52 cpuset.mem_hardwall
-rw-r--r--. 1 root root 0 Oct 22 21:52 cpuset.memory_migrate
-r--r--r--. 1 root root 0 Oct 22 21:52 cpuset.memory_pressure
-rw-r--r--. 1 root root 0 Oct 22 21:52 cpuset.memory_pressure_enabled
-rw-r--r--. 1 root root 0 Oct 22 21:52 cpuset.memory_spread_page
-rw-r--r--. 1 root root 0 Oct 22 21:52 cpuset.memory_spread_slab
-rw-r--r--. 1 root root 0 Oct 22 21:52 cpuset.mems
-rw-r--r--. 1 root root 0 Oct 22 21:52 cpuset.sched_load_balance
-rw-r--r--. 1 root root 0 Oct 22 21:52 cpuset.sched_relax_domain_level
drwxr-xr-x. 3 root root 0 Oct 22 21:53 libvirt
-rw-r--r--. 1 root root 0 Oct 22 21:52 notify_on_release
-rw-r--r--. 1 root root 0 Oct 22 21:52 release_agent
-rw-r--r--. 1 root root 0 Oct 22 21:52 tasks
查看cpuset.cpus,这里显示的是0-1,说明默认情况下我们的系统可以使用0,1两个cpu.
more cpuset.cpus
0-1
查看cpuset.mems,这里显示的是0,说明默认情况下我们的系统可以使用结点0的内存系统,这里是单路控制,不是NUMA,所以只看到了0.
more cpuset.mems
0
查看有多少个进程在这个cgroup中.如下:
more tasks
1
2
3
4
5
9
11
略
这里我们再建一个cgroup/cpusets,将某些进程指定到这里,如下:
mkdir /cpusets/test
查看新cgroup
cd test/
[root@localhost test]# ls
cgroup.procs cpuset.mem_exclusive cpuset.memory_pressure cpuset.mems notify_on_release
cpuset.cpu_exclusive cpuset.mem_hardwall cpuset.memory_spread_page cpuset.sched_load_balance tasks
cpuset.cpus cpuset.memory_migrate cpuset.memory_spread_slab cpuset.sched_relax_domain_level
注:
我们看到新建的目录下面包含了cpusets文件系统所有的文件.另外新的cgroup下面的task是空的,cgroup.cpus/cgroup.mems也是空的.
这里指定新的cgroup/cpusets中的cpus为0,也就是在这个cgroup中的进程只能使用第一个CPU,如下:
echo 0 > cpuset.cpus
内存做同样的指定,如下:
echo 0 > cpuset.mems
我们这里运行一个测试程序a.out,如下:
/root/a.out &
[1] 1848
查看该进程使用了哪个cpu,如下:
ps -eo pid,args:20,psr|grep a.out
1848 /root/a.out 1
将该进程PID指定到tasks文件中,也不是指定到第一块cpu上.如下:
echo "1848" > tasks
查看该进程使用的cpu,如下:
ps -eo pid,args:50,psr|grep a.out
1848 ./a.out 0
注:这里我们看到a.out进程已经被指定到第1块CPU上运行了,如下:
这里我们查看该进程的状态,如下:
cat /proc/1848/status
Name: a.out
State: R (running)
Tgid: 1848
Pid: 1848
PPid: 1584
TracerPid: 0
Uid: 0 0 0 0
Gid: 0 0 0 0
Utrace: 0
FDSize: 256
Groups: 0 1 2 3 4 6 10
VmPeak: 1896 kB
VmSize: 1816 kB
VmLck: 0 kB
VmHWM: 276 kB
VmRSS: 276 kB
VmData: 24 kB
VmStk: 88 kB
VmExe: 4 kB
VmLib: 1676 kB
VmPTE: 20 kB
VmSwap: 0 kB
Threads: 1
SigQ: 3/7954
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000000
SigCgt: 0000000000000000
CapInh: 0000000000000000
CapPrm: ffffffffffffffff
CapEff: ffffffffffffffff
CapBnd: ffffffffffffffff
Cpus_allowed: 1
Cpus_allowed_list: 0
Mems_allowed: 1
Mems_allowed_list: 0
voluntary_ctxt_switches: 2
nonvoluntary_ctxt_switches: 343
注:
Cpus_allowed:1指出该进程可以使用CPU的亲和性掩码,因为我们指定为第1块CPU,所以这里就是1,如果该进程指定为4个CPU(如果有话),这里就是F(1111).
Cpus_allowed_list:0指出该进程可以使用CPU的列表,这里是0,所以只能使用第1块CPU.
Mems_allowed: 1
Mems_allowed_list: 0
内存同CPU一样,进程a.out只是使用了结点0的内存资源.
下面是init进程的status,我们可以做一下对比,如下:
cat /proc/1/status
略
Cpus_allowed: 3
Cpus_allowed_list: 0-1
Mems_allowed: 1
Mems_allowed_list: 0
最后我们总结一下cpusets的各子目录的关系,如果我们有8块cpu,在/cpusets目录下建了子目录test1,同时给test1中的cpusets.cpus指定为0-3,那么在test1目录下再建test2子目录,此时只能给test2子目录中的cpuset.cpus指定为0-3.
也就是说这是一种继承的关系.在我们的例子中在/cpusets/test目录下再建subtest,此时不允许向subtest目录下的cpuset.cpus写入1,因为test的cpuset.cpus是0.
如下:
mkdir subtest && cd subtest
写入失败
echo 1 > cpuset.cpus
-bash: echo: write error: Permission denied
查看test的cpuset.cpus为0
cat ../cpuset.cpus
0
对subtest的cpuset.cpus写入0是可以的,如下:
echo 0 > cpuset.cpus
cat cpuset.cpus
0
3)我们演示一下cgroup中的memory系统,这里我们指定某个进程只能使用300MB的虚拟内存.
创建用于挂载的cgroup/memory的目录,如下:
mkdir /mnt/cgroup/
指定用cgroup文件系统挂载,同时指定用memory子系统,如下:
mount -t cgroup none /mnt/cgroup/ -o memory
查看memory子系统中包括的文件,如下:
ls /mnt/cgroup/
cgroup.procs memory.memsw.usage_in_bytes
libvirt memory.soft_limit_in_bytes
memory.failcnt memory.stat
memory.force_empty memory.swappiness
memory.limit_in_bytes memory.usage_in_bytes
memory.max_usage_in_bytes memory.use_hierarchy
memory.memsw.failcnt notify_on_release
memory.memsw.limit_in_bytes release_agent
memory.memsw.max_usage_in_bytes tasks
查看cgroup/memory系统中的文件,如下:
cd /mnt/cgroup/
ls
cgroup.procs memory.limit_in_bytes memory.memsw.max_usage_in_bytes memory.swappiness release_agent
libvirt memory.max_usage_in_bytes memory.memsw.usage_in_bytes memory.usage_in_bytes tasks
memory.failcnt memory.memsw.failcnt memory.soft_limit_in_bytes memory.use_hierarchy
memory.force_empty memory.memsw.limit_in_bytes memory.stat notify_on_release
新建目录test,如下:
mkdir test && cd test/
ls
cgroup.procs memory.max_usage_in_bytes memory.memsw.usage_in_bytes memory.usage_in_bytes
memory.failcnt memory.memsw.failcnt memory.soft_limit_in_bytes memory.use_hierarchy
memory.force_empty memory.memsw.limit_in_bytes memory.stat notify_on_release
memory.limit_in_bytes memory.memsw.max_usage_in_bytes memory.swappiness tasks
查看相关的内存限制:
more memory.limit_in_bytes
9223372036854775807
more memory.memsw.limit_in_bytes
9223372036854775807
将该进程组中的内存限制开启为300MB,如下:
echo 300M > memory.limit_in_bytes
echo 300M > memory.memsw.limit_in_bytes
cat memory.limit_in_bytes
314572800
cat memory.memsw.limit_in_bytes
314572800
将当前进程的PID写入到tasks中,如下:
echo $$ > tasks
通过下面的程序,我们申请内存资源,如下:
more /tmp/test.c
#include <stdio.h>
#include <stdlib.h>
#define MALLOC_SIZE 1024 * 1024 * 300 //1G
int main(void)
{
char *i = NULL;
long int j;
i = malloc(MALLOC_SIZE);
if(i != NULL) {
for(j = 0; j < MALLOC_SIZE; j++)
*(i+j) = 'a';
}
sleep(5);
return 0;
}
编译后运行,如下:
gcc /tmp/test.c /tmp/test
/tmp/test
Killed
进程在申请了300MB后,想继续申请,发现内存不够用后退出,我们查看日志,如下:
tail -f /var/log/message
Oct 23 00:28:38 localhost kernel: 20531 total pagecache pages
Oct 23 00:28:38 localhost kernel: 0 pages in swap cache
Oct 23 00:28:38 localhost kernel: Swap cache stats: add 0, delete 0, find 0/0
Oct 23 00:28:38 localhost kernel: Free swap = 1760248kB
Oct 23 00:28:38 localhost kernel: Total swap = 1760248kB
Oct 23 00:28:38 localhost kernel: 262128 pages RAM
Oct 23 00:28:38 localhost kernel: 35330 pages HighMem
Oct 23 00:28:38 localhost kernel: 4333 pages reserved
Oct 23 00:28:38 localhost kernel: 17603 pages shared
Oct 23 00:28:38 localhost kernel: 103368 pages non-shared
Oct 23 00:28:38 localhost kernel: Memory cgroup out of memory: kill process 1621 (test) score 4828 or a child
Oct 23 00:28:38 localhost kernel: Killed process 1621 (test) vsz:309020kB, anon-rss:307104kB, file-rss:268kB
Memory cgroup out of memory正是说明cgroup启到了作用,它kill了引起OOM的进程.
4)最后我们挂载一下其它的几种cgroup子系统,看一下他们的相关配置文件,有关于更深入的分析,我手头的资料还有限,在不断研究后再总结出来.
mount -t cgroup none /mnt/cgroup/ -o cpu
ls -l /mnt/cgroup/
total 0
-r--r--r--. 1 root root 0 Oct 22 21:52 cgroup.procs
-rw-r--r--. 1 root root 0 Oct 22 21:52 cpu.rt_period_us
-rw-r--r--. 1 root root 0 Oct 22 21:52 cpu.rt_runtime_us
-rw-r--r--. 1 root root 0 Oct 22 21:52 cpu.shares
drwxr-xr-x. 3 root root 0 Oct 22 21:53 libvirt
-rw-r--r--. 1 root root 0 Oct 22 21:52 notify_on_release
-rw-r--r--. 1 root root 0 Oct 22 21:52 release_agent
-rw-r--r--. 1 root root 0 Oct 22 21:52 tasks
mount -t cgroup none /mnt/cgroup/ -o cpuacct
ls -l /mnt/cgroup/
total 0
-r--r--r--. 1 root root 0 Oct 22 21:52 cgroup.procs
-r--r--r--. 1 root root 0 Oct 22 21:52 cpuacct.stat
-rw-r--r--. 1 root root 0 Oct 22 21:52 cpuacct.usage
-r--r--r--. 1 root root 0 Oct 22 21:52 cpuacct.usage_percpu
drwxr-xr-x. 3 root root 0 Oct 22 21:53 libvirt
-rw-r--r--. 1 root root 0 Oct 22 21:52 notify_on_release
-rw-r--r--. 1 root root 0 Oct 22 21:52 release_agent
-rw-r--r--. 1 root root 0 Oct 22 21:52 tasks
mount -t cgroup none /mnt/cgroup/ -o devices
ls -l /mnt/cgroup/
total 0
-r--r--r--. 1 root root 0 Oct 22 21:52 cgroup.procs
--w-------. 1 root root 0 Oct 22 21:52 devices.allow
--w-------. 1 root root 0 Oct 22 21:52 devices.deny
-r--r--r--. 1 root root 0 Oct 22 21:52 devices.list
drwxr-xr-x. 3 root root 0 Oct 22 21:53 libvirt
-rw-r--r--. 1 root root 0 Oct 22 21:52 notify_on_release
-rw-r--r--. 1 root root 0 Oct 22 21:52 release_agent
-rw-r--r--. 1 root root 0 Oct 22 21:52 tasks
mount -t cgroup none /mnt/cgroup/ -o freezer
[root@localhost ~]# ls -l /mnt/cgroup/
total 0
-r--r--r--. 1 root root 0 Oct 22 21:52 cgroup.procs
drwxr-xr-x. 3 root root 0 Oct 22 21:53 libvirt
-rw-r--r--. 1 root root 0 Oct 22 21:52 notify_on_release
-rw-r--r--. 1 root root 0 Oct 22 21:52 release_agent
-rw-r--r--. 1 root root 0 Oct 22 21:52 tasks
mount -t cgroup none /mnt/cgroup/ -o net_cls
[root@localhost ~]# ls -l /mnt/cgroup/
total 0
-r--r--r--. 1 root root 0 Oct 22 21:52 cgroup.procs
-rw-r--r--. 1 root root 0 Oct 22 21:52 net_cls.classid
-rw-r--r--. 1 root root 0 Oct 22 21:52 notify_on_release
-rw-r--r--. 1 root root 0 Oct 22 21:52 release_agent
-rw-r--r--. 1 root root 0 Oct 22 21:52 tasks
mount -t cgroup none /mnt/cgroup/ -o blkio
[root@localhost ~]# ls -l /mnt/cgroup/
total 0
-r--r--r--. 1 root root 0 Oct 22 21:52 blkio.io_merged
-r--r--r--. 1 root root 0 Oct 22 21:52 blkio.io_queued
-r--r--r--. 1 root root 0 Oct 22 21:52 blkio.io_service_bytes
-r--r--r--. 1 root root 0 Oct 22 21:52 blkio.io_serviced
-r--r--r--. 1 root root 0 Oct 22 21:52 blkio.io_service_time
-r--r--r--. 1 root root 0 Oct 22 21:52 blkio.io_wait_time
--w-------. 1 root root 0 Oct 22 21:52 blkio.reset_stats
-r--r--r--. 1 root root 0 Oct 22 21:52 blkio.sectors
-r--r--r--. 1 root root 0 Oct 22 21:52 blkio.time
-rw-r--r--. 1 root root 0 Oct 22 21:52 blkio.weight
-rw-r--r--. 1 root root 0 Oct 22 21:52 blkio.weight_device
-r--r--r--. 1 root root 0 Oct 22 21:52 cgroup.procs
-rw-r--r--. 1 root root 0 Oct 22 21:52 notify_on_release
-rw-r--r--. 1 root root 0 Oct 22 21:52 release_agent
-rw-r--r--. 1 root root 0 Oct 22 21:52 tasks