进程就是一段执行的程序,每当一个程序运行时,对于操作系统本身来说,就创建了一个进程,并且分配了对应的资源。进程可以分为3个类别:
1.交互式进程(I/O)
2.批处理进程 (CPU)
3.实时进程 (REAL-TIME)
对于交互式进程来说,一般其占用的cpu时间片很段,但是优先级偏高;批处理进程占用的cpu时间片很长,但是优先级偏底;实时进程是内核所使用的,其优先级高于前面两种。
上面说到了优先级,linux进程是具有优先级的,一般分为两种:
1.实时优先级
2.静态优先级
实时优先级的取值范围是1-99,值越底,优先级越底,一般为系统内核所使用;静态优先级的取值范围为100-139,值越底优先级越高,一般为应用程序所使用,通过使用nice值可以调静态优先级的先后,其取值范围为-20到19,对应与100-139;而实时优先级比静态优先级的级别高。
linux的内部支持3种调度类别:
对于实时进程来说其支持两种调度类别:
1.SCHED_FIFO:first in first out
2.SCHED_RR:round robin
对于FF类型的进程就通过SCHED_FIFO来调用;对于RR类型的进程就通过SCHED_RR来调用
对于用户进程来说其支持1种调度类别:
1.SCHED_OTHER
对于OS类型的进程就通过SCHED_OTHER来调用
那么如何查看当前操作系统进程使用的是那些信息呢?可以通过如下命令来查看
# ps -e -o class,rtprio,pri,nice,command
CLS RTPRIO PRI NI COMMAND
TS - 24 0 init [3]
FF 99 139 - [migration/0]
TS - 5 19 [ksoftirqd/0]
TS - 29 -5 [events/0]
TS - 28 -5 [khelper]
TS - 29 -5 [kthread]
TS - 29 -5 [kblockd/0]
TS - 19 -5 [kacpid]
TS - 20 -5 [cqueue/0]
TS - 29 -5 [khubd]
TS - 29 -5 [kseriod]
TS - 24 0 [khungtaskd]
TS - 24 0 [pdflush]
TS - 29 -5 [kswapd0]
TS - 19 -5 [aio/0]
TS - 28 -5 [kpsmoused]
TS - 29 -5 [mpt_poll_0]
TS - 19 -5 [mpt/0]
TS - 19 -5 [scsi_eh_0]
TS - 19 -5 [ata/0]
TS - 19 -5 [ata_aux]
TS - 19 -5 [kstriped]
TS - 21 0 /usr/local/php/bin/php server.php
TS - 21 0 /usr/local/php/bin/php server.php
TS - 21 0 /usr/local/php/bin/php server.php
TS - 21 0 /usr/local/apache/bin/httpd -k start
TS - 29 -5 [kjournald]
TS - 22 0 /usr/local/apache/bin/httpd -k start
TS - 22 0 /usr/local/apache/bin/httpd -k start
TS - 22 0 /usr/local/apache/bin/httpd -k start
TS - 29 -5 [kauditd]
TS - 24 -4 /sbin/udevd -d
TS - 21 0 /usr/local/apache/bin/httpd -k start
TS - 17 0 /bin/sh /usr/local/mysql/bin/mysqld_safe --datadir=/data --pi
TS - 21 0 /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --data
TS - 24 0 /sbin/dhclient -1 -q -lf /var/lib/dhclient/dhclient-eth0.leas
TS - 20 -5 [kgameportd]
TS - 23 0 sshd: chenqiguo [priv]
TS - 24 0 sshd: chenqiguo@pts/0
TS - 24 0 -bash
TS - 23 0 su -
TS - 24 0 -bash
TS - 19 -5 [kmpathd/0]
TS - 19 -5 [kmpath_handlerd]
TS - 29 -5 [kjournald]
TS - 29 -5 [kjournald]
TS - 24 0 /usr/sbin/vmtoolsd
TS - 24 0 cupsd
TS - 24 0 tpvmlpd2
TS - 26 -5 [iscsi_eh]
TS - 22 -5 [cnic_wq]
TS - 36 -20 [bnx2i_thread/0]
TS - 29 -5 [ib_addr]
TS - 19 -5 [ib_mcast]
TS - 19 -5 [ib_inform]
TS - 19 -5 [local_sa]
TS - 19 -5 [iw_cm_wq]
TS - 19 -5 [ib_cm/0]
TS - 19 -5 [rdma_cm]
TS - 25 -10 iscsiuio
TS - 21 0 iscsid
TS - 34 -10 iscsid
TS - 28 -4 auditd
TS - 32 -8 /sbin/audispd
TS - 24 0 syslogd -m 0
TS - 24 0 klogd -x
TS - 24 0 portmap
TS - 29 -5 [rpciod/0]
TS - 14 0 rpc.statd
TS - 24 0 dbus-daemon --system
TS - 24 0 /usr/sbin/hcid
TS - 14 0 /usr/sbin/sdpd
TS - 30 -10 [krfcommd]
TS - 17 0 pcscd
TS - 21 0 /usr/sbin/acpid
TS - 24 0 hald
TS - 24 0 hald-runner
TS - 21 0 hald-addon-acpi: listening on acpid socket /var/run/acpid.soc
TS - 24 0 hald-addon-keyboard: listening on /dev/input/event0
TS - 23 0 hald-addon-storage: polling /dev/hdc
TS - 14 0 /usr/bin/hidd --server
TS - 17 0 automount --pid-file /var/run/autofs.pid
TS - 24 0 /usr/sbin/sshd
TS - 24 0 sendmail: accepting connections
TS - 24 0 sendmail: Queue runner@01:00:00 for /var/spool/clientmqueue
TS - 24 0 gpm -m /dev/input/mice -t exps2
TS - 24 0 crond
TS - 21 0 xfs -droppriv -daemon
TS - 21 0 /usr/sbin/atd
TS - 21 0 libvirtd --daemon
TS - 24 0 avahi-daemon: running [localhost-2.local]
TS - 15 0 avahi-daemon: chroot helper
TS - 24 0 /usr/sbin/dnsmasq --strict-order --bind-interfaces --pid-file
TS - 24 0 /usr/sbin/smartd -q never
TS - 23 0 login -- root
TS - 24 0 /sbin/mingetty tty2
TS - 24 0 /sbin/mingetty tty3
TS - 24 0 /sbin/mingetty tty4
TS - 19 0 /sbin/mingetty tty5
TS - 20 0 /sbin/mingetty tty6
TS - 24 0 -bash
TS - 5 19 /usr/bin/python -tt /usr/sbin/yum-updatesd
TS - 5 19 /usr/libexec/gam_server
TS - 5 19 /usr/bin/python -tt /usr/libexec/yum-updatesd-helper --check
TS - 22 0 ps -e -o class,rtprio,pri,nice,command
TS - 21 0 rpc.idmapd
TS - 29 -5 [kjournald]
TS - 24 0 [pdflush]
TS - 24 0 /usr/sbin/snmptrapd -Lsd -p /var/run/snmptrapd.pid
TS - 24 0 smbd -D
TS - 24 0 nmbd -D
TS - 21 0 smbd -D
TS - 24 0 /usr/local/php/bin/php server.php
TS - 20 0 /usr/local/php/bin/php server.php
TS - 24 0 /usr/local/php/bin/php server.php
TS - 21 0 /usr/local/php/bin/php server.php
TS - 21 0 /usr/local/php/bin/php server.php
TS - 21 0 /usr/local/php/bin/php server.php
TS - 21 0 /usr/local/php/bin/php server.php
TS - 24 0 /usr/sbin/snmpd -Lsd -Lf /dev/null -p /var/run/snmpd.pid -a
CLS对应内核的调度算法,RTPRIO对应实时优先级,PRI对应静态优先级,NICE对应静态优先级的指定级别,COMMAND对应进程的命令。注意上面[]都是内核的一些线程。
由于进程具有优先级的概念,在极端的情况下可能会出现一种情况就是由于前面有着很多优先级高的进程,导致优先级底的进程永远运行不上,所以又引出了动态优先级的概念。为了避免有些优先级偏底的进程运行不上,linux内核内部会临时的调高这些优先级偏底的进程;当然相反,对于有些优先级偏高的进程,则会临时的调底他的优先级。动态优先级作用在SCHED_OTHER上,主要就是用户空间的进程。其算法为:dynamic priority = max (100,min(static priority - bonus + 5,139))。bonux的取值范围为0-10
当然动态优先级不是万能的,有些时候可能违背我们的意愿。比如我们现在正在运行一台web服务器,其并发量相对较高,需要实时运行,而动态优先级可能有时候会降低我们的web服务器的进程,显示这违背了我们的意愿,我们期待的是web服务器本身就应该较高的获得cpu的使用权。所以可以手动调整一个进程的优先级。
对于100-139的进程优先级可以使用nice和renice命令来调整。nice表示启动一个进程的时候手动调整一个进程的优先级;renice表示更改一个正在运行的进程的优先级。
例如对刚启动的httpd手执行优先级的命令:
# nice --5 /usr/local/apache/bin/apachectl start
# ps -e -o class,rtprio,pri,nice,command | grep httpd
TS - 24 -5 /usr/local/apache/bin/httpd -k start
TS - 23 -5 /usr/local/apache/bin/httpd -k start
TS - 23 -5 /usr/local/apache/bin/httpd -k start
TS - 23 -5 /usr/local/apache/bin/httpd -k start
可以看到优先级已经被改成了-5。对正在运行的进程改变优先级的命令:
# ps -ef | grep httpd
root 5474 1 0 06:50 ? 00:00:00 /usr/local/apache/bin/httpd -k start
# renice -10 5474
# ps -e -o class,rtprio,pri,nice,command | grep httpd
TS - 34 -10 /usr/local/apache/bin/httpd -k start
可以看到以前优先级为-5的httpd进程已经被改成了-10。
对于1-99的进程可以使用chrt命令来改变优先级。而对于实时优先级来说,又分为sched_fifo和sched_rr,所以在调整优先级的时候还需要指定是那一个调度算法。
这里我们来调整一个fifo类别的优先级:
# ps -e -o class,rtprio,pri,nice,command,pid | grep FF
FF 99 139 - [migration/0] 2
# chrt -f -p 90 2 (-f指定是fifo类别的,-p指定级别,2指定进程的pid)
# ps -e -o class,rtprio,pri,nice,command,pid | grep FF
FF 90 130 - [migration/0] 2
对于rr类别的使用给上面一样,只需要把-f换成-r即可。
那么linux是如何在众多进程中去选择当前需要使用的进程的呢?因为进程的优先级别总共有139种,所以建立139个队列,每个队列中存放了当前对应级别的进程。当需要调用一个进程的时候,就按照进程的优先级去扫描整个队列的首部(第一个元素),如果当前较高的优先级中有了就取出,没有就继续往下寻找,直到找到一个为止。这种寻找方式可以忽略进程的多少,促使进程的算法复杂度始终为0(1),以达到快速寻找到高优先级的进程。事实上,每一个队列又分为了两种情况,活动队列和过期队列。当一个进程被调度以后,但是又没有执行完成的情况下,就会被放到过期队列中排队。当活动队列中的所有进程运行完毕以后,再把活动队列和过期队列交换一下,重新执行活动队列(老的过期队列)。
在一个多cpu的操作系统上,由于内存是共有资源,所以在访问内存上的资源的时候可能会产生资源竞争,我们把这种cpu的架构叫做SMP(Symmetric Multi-Processor,对称多处理器)
一个cpu在访问内存数据后至少需要3个cpu的时钟周期:
1.向内存控制器传输一个寻址要求,内存控制器返回寻址指令。
2.找到对应的内存地址,并施加一定的请求机制。例如读锁和写锁。
3.完成读或者写的操作。
因此一个cpu在给内存打交道的时候,其余cpu是不能访问内存的。所以在smp架构下,cpu多了并不一定是好事,因为cpu越多,竞争资源的机会就越大,性能反而会降低。因此产生了另外一种cpu的架构numa( Non-Unified Memory Access,非一致内存访问)
硬件已经趋向使用多条系统总线,每条系统总线为一小组处理器提供服务。每组处理器都有自己的内存,并可能有自己的 I/O 通道。但是,每个 CPU都可以通过一致的方式访问与其他组关联的内存。每个组称为一个“NUMA 节点”。NUMA 节点中的 CPU 数量取决于硬件供应商。访问本地内存比访问与其他 NUMA 节点关联的内存快。这就是“非一致性内存访问体系结构”名称的由来。
在numa架构的cpu上,由于访问其它“NUMA”节点内存的速率没有本地的快,所以要尽可能的使得cpu只访问自己控制的内存。由于内核自身会平衡进程,所以使得进程会在两个cpu之间频繁的切换,最终带来的结果就是一定会交叉内存访问,即访问不是自己的"NUMA"节点内存,从而造成性能的降低。因此,在一个繁忙的服务器上,例如web服务器,可以使用cpu绑定来让web服务器固定的运行在某个cpu上来避免交叉内存的访问。
在有些时候,平衡是必然的,不然会产生一颗cpu很闲,而另外一颗cpu很忙的情况,所以必须要找到一个平衡点。一般来说,在numa架构下,当我们的内存本身命中次数很低的情况下,这种效果就很有用了。可以使用numastat命令来查看。
# numastat
node0
numa_hit 25102521
numa_miss 0
numa_foreign 0
interleave_hit 46349
local_node 25102521
other_node 0
由于本机是非numa架构的cpu所以只显示了node0,在真正的numa架构下,还会显示node1等等。numa_hit表示在本地的内存访问命中的次数,numa_miss表示没有命中的次数。因为只有一段内存空间,所以这里全部命中。如果在numa架构下,numa_miss的值过高的话,就应该把核心服务进程绑定到指定的cpu上,并且启动numad进程。numad可以在硬件级别将我们的进程和某些cpu绑定在一起。
要想达到将指定的进程和指定的cpu绑定,可以使用taskset命令,并且使用掩码的方式来标记cpu。0x0000 0001表示第0颗cpu,0x00000003表示第0颗和第一颗cpu。taskset的基本命令:taskset -p mask pid。例如我们想在5132这个进程绑定在1号cpu上的话可以使用:
# taskset -p 0x00000002 5132
但是这种方法不够直观,所以可以使用-c选项直接指定绑定的cpu号数。例如加了-c选项的上述写法为:
# taskset -p -c 1 5132
这样就直接将5132这个进程绑定在了第一号cpu上。
例如我现在的机子上有一颗2核心的cpu。查看某些进程绑定在了那些cpu上可以使用:
# ps -e -o pid,psr,command
PID PSR COMMAND
1 0 init [3]
2 0 [migration/0]
3 0 [ksoftirqd/0]
4 1 [migration/1]
5 1 [ksoftirqd/1]
6 0 [events/0]
7 1 [events/1]
8 0 [khelper]
由于篇幅限制就不列举完了,其中的PSR就是当前进程运行在了那颗cpu号上。观察当前mysql服务运行在了那颗cpu上:
# ps -e -o pid,psr,command | grep mysql
4498 0 /bin/sh /usr/local/mysql/bin/mysqld_safe --datadir=/data --pid-file=/data/localhost.localdomain.pid
可以看到mysql进程运行在了0号cpu上,于是我们实验一下将他绑定在1号cpu上。
# taskset -p -c 1 4498
pid 4498's current affinity list: 0,1
pid 4498's new affinity list: 1
# ps -e -o pid,psr,command | grep mysql
4498 1 /bin/sh /usr/local/mysql/bin/mysqld_safe --datadir=/data --pid-file=/data/localhost.localdomain.pid
这样我们就完成了对mysql进程到对应cpu的绑定过程。
但是上面仍然不能满足1号cpu只运行mysql进程,还有其它的进程也会运行在1号cpu,所以仍然需要进程间的大量切换。因此可以在/etc/grub.conf中的kener参数中指定某些cpu开机以来不运行其它进程或者服务。例如:
kernel /vmlinuz-2.6.18-371.6.1.el5 ro root=LABEL=/ rhgb quiet isolcpus=1
isolcpus的语法是isolcpus=cpu number,...,cpu number,就是把指定的cpu隔离出来不供其它进程使用。指定上述以后重新启动操作以后再来观察cpu的进程使用情况。
# ps -e -o pid,psr,command
PID PSR COMMAND
1 0 init [3]
2 0 [migration/0]
3 0 [ksoftirqd/0]
4 1 [migration/1]
5 1 [ksoftirqd/1]
6 0 [events/0]
7 1 [events/1]
8 0 [khelper]
13 0 [kthread]
18 0 [kblockd/0]
19 1 [kblockd/1]
20 0 [kacpid]
190 0 [cqueue/0]
191 1 [cqueue/1]
194 0 [khubd]
196 0 [kseriod]
274 0 [khungtaskd]
275 0 [pdflush]
276 0 [pdflush]
277 0 [kswapd0]
278 0 [aio/0]
279 1 [aio/1]
486 0 [kpsmoused]
527 0 [mpt_poll_0]
528 0 [mpt/0]
529 0 [scsi_eh_0]
533 0 [ata/0]
534 1 [ata/1]
535 0 [ata_aux]
545 0 [kstriped]
558 0 [kjournald]
584 0 [kauditd]
617 0 /sbin/udevd -d
1800 0 [kgameportd]
2409 0 [kmpathd/0]
2410 1 [kmpathd/1]
2411 0 [kmpath_handlerd]
2444 0 [kjournald]
2446 0 [kjournald]
3111 0 /usr/sbin/vmtoolsd
3178 0 cupsd
3223 0 tpvmlpd2
3329 0 [iscsi_eh]
3375 0 [cnic_wq]
3382 0 [bnx2i_thread/0]
3383 1 [bnx2i_thread/1]
3395 0 [ib_addr]
3405 0 [ib_mcast]
3406 0 [ib_inform]
3407 0 [local_sa]
3411 0 [iw_cm_wq]
3415 0 [ib_cm/0]
3416 1 [ib_cm/1]
3420 0 [rdma_cm]
3437 0 iscsiuio
3442 0 iscsid
3443 0 iscsid
3850 0 /sbin/dhclient -1 -q -lf /var/lib/dhclient/dhclient-eth0.leases -pf /v
3958 0 auditd
3960 0 /sbin/audispd
3992 0 syslogd -m 0
3995 0 klogd -x
4075 0 irqbalance
4108 0 portmap
4144 0 [rpciod/0]
4145 1 [rpciod/1]
4152 0 rpc.statd
4190 0 rpc.idmapd
4222 0 dbus-daemon --system
4236 0 /usr/sbin/hcid
4240 0 /usr/sbin/sdpd
4256 0 [krfcommd]
4302 0 pcscd
4317 0 /usr/sbin/acpid
4332 0 hald
4333 0 hald-runner
4340 0 hald-addon-acpi: listening on acpid socket /var/run/acpid.socket
4354 0 hald-addon-keyboard: listening on /dev/input/event0
4363 0 hald-addon-storage: polling /dev/hdc
4393 0 /usr/bin/hidd --server
4439 0 automount --pid-file /var/run/autofs.pid
4464 0 /usr/sbin/sshd
4481 0 /bin/sh /usr/local/mysql/bin/mysqld_safe --datadir=/data --pid-file=/d
4632 0 /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --datadir=/data
4689 0 sendmail: accepting connections
4697 0 sendmail: Queue runner@01:00:00 for /var/spool/clientmqueue
4712 0 gpm -m /dev/input/mice -t exps2
4726 0 crond
4758 0 xfs -droppriv -daemon
4772 0 anacron -s
4785 0 /usr/sbin/atd
4802 0 libvirtd --daemon
4845 0 avahi-daemon: running [localhost.local]
4846 0 avahi-daemon: chroot helper
4918 0 /usr/sbin/dnsmasq --strict-order --bind-interfaces --pid-file=/var/run
4940 0 /usr/sbin/smartd -q never
4944 0 login -- root
4945 0 /sbin/mingetty tty2
4946 0 /sbin/mingetty tty3
4947 0 /sbin/mingetty tty4
4952 0 /sbin/mingetty tty5
4955 0 /sbin/mingetty tty6
4960 0 /usr/bin/python -tt /usr/sbin/yum-updatesd
4962 0 /usr/libexec/gam_server
可以看到大量的进程都运行在0号cpu上,除了内核的一些线程以外。也就是说所谓的隔离cpu并不是完全的,因为内核的一些少量的线程仍然会运行在被隔离的cpu上。于是我们还是拿上面的mysql进程来说。先让它运行在1号cpu上。
# taskset -p -c 1 4481
pid 4481's current affinity list: 0
pid 4481's new affinity list: 1
看到这里的区别了吗?上面没有隔离之前current affinity list的值为0和1,而这里只为0,说明隔离cpu是成功了。
当然即使这样做了,仍然不能保证1号cpu上只服务mysql进程,因为cpu还要服务于中断。1号cpu正在服务于mysql的时候,突然其它硬件发出了一个中断请求,1号cpu可能还会被转入内核模式去处理中断请求。因此还可以把1号cpu给隔离出来,也不处理中断的请求,这样1号cpu就可以完完全全服务于mysql进程和一些少量的内核线程了。
为了实现cpu的中断绑定,我们还需要关闭系统内部的的一个服务,叫做irqbalance。这个服务会定期平均的重新分配硬件服务中断到各个cpu号上去。为了纺织irqbalance服务分配中断服务到隔离的cpu号上去,那么就应该禁止启用该服务。
# service irqbalance stop
# chkconfig irqbalance off
将某些中断号给某颗或者多颗cpu绑定起来的语法格式为:echo cpu_mask > /proc/irq/<irq_num>/smp_affinity,当前我的机子上的中断号有:
# cat /proc/irq/
0/ 10/ 12/ 14/ 2/ 4/ 51/ 6/ 7/ 8/
1/ 11/ 13/ 15/ 3/ 5/ 59/ 67/ 75/ 9/
# cat /proc/interrupts查看当前的中断信息
CPU0 CPU1
0: 4719693 0 IO-APIC-edge timer
1: 83 0 IO-APIC-edge i8042
6: 5 0 IO-APIC-edge floppy
7: 0 0 IO-APIC-edge parport0
8: 1 0 IO-APIC-edge rtc
9: 0 0 IO-APIC-level acpi
12: 132 0 IO-APIC-edge i8042
15: 2964 39221 IO-APIC-edge ide1
51: 12271 13247 IO-APIC-level ehci_hcd:usb1, ioc0
59: 9142 3854647 IO-APIC-level uhci_hcd:usb2
67: 86 10107 IO-APIC-level eth0
75: 0 0 IO-APIC-level Ensoniq AudioPCI
NMI: 0 0
LOC: 4719083 4720165
ERR: 0
MIS: 0
可以看到一些中断信息。我们拿0号中断来说明:
0就为中断号,4719693表示在cpu0上响应了4719693个中断,0表示在cpu1上响应了0 个中断,IO-APIC-edge表示链接在这个端口的中断链表上设备接口,timer表示设备名称。
注意:对于0和2的中断号来说是特殊的,内核不允许这2个中断号被修改。所以在我当前的系统下除了0以外的都可以设置其中断固定在某个cpu号上。假如我们要将中断号为59的绑定在cpu0上可以使用:
# echo 00000001 > /proc/irq/59/smp_affinity
这样所有发生在59号上的中断都会教给cpu0来处理,从而彻底隔离了cpu1(原来59号中断都是由cpu1来处理)。这里我们采用了掩码的方式来计算cpu的号数,当然也可以用十进制的方式来处理。cpu的号数和对应的十进制如下:
Zero-based CPU ID: 7 6 5 4 3 2 1 0
Decimal Value: 128 64 32 16 8 4 2 1
假如这里我们想把所有中断绑定在1-4号cpu上,可以使用echo "15" > /proc/irq/59/smp_affinity(15=1+2+4+8)。
由于重新关机以后,这些设置都不会起作用,要使他们生效该怎么做,这里就不再说明了把,想必大家都知道了。
1.交互式进程(I/O)
2.批处理进程 (CPU)
3.实时进程 (REAL-TIME)
对于交互式进程来说,一般其占用的cpu时间片很段,但是优先级偏高;批处理进程占用的cpu时间片很长,但是优先级偏底;实时进程是内核所使用的,其优先级高于前面两种。
上面说到了优先级,linux进程是具有优先级的,一般分为两种:
1.实时优先级
2.静态优先级
实时优先级的取值范围是1-99,值越底,优先级越底,一般为系统内核所使用;静态优先级的取值范围为100-139,值越底优先级越高,一般为应用程序所使用,通过使用nice值可以调静态优先级的先后,其取值范围为-20到19,对应与100-139;而实时优先级比静态优先级的级别高。
linux的内部支持3种调度类别:
对于实时进程来说其支持两种调度类别:
1.SCHED_FIFO:first in first out
2.SCHED_RR:round robin
对于FF类型的进程就通过SCHED_FIFO来调用;对于RR类型的进程就通过SCHED_RR来调用
对于用户进程来说其支持1种调度类别:
1.SCHED_OTHER
对于OS类型的进程就通过SCHED_OTHER来调用
那么如何查看当前操作系统进程使用的是那些信息呢?可以通过如下命令来查看
# ps -e -o class,rtprio,pri,nice,command
CLS RTPRIO PRI NI COMMAND
TS - 24 0 init [3]
FF 99 139 - [migration/0]
TS - 5 19 [ksoftirqd/0]
TS - 29 -5 [events/0]
TS - 28 -5 [khelper]
TS - 29 -5 [kthread]
TS - 29 -5 [kblockd/0]
TS - 19 -5 [kacpid]
TS - 20 -5 [cqueue/0]
TS - 29 -5 [khubd]
TS - 29 -5 [kseriod]
TS - 24 0 [khungtaskd]
TS - 24 0 [pdflush]
TS - 29 -5 [kswapd0]
TS - 19 -5 [aio/0]
TS - 28 -5 [kpsmoused]
TS - 29 -5 [mpt_poll_0]
TS - 19 -5 [mpt/0]
TS - 19 -5 [scsi_eh_0]
TS - 19 -5 [ata/0]
TS - 19 -5 [ata_aux]
TS - 19 -5 [kstriped]
TS - 21 0 /usr/local/php/bin/php server.php
TS - 21 0 /usr/local/php/bin/php server.php
TS - 21 0 /usr/local/php/bin/php server.php
TS - 21 0 /usr/local/apache/bin/httpd -k start
TS - 29 -5 [kjournald]
TS - 22 0 /usr/local/apache/bin/httpd -k start
TS - 22 0 /usr/local/apache/bin/httpd -k start
TS - 22 0 /usr/local/apache/bin/httpd -k start
TS - 29 -5 [kauditd]
TS - 24 -4 /sbin/udevd -d
TS - 21 0 /usr/local/apache/bin/httpd -k start
TS - 17 0 /bin/sh /usr/local/mysql/bin/mysqld_safe --datadir=/data --pi
TS - 21 0 /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --data
TS - 24 0 /sbin/dhclient -1 -q -lf /var/lib/dhclient/dhclient-eth0.leas
TS - 20 -5 [kgameportd]
TS - 23 0 sshd: chenqiguo [priv]
TS - 24 0 sshd: chenqiguo@pts/0
TS - 24 0 -bash
TS - 23 0 su -
TS - 24 0 -bash
TS - 19 -5 [kmpathd/0]
TS - 19 -5 [kmpath_handlerd]
TS - 29 -5 [kjournald]
TS - 29 -5 [kjournald]
TS - 24 0 /usr/sbin/vmtoolsd
TS - 24 0 cupsd
TS - 24 0 tpvmlpd2
TS - 26 -5 [iscsi_eh]
TS - 22 -5 [cnic_wq]
TS - 36 -20 [bnx2i_thread/0]
TS - 29 -5 [ib_addr]
TS - 19 -5 [ib_mcast]
TS - 19 -5 [ib_inform]
TS - 19 -5 [local_sa]
TS - 19 -5 [iw_cm_wq]
TS - 19 -5 [ib_cm/0]
TS - 19 -5 [rdma_cm]
TS - 25 -10 iscsiuio
TS - 21 0 iscsid
TS - 34 -10 iscsid
TS - 28 -4 auditd
TS - 32 -8 /sbin/audispd
TS - 24 0 syslogd -m 0
TS - 24 0 klogd -x
TS - 24 0 portmap
TS - 29 -5 [rpciod/0]
TS - 14 0 rpc.statd
TS - 24 0 dbus-daemon --system
TS - 24 0 /usr/sbin/hcid
TS - 14 0 /usr/sbin/sdpd
TS - 30 -10 [krfcommd]
TS - 17 0 pcscd
TS - 21 0 /usr/sbin/acpid
TS - 24 0 hald
TS - 24 0 hald-runner
TS - 21 0 hald-addon-acpi: listening on acpid socket /var/run/acpid.soc
TS - 24 0 hald-addon-keyboard: listening on /dev/input/event0
TS - 23 0 hald-addon-storage: polling /dev/hdc
TS - 14 0 /usr/bin/hidd --server
TS - 17 0 automount --pid-file /var/run/autofs.pid
TS - 24 0 /usr/sbin/sshd
TS - 24 0 sendmail: accepting connections
TS - 24 0 sendmail: Queue runner@01:00:00 for /var/spool/clientmqueue
TS - 24 0 gpm -m /dev/input/mice -t exps2
TS - 24 0 crond
TS - 21 0 xfs -droppriv -daemon
TS - 21 0 /usr/sbin/atd
TS - 21 0 libvirtd --daemon
TS - 24 0 avahi-daemon: running [localhost-2.local]
TS - 15 0 avahi-daemon: chroot helper
TS - 24 0 /usr/sbin/dnsmasq --strict-order --bind-interfaces --pid-file
TS - 24 0 /usr/sbin/smartd -q never
TS - 23 0 login -- root
TS - 24 0 /sbin/mingetty tty2
TS - 24 0 /sbin/mingetty tty3
TS - 24 0 /sbin/mingetty tty4
TS - 19 0 /sbin/mingetty tty5
TS - 20 0 /sbin/mingetty tty6
TS - 24 0 -bash
TS - 5 19 /usr/bin/python -tt /usr/sbin/yum-updatesd
TS - 5 19 /usr/libexec/gam_server
TS - 5 19 /usr/bin/python -tt /usr/libexec/yum-updatesd-helper --check
TS - 22 0 ps -e -o class,rtprio,pri,nice,command
TS - 21 0 rpc.idmapd
TS - 29 -5 [kjournald]
TS - 24 0 [pdflush]
TS - 24 0 /usr/sbin/snmptrapd -Lsd -p /var/run/snmptrapd.pid
TS - 24 0 smbd -D
TS - 24 0 nmbd -D
TS - 21 0 smbd -D
TS - 24 0 /usr/local/php/bin/php server.php
TS - 20 0 /usr/local/php/bin/php server.php
TS - 24 0 /usr/local/php/bin/php server.php
TS - 21 0 /usr/local/php/bin/php server.php
TS - 21 0 /usr/local/php/bin/php server.php
TS - 21 0 /usr/local/php/bin/php server.php
TS - 21 0 /usr/local/php/bin/php server.php
TS - 24 0 /usr/sbin/snmpd -Lsd -Lf /dev/null -p /var/run/snmpd.pid -a
CLS对应内核的调度算法,RTPRIO对应实时优先级,PRI对应静态优先级,NICE对应静态优先级的指定级别,COMMAND对应进程的命令。注意上面[]都是内核的一些线程。
由于进程具有优先级的概念,在极端的情况下可能会出现一种情况就是由于前面有着很多优先级高的进程,导致优先级底的进程永远运行不上,所以又引出了动态优先级的概念。为了避免有些优先级偏底的进程运行不上,linux内核内部会临时的调高这些优先级偏底的进程;当然相反,对于有些优先级偏高的进程,则会临时的调底他的优先级。动态优先级作用在SCHED_OTHER上,主要就是用户空间的进程。其算法为:dynamic priority = max (100,min(static priority - bonus + 5,139))。bonux的取值范围为0-10
当然动态优先级不是万能的,有些时候可能违背我们的意愿。比如我们现在正在运行一台web服务器,其并发量相对较高,需要实时运行,而动态优先级可能有时候会降低我们的web服务器的进程,显示这违背了我们的意愿,我们期待的是web服务器本身就应该较高的获得cpu的使用权。所以可以手动调整一个进程的优先级。
对于100-139的进程优先级可以使用nice和renice命令来调整。nice表示启动一个进程的时候手动调整一个进程的优先级;renice表示更改一个正在运行的进程的优先级。
例如对刚启动的httpd手执行优先级的命令:
# nice --5 /usr/local/apache/bin/apachectl start
# ps -e -o class,rtprio,pri,nice,command | grep httpd
TS - 24 -5 /usr/local/apache/bin/httpd -k start
TS - 23 -5 /usr/local/apache/bin/httpd -k start
TS - 23 -5 /usr/local/apache/bin/httpd -k start
TS - 23 -5 /usr/local/apache/bin/httpd -k start
可以看到优先级已经被改成了-5。对正在运行的进程改变优先级的命令:
# ps -ef | grep httpd
root 5474 1 0 06:50 ? 00:00:00 /usr/local/apache/bin/httpd -k start
# renice -10 5474
# ps -e -o class,rtprio,pri,nice,command | grep httpd
TS - 34 -10 /usr/local/apache/bin/httpd -k start
可以看到以前优先级为-5的httpd进程已经被改成了-10。
对于1-99的进程可以使用chrt命令来改变优先级。而对于实时优先级来说,又分为sched_fifo和sched_rr,所以在调整优先级的时候还需要指定是那一个调度算法。
这里我们来调整一个fifo类别的优先级:
# ps -e -o class,rtprio,pri,nice,command,pid | grep FF
FF 99 139 - [migration/0] 2
# chrt -f -p 90 2 (-f指定是fifo类别的,-p指定级别,2指定进程的pid)
# ps -e -o class,rtprio,pri,nice,command,pid | grep FF
FF 90 130 - [migration/0] 2
对于rr类别的使用给上面一样,只需要把-f换成-r即可。
那么linux是如何在众多进程中去选择当前需要使用的进程的呢?因为进程的优先级别总共有139种,所以建立139个队列,每个队列中存放了当前对应级别的进程。当需要调用一个进程的时候,就按照进程的优先级去扫描整个队列的首部(第一个元素),如果当前较高的优先级中有了就取出,没有就继续往下寻找,直到找到一个为止。这种寻找方式可以忽略进程的多少,促使进程的算法复杂度始终为0(1),以达到快速寻找到高优先级的进程。事实上,每一个队列又分为了两种情况,活动队列和过期队列。当一个进程被调度以后,但是又没有执行完成的情况下,就会被放到过期队列中排队。当活动队列中的所有进程运行完毕以后,再把活动队列和过期队列交换一下,重新执行活动队列(老的过期队列)。
在一个多cpu的操作系统上,由于内存是共有资源,所以在访问内存上的资源的时候可能会产生资源竞争,我们把这种cpu的架构叫做SMP(Symmetric Multi-Processor,对称多处理器)
一个cpu在访问内存数据后至少需要3个cpu的时钟周期:
1.向内存控制器传输一个寻址要求,内存控制器返回寻址指令。
2.找到对应的内存地址,并施加一定的请求机制。例如读锁和写锁。
3.完成读或者写的操作。
因此一个cpu在给内存打交道的时候,其余cpu是不能访问内存的。所以在smp架构下,cpu多了并不一定是好事,因为cpu越多,竞争资源的机会就越大,性能反而会降低。因此产生了另外一种cpu的架构numa( Non-Unified Memory Access,非一致内存访问)
硬件已经趋向使用多条系统总线,每条系统总线为一小组处理器提供服务。每组处理器都有自己的内存,并可能有自己的 I/O 通道。但是,每个 CPU都可以通过一致的方式访问与其他组关联的内存。每个组称为一个“NUMA 节点”。NUMA 节点中的 CPU 数量取决于硬件供应商。访问本地内存比访问与其他 NUMA 节点关联的内存快。这就是“非一致性内存访问体系结构”名称的由来。
在numa架构的cpu上,由于访问其它“NUMA”节点内存的速率没有本地的快,所以要尽可能的使得cpu只访问自己控制的内存。由于内核自身会平衡进程,所以使得进程会在两个cpu之间频繁的切换,最终带来的结果就是一定会交叉内存访问,即访问不是自己的"NUMA"节点内存,从而造成性能的降低。因此,在一个繁忙的服务器上,例如web服务器,可以使用cpu绑定来让web服务器固定的运行在某个cpu上来避免交叉内存的访问。
在有些时候,平衡是必然的,不然会产生一颗cpu很闲,而另外一颗cpu很忙的情况,所以必须要找到一个平衡点。一般来说,在numa架构下,当我们的内存本身命中次数很低的情况下,这种效果就很有用了。可以使用numastat命令来查看。
# numastat
node0
numa_hit 25102521
numa_miss 0
numa_foreign 0
interleave_hit 46349
local_node 25102521
other_node 0
由于本机是非numa架构的cpu所以只显示了node0,在真正的numa架构下,还会显示node1等等。numa_hit表示在本地的内存访问命中的次数,numa_miss表示没有命中的次数。因为只有一段内存空间,所以这里全部命中。如果在numa架构下,numa_miss的值过高的话,就应该把核心服务进程绑定到指定的cpu上,并且启动numad进程。numad可以在硬件级别将我们的进程和某些cpu绑定在一起。
要想达到将指定的进程和指定的cpu绑定,可以使用taskset命令,并且使用掩码的方式来标记cpu。0x0000 0001表示第0颗cpu,0x00000003表示第0颗和第一颗cpu。taskset的基本命令:taskset -p mask pid。例如我们想在5132这个进程绑定在1号cpu上的话可以使用:
# taskset -p 0x00000002 5132
但是这种方法不够直观,所以可以使用-c选项直接指定绑定的cpu号数。例如加了-c选项的上述写法为:
# taskset -p -c 1 5132
这样就直接将5132这个进程绑定在了第一号cpu上。
例如我现在的机子上有一颗2核心的cpu。查看某些进程绑定在了那些cpu上可以使用:
# ps -e -o pid,psr,command
PID PSR COMMAND
1 0 init [3]
2 0 [migration/0]
3 0 [ksoftirqd/0]
4 1 [migration/1]
5 1 [ksoftirqd/1]
6 0 [events/0]
7 1 [events/1]
8 0 [khelper]
由于篇幅限制就不列举完了,其中的PSR就是当前进程运行在了那颗cpu号上。观察当前mysql服务运行在了那颗cpu上:
# ps -e -o pid,psr,command | grep mysql
4498 0 /bin/sh /usr/local/mysql/bin/mysqld_safe --datadir=/data --pid-file=/data/localhost.localdomain.pid
可以看到mysql进程运行在了0号cpu上,于是我们实验一下将他绑定在1号cpu上。
# taskset -p -c 1 4498
pid 4498's current affinity list: 0,1
pid 4498's new affinity list: 1
# ps -e -o pid,psr,command | grep mysql
4498 1 /bin/sh /usr/local/mysql/bin/mysqld_safe --datadir=/data --pid-file=/data/localhost.localdomain.pid
这样我们就完成了对mysql进程到对应cpu的绑定过程。
但是上面仍然不能满足1号cpu只运行mysql进程,还有其它的进程也会运行在1号cpu,所以仍然需要进程间的大量切换。因此可以在/etc/grub.conf中的kener参数中指定某些cpu开机以来不运行其它进程或者服务。例如:
kernel /vmlinuz-2.6.18-371.6.1.el5 ro root=LABEL=/ rhgb quiet isolcpus=1
isolcpus的语法是isolcpus=cpu number,...,cpu number,就是把指定的cpu隔离出来不供其它进程使用。指定上述以后重新启动操作以后再来观察cpu的进程使用情况。
# ps -e -o pid,psr,command
PID PSR COMMAND
1 0 init [3]
2 0 [migration/0]
3 0 [ksoftirqd/0]
4 1 [migration/1]
5 1 [ksoftirqd/1]
6 0 [events/0]
7 1 [events/1]
8 0 [khelper]
13 0 [kthread]
18 0 [kblockd/0]
19 1 [kblockd/1]
20 0 [kacpid]
190 0 [cqueue/0]
191 1 [cqueue/1]
194 0 [khubd]
196 0 [kseriod]
274 0 [khungtaskd]
275 0 [pdflush]
276 0 [pdflush]
277 0 [kswapd0]
278 0 [aio/0]
279 1 [aio/1]
486 0 [kpsmoused]
527 0 [mpt_poll_0]
528 0 [mpt/0]
529 0 [scsi_eh_0]
533 0 [ata/0]
534 1 [ata/1]
535 0 [ata_aux]
545 0 [kstriped]
558 0 [kjournald]
584 0 [kauditd]
617 0 /sbin/udevd -d
1800 0 [kgameportd]
2409 0 [kmpathd/0]
2410 1 [kmpathd/1]
2411 0 [kmpath_handlerd]
2444 0 [kjournald]
2446 0 [kjournald]
3111 0 /usr/sbin/vmtoolsd
3178 0 cupsd
3223 0 tpvmlpd2
3329 0 [iscsi_eh]
3375 0 [cnic_wq]
3382 0 [bnx2i_thread/0]
3383 1 [bnx2i_thread/1]
3395 0 [ib_addr]
3405 0 [ib_mcast]
3406 0 [ib_inform]
3407 0 [local_sa]
3411 0 [iw_cm_wq]
3415 0 [ib_cm/0]
3416 1 [ib_cm/1]
3420 0 [rdma_cm]
3437 0 iscsiuio
3442 0 iscsid
3443 0 iscsid
3850 0 /sbin/dhclient -1 -q -lf /var/lib/dhclient/dhclient-eth0.leases -pf /v
3958 0 auditd
3960 0 /sbin/audispd
3992 0 syslogd -m 0
3995 0 klogd -x
4075 0 irqbalance
4108 0 portmap
4144 0 [rpciod/0]
4145 1 [rpciod/1]
4152 0 rpc.statd
4190 0 rpc.idmapd
4222 0 dbus-daemon --system
4236 0 /usr/sbin/hcid
4240 0 /usr/sbin/sdpd
4256 0 [krfcommd]
4302 0 pcscd
4317 0 /usr/sbin/acpid
4332 0 hald
4333 0 hald-runner
4340 0 hald-addon-acpi: listening on acpid socket /var/run/acpid.socket
4354 0 hald-addon-keyboard: listening on /dev/input/event0
4363 0 hald-addon-storage: polling /dev/hdc
4393 0 /usr/bin/hidd --server
4439 0 automount --pid-file /var/run/autofs.pid
4464 0 /usr/sbin/sshd
4481 0 /bin/sh /usr/local/mysql/bin/mysqld_safe --datadir=/data --pid-file=/d
4632 0 /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --datadir=/data
4689 0 sendmail: accepting connections
4697 0 sendmail: Queue runner@01:00:00 for /var/spool/clientmqueue
4712 0 gpm -m /dev/input/mice -t exps2
4726 0 crond
4758 0 xfs -droppriv -daemon
4772 0 anacron -s
4785 0 /usr/sbin/atd
4802 0 libvirtd --daemon
4845 0 avahi-daemon: running [localhost.local]
4846 0 avahi-daemon: chroot helper
4918 0 /usr/sbin/dnsmasq --strict-order --bind-interfaces --pid-file=/var/run
4940 0 /usr/sbin/smartd -q never
4944 0 login -- root
4945 0 /sbin/mingetty tty2
4946 0 /sbin/mingetty tty3
4947 0 /sbin/mingetty tty4
4952 0 /sbin/mingetty tty5
4955 0 /sbin/mingetty tty6
4960 0 /usr/bin/python -tt /usr/sbin/yum-updatesd
4962 0 /usr/libexec/gam_server
可以看到大量的进程都运行在0号cpu上,除了内核的一些线程以外。也就是说所谓的隔离cpu并不是完全的,因为内核的一些少量的线程仍然会运行在被隔离的cpu上。于是我们还是拿上面的mysql进程来说。先让它运行在1号cpu上。
# taskset -p -c 1 4481
pid 4481's current affinity list: 0
pid 4481's new affinity list: 1
看到这里的区别了吗?上面没有隔离之前current affinity list的值为0和1,而这里只为0,说明隔离cpu是成功了。
当然即使这样做了,仍然不能保证1号cpu上只服务mysql进程,因为cpu还要服务于中断。1号cpu正在服务于mysql的时候,突然其它硬件发出了一个中断请求,1号cpu可能还会被转入内核模式去处理中断请求。因此还可以把1号cpu给隔离出来,也不处理中断的请求,这样1号cpu就可以完完全全服务于mysql进程和一些少量的内核线程了。
为了实现cpu的中断绑定,我们还需要关闭系统内部的的一个服务,叫做irqbalance。这个服务会定期平均的重新分配硬件服务中断到各个cpu号上去。为了纺织irqbalance服务分配中断服务到隔离的cpu号上去,那么就应该禁止启用该服务。
# service irqbalance stop
# chkconfig irqbalance off
将某些中断号给某颗或者多颗cpu绑定起来的语法格式为:echo cpu_mask > /proc/irq/<irq_num>/smp_affinity,当前我的机子上的中断号有:
# cat /proc/irq/
0/ 10/ 12/ 14/ 2/ 4/ 51/ 6/ 7/ 8/
1/ 11/ 13/ 15/ 3/ 5/ 59/ 67/ 75/ 9/
# cat /proc/interrupts查看当前的中断信息
CPU0 CPU1
0: 4719693 0 IO-APIC-edge timer
1: 83 0 IO-APIC-edge i8042
6: 5 0 IO-APIC-edge floppy
7: 0 0 IO-APIC-edge parport0
8: 1 0 IO-APIC-edge rtc
9: 0 0 IO-APIC-level acpi
12: 132 0 IO-APIC-edge i8042
15: 2964 39221 IO-APIC-edge ide1
51: 12271 13247 IO-APIC-level ehci_hcd:usb1, ioc0
59: 9142 3854647 IO-APIC-level uhci_hcd:usb2
67: 86 10107 IO-APIC-level eth0
75: 0 0 IO-APIC-level Ensoniq AudioPCI
NMI: 0 0
LOC: 4719083 4720165
ERR: 0
MIS: 0
可以看到一些中断信息。我们拿0号中断来说明:
0就为中断号,4719693表示在cpu0上响应了4719693个中断,0表示在cpu1上响应了0 个中断,IO-APIC-edge表示链接在这个端口的中断链表上设备接口,timer表示设备名称。
注意:对于0和2的中断号来说是特殊的,内核不允许这2个中断号被修改。所以在我当前的系统下除了0以外的都可以设置其中断固定在某个cpu号上。假如我们要将中断号为59的绑定在cpu0上可以使用:
# echo 00000001 > /proc/irq/59/smp_affinity
这样所有发生在59号上的中断都会教给cpu0来处理,从而彻底隔离了cpu1(原来59号中断都是由cpu1来处理)。这里我们采用了掩码的方式来计算cpu的号数,当然也可以用十进制的方式来处理。cpu的号数和对应的十进制如下:
Zero-based CPU ID: 7 6 5 4 3 2 1 0
Decimal Value: 128 64 32 16 8 4 2 1
假如这里我们想把所有中断绑定在1-4号cpu上,可以使用echo "15" > /proc/irq/59/smp_affinity(15=1+2+4+8)。
由于重新关机以后,这些设置都不会起作用,要使他们生效该怎么做,这里就不再说明了把,想必大家都知道了。