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
当然动态优先级不是万能的,有些时候可能违背我们的意愿。比方我们如今正在执行一台webserver,其并发量相对较高,须要实时执行。而动态优先级可能有时候会减少我们的webserver的进程。显示这违背了我们的意愿,我们期待的是webserver本身就应该较高的获得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"节点内存,从而造成性能的减少。
因此。在一个繁忙的server上。比如webserver,能够使用cpu绑定来让webserver固定的执行在某个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表示链接在这个port的中断链表上设备接口。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)。
因为又一次关机以后,这些设置都不会起作用。要使他们生效该怎么做,这里就不再说明了把,想必大家都知道了。