一、进程描述符及任务结构
进程存放在叫做任务队列(tasklist)的双向循环链表中。链表中的每一项包含一个具体进程的所有信息,类型为task_struct,称为进程描述符(process descriptor),该结构定义在文件中。
Linux通过slab分配器分配task_struct结构,这样能达到对象复用和缓存着色(cache coloring)的目的。另一方面,由于x86这样的硬件体系结构寄存器比较少,因此在文件中定义结构为thread_info的栈指针,通过栈指针可以计算出task_struct的位置,避免了使用额外的寄存器存储专门记录。
二、进程的创建
在Linux系统中,所有的进程都是PID为1的init进程的后代;内核在系统启动的最后阶段启动init进程,该进程读取系统的初始化脚本并执行其他的相关程序,最终完成系统启动的整个过程。Linux提供两个函数处理进程的创建和执行:fork()和exec()。
fork()通过拷贝当前进程创建一个子进程;子进程与父进程的区别仅仅在于PID(每个进程唯一),PPID(父进程的PID)和某些资源和统计量(例如挂起的信号)。exec()函数负责读取可执行文件并将其载入地址空间开始运行。
fork()使用写时复制(copy-on-write)页实现;内核在fork进程时不复制整个进程地址空间,让父进程和子进程共享同一个拷贝,当需要写入时,数据才会被复制,使各进程拥有自己的拷贝。在页根本不会被写入的情况下(fork()后立即exec()),fork的实际开销只有复制父进程的页表以及给子进程创建唯一的task_struct。
创建进程的fork()函数实际上最终是调用clone()函数。创建线程和进程的步骤一样,只是最终传给clone()函数的参数不同。比如,通过一个普通的fork来创建进程:clone(SIGCHLD, 0);创建一个和父进程共享地址空间,文件系统资源,文件描述符和信号处理程序的进程,即一个线程:clone(CLONE_VM | CLONE_FS | CLONE_FILES |CLONE_SIGHAND, 0)。在内核中创建的内核线程与普通的进程之间还有个主要区别在于:内核线程没有独立的地址空间,它们只能在内核空间运行。
三、Linux进程状态机
task_struct中的state描述进程的当前状态。进程的状态一共有5种,而进程必然处于其中一种状态:
TASK_RUNNING(运行)
进程是可执行的,它或者正在执行,或者在运行队列中等待执行。这是进程在用户空间中执行唯一可能的状态;也可以应用到内核空间中正在执行的进程。
TASK_INTERRUPTIBLE(可中断)
进程正在睡眠(被阻塞),等待某些资源的释放。一旦这些资源可用,内核就会把进程状态设置为运行,处于此状态的进程也会因为接收到信号而提前被唤醒并投入运行。
TASK_UNINTERRUPTIBLE(不可中断)
除了不会因为接收到信号而被唤醒从而投入运行外,这个状态与可打断状态相同。不可中断并不是指CPU不响应外部硬件的中断,而是指进程不响应异步信号;较之可中断状态,使用得较少。
TASK_ZOMBIE(僵死)
进程已经结束,但是其父进程还没有调用wait4()系统调用。为了父进程能够获知它的消息,子进程的进程描述符仍然被保留着。一旦父进程调用了wait4(),进程描述符就会被释放。
TASK_STOPPED(停止)
进程停止执行,既没有投入运行也不能投入运行。通常这种状态发生在接收SIGSTOP,SIGTSTP,
SIGTTIN,SIGTTOU等信号的时候。此外,在调试期间接收到任何信号,都会使进程进入这种状态。
说明:绝大多数情况下,进程处在睡眠状态时,应该是能够响应异步信号的。否则kill -9无法杀死一个正在睡眠的进程。于是也很好理解,为什么ps命令看到的进程几乎不会出现TASK_UNINTERRUPTIBLE状态,而总是TASK_INTERRUPTIBLE状态。
TASK_UNINTERRUPTIBLE状态存在的意义在于内核的某些处理流程是不能被打断的。如果响应异步信号,程序的执行流程中就会被插入一段用于处理异步信号的流程(这个插入的流程可能只存在于内核态,也可能延伸到用户态),于是原有的流程就被中断了(参见《linux异步信号handle浅析》)。比如进程调用read系统调用对某个设备文件进行读操作,而read系统调用最终执行到对应设备驱动的代码,并与对应的物理设备进行交互,可能需要使用TASK_UNINTERRUPTIBLE状态对进程进行保护,以避免进程与设备交互的过程被打断,造成设备陷入不可控的状态。
四、进程查看
用ps –l命令仅查看当前登录的PID与相关信息;aux则列出目前所有的正在内存当中的进程
- F:代表进程标志(process flags)说明进程的权限,常见的号码有
4:表示进程权限为root
1:表示此子进程仅可进行复制(fork)而无法实际执行(exec) - S:代表进程状态(stat)
- UID : 代表执行者的身份
- PID : 代表这个进程的代号
- PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
- PRI :代表这个进程可被执行的优先级,其值越小越早被执行
- NI :代表这个进程的nice值
- C:代表CPU使用率,单位为百分比
- ADDR/SZ/WCHAN:都与内存有关
ADDR 是 kernel function,指出该进程在内存的哪个部分,如果是个 running 的程序,一般就会显示‘ - ’
SZ 代表此进程使用了多少内存
WCHAN 表示目前进程是否运行中,同样的, 若为 - 表示正在运行中 - TTY:登入者的终端机位置,若为远程登录则使用动态终端接口 (pts/n);
- TIME:使用掉的 CPU 时间,注意,是此程序实际花费 CPU 运行的时间,而不是系统时间;
- CMD:command 的缩写,造成此程序的触发进程之命令为何,[]表示触发命令为内核线程
- %MEM:该进程所占用的物理内存百分比;
- VSZ :该进程使用掉的虚拟内存百分比 (Kbytes)
- RSS :该进程占用的固定内存百分比 (Kbytes)
- START:该进程被触发启动的时间
rtprio RTPRIO realtime priority
五、调整进程优先级
实时进程:chrt常用语法
[root@DQ ~]# man chrt
chrt {-r|-f} -p [prio] {<PID>|<COMMAND>}
NAME
chrt - manipulate real-time attributes of a process
-p, --pid
operate on an existing PID and do not launch a new task
-f, --fifo
set scheduling policy to SCHED_FIFO
-r, --rr
set scheduling policy to SCHED_RR (the default)
普通进程:
设置欲执行的指令的优先权等级:nice
nice -n< N >或-< N >或–-adjustment=< N >
N的取值范围[-20-19],其中-20最高,19最低,只有root可以设置负数的等级
调整已存在进程的优先权等级:renice
[root@DQ ~]# renice --help
Usage:
renice [-n] priority [-p|--pid] pid [... pid]
renice [-n] priority -g|--pgrp pgrp [... pgrp]
##修改所有隶属于该程序群组的程序的优先权
renice [-n] priority -u|--user user [... user]
##修改所有隶属于该用户的程序的优先权
renice -h | --help
renice -v | --version
只有root可以改变其他用户程序的优先权,也仅有root可以设置负数等级。
六、系统性能分析
Sysstat是一款开源软件,工具包提供了多个系统性能分析工具
通过sar命令可以全面获取系统的CPU、运行队列、磁盘I/O、分页(交换区)、内存、CPU中断、网络性能等参数;语法如下
sar [options] [-A] [-o filename] intserval [count]
- intserval:表示采样间隔,是必须有的参数
- count:表示采样次数,是可选的,默认值是1,
- -A:显示系统所有资源设备(CPU,内存,磁盘)的运行状况
- -u:显示系统所有CPU在采样时间内的负载状态
- -P:显示当前系统中指定CPU的使用情况
- -w:显示系统交换活动在采样时间内的状态
- -q:显示运行队列的大小,与系统当时的平均负载相同
- -o filename:表示将命令结果以二进制格式存放在文件中
每隔1秒显示交换活动的状态
-w Report task creation and system switching activity.
proc/s ##每秒创建的任务(进程)个数
Total number of tasks created per second.
cswch/s ##每秒上下文切换次数
Total number of context switches per second.
每隔1秒显示运行队列的大小
-q Report queue length and load averages. The following values are displayed:
runq-sz
Run queue length (number of tasks waiting for run time).
plist-sz
Number of tasks in the task list.
ldavg-1
System load average for the last minute. The load average is calculated as the average number of runnable or running tasks (R state), and the number of tasks in uninterruptible sleep (D state) over the specified interval.
ldavg-5
System load average for the past 5 minutes.
ldavg-15
System load average for the past 15 minutes.
系统CPU计数是从0开始的,下面的命令表示查看第二颗CPU的运行负载,每3秒统计一次,统计5次
- %user:用户进程消耗CPU的时间百分比
- %nice:运行正常进程所消耗的CPU的时间百分比
- %system:系统进程消耗CPU的时间百分比
- %iowait:I/O等待占用CPU的时间百分比
- %steal:在内存相对紧张的情况下pagein强制对不同的页面进行steal操作
- %idle:CPU处于空闲状态的时间百分比
在所有的显示中,要注意%iowait和%idle,%iowait的值过高,表示硬盘存在I/O瓶颈,%idle值高,表示CPU较空闲,如 果%idle值高但系统响应慢时,有可能是CPU等待分配内存,此时应加大内存容量。%idle值如果持续低于10,那么系统的CPU处理能力相对较低, 表明系统中最需要解决的资源是CPU。
iostat主要的功能是对系统的磁盘I/O操作进行监视,主要输出磁盘读写操作的统计信息以及CPU的使用情况,iostat不能对某个进程进行深入分析,仅对系统的整体情况进行分析;语法如下
iostat [ -c | -d ] [ -k ] [ -t ] [ -x [ device ] ] [ interval [ count ] ]
- -c:显示CPU的使用情况
- -d :显示磁盘设备使用状态
- -k:每秒以KB/MB为单位显示数据
- -x:指定要统计的磁盘设备名称,默认为所有的磁盘设备
- -t:打印出统计信息开始执行的时间
每隔1秒显示CPU的使用情况,显示6次
每隔2秒显示磁盘使用状态,显示两次
- tps:该设备每秒的传输次数
- Blk_read/s:每秒读取的数据量
- Blk_wrtn/s:每秒写入的数据量
- Blk_read:读取的总数据量
- Blk_wrtn:写入的总数据量
注意:上面统计信息,我只指定了统计一次,表示是系统从启动到统计时的所有传输信息,如果指定统计多次,那么第二次输出的数据才代表检测的时间段内系统的传输值。
如果Blk_wrtn/s值很大,表示磁盘写操作频繁,可以考虑优化磁盘或优化程序,如果Blk_read/s值很大,表示磁盘读频繁,可以将读取的数据放入内存中进行操作。这两个值没有固定大小,根据系统实际应用不同会有不同值,但是长期的超大读写会影响系统性能。
单独统计/dev/sda与I/O相关而对扩展数据
- rrqm/s:每秒进行合并的读操作数目(当系统调用需要读取数据的时候,VFS将请求发到各个FS,如果FS发现不同的读取请求读取的是相同Block的数据,FS会将这个请求合并Merge);
- wrqm/s:每秒进行合并的写操作数目
- r/s:每秒完成读I/O设备的次数
- w/s:每秒完成写I/O设备的次数
- rsec/s:每秒读取的扇区数
- wsec/:每秒写入的扇区数
- avgrq-sz :平均请求扇区的大小
- avgqu-sz :平均请求队列的长度
- await: 每一个IO请求的处理的平均时间(单位是微秒毫秒);可以理解为I/O的响应时间,包括队列时间和服务时间,一般地系统IO响应时间应该低于5ms
- svctm :表示平均每次设备I/O操作的服务时间(以毫秒为单位)。如果svctm的值与await很接近,表示几乎没有I/O等待,磁盘性能很好,如果await的值远高于svctm的值,则表示I/O队列等待太长,系统上运行的应用程序将变慢
- %util: 在统计时间内所有处理IO时间占总共统计时间的百分比;该参数暗示了设备的繁忙程度,一般地,如果该参数是100%表示设备已经接近满负荷运行了(如果是多磁盘,即使%util是100%,考虑磁盘的并发能力,磁盘的使用未必到了瓶颈)
dstat是一款多功能系统资源统计生成工具,集合了vmstat、iostat、ifstat等工具功能,克服了上述工具的一些限制并且添加了许多额外的功能;其结果可以保持到csv文件,使用脚本或第三方工具对性能进行分析利用(如通过监控平台监控,也可以保存到数据库)。在Centos 6.4系统上安装基本服务器即默认安装,而在其他操作系统可能需要手动安装。
可通过–help查看常用参数,更详细参数选项则使用man dstat
[root@DQ ~]# dstat --help
Usage: dstat [-afv] [options..] [delay [count]]
Versatile tool for generating system resource statistics
-c, --cpu enable cpu stats ##hiq,siq分别为硬中断和软中断次数
--aio enable aio stats
-l, --load enable load stats
-m, --mem enable memory stats
--fs, --filesystem enable fs stats
-y, --sys enable system stats ##int,csw分别为系统的中断次数(interrupt)和上下文切换(context switch)
--socket enable socket stats
-n, --net enable network stats
dstat附带了一些插件很大程度地扩展了它的功能,例如
--disk-util
##显示某一时间磁盘的忙碌状况
per disk utilization in percentage
--freespace
##显示当前磁盘空间使用率
per filesystem disk usage
--proc-count
##显示正在运行的程序数量
show total number of processes
--top-cputime
show process using the most CPU time (in ms)
--top-io
show most expensive I/O process
--top-mem
show process using the most memory
--top-cputime
show process using the most CPU time (in ms)
通过以上命令监测系统性能可以知道哪些进程繁忙,把繁忙的进程绑定到指定的CPU上,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。
七、CPU隔离与绑定
CPU affinity:姻亲关系可以理解为将经常需要运行的批处理的进程或者服务进程在启动后直接绑定在某颗CPU上或者某颗CPU的某些核心上,以避免交叉内存访问,但在某些情况下平衡进程也十分重要,避免出现某些CPU很闲,而某些CPU很忙,一般来说在NUMA架构上如果内存本身的命中次数较低,即miss值过高需要绑定进程至指定的CPU,而在非NUMA架构中进程切换率过高则需要绑定
[root@DQ ~]# man numactl
--cpunodebind=nodes, -N nodes ##将命令运行在某颗CPU所属的node上
Only execute command on the CPUs of nodes. Note that nodes may consist of multiple CPUs. nodes may be specified as noted above.
--physcpubind=cpus, -C cpus ##将进程绑定至CPU
Only execute process on cpus. This accepts cpu numbers as shown in the processor fields of /proc/cpuinfo, or relative cpus as in relative to the current
cpuset.
[root@DQ ~]# man numad
numad ##用户空间级别的守护进程,提供策略将进程关联至CPU,自我监控并完成优化跟管理
numad - A user-level daemon that provides placement advice and process management for efficient use of CPUs and memory on systems with NUMA topology.
使用taskset绑定进程至指定CPU
[root@DQ ~]# man taskset
SYNOPSIS
taskset [options] mask command [arg]...
taskset [options] -p [mask] pid
DESCRIPTION
taskset is used to set or retrieve the CPU affinity of a running process given its PID or to launch a new COMMAND with a given CPU affinity.
CPU隔离,在启动系统时向GRUB传递一个参数
Isolate a CPU from automatic scheduling in /etc/grub.conf
isolcpus=cpu number,…,cpu number
Pin tasks to that CPU with taskset
避免已隔离的CPU处理中断
查看中断线:表示0号中断可运行在所有CPU上,ffffffff表示任意CPU
假设系统上共有16核,现在期望系统只运行在第0核与第1核,剩下的14核需要隔离,需要将中断值绑定到0和1上(非隔离的CPU),从而避免2-15这14核(隔离的CPU)处理中断,可通过以下方式实现将某些CPU从中断处理中隔离出来
echo CPU_MASK > /proc/irq/<irq number>/smp_affinity
Linux上支持CPU调度器域,类似于taskset实现的功能, 将某个进程绑定到某个组内的CPU中,而不是某个单独的CPU;所谓的调度域类似于Linux文件系统结构,将CPU组织成倒置的树状结构,假如系统上有4颗CPU,现在将第1和第2颗CPU划分为一个域A,第3和第4颗CPU划分为一个域B,每一个域可以在划分子域,可将进程绑定在域A上,表示进程只能在第一颗和第二颗CPU上运行,如果绑定在根域表示进程可以运行在所有CPU上。在NUMA体系结构中应该将某些会因为重新平衡而切换的进程绑定至某颗CPU上,进而实现该进程只在当前CPU上执行,其数据也只在该CPU上装载,减少上下文切换的开销
创建子域将CPU绑定至该域,未绑定前对应/cpus,/mes,/tasks目录都为空
注意使用这种方法绑定CPU,系统重启后会失效后,但文件系统因为写入配置文件所以仍会保存