前言
I/O Wait是一个需要使用高级的工具来debug问题原因,当然也有许多基本工具的高级用法。I/O wait的问题难以定位的原因是:因为我们有很多工具可以告诉你I/O 受限了,但是并没有告诉你具体是哪个进程引起的。
1. 如何确认,是否是I/O问题导致系统缓慢
确认是否是I/O导致的系统缓慢我们可以使用多个命令,但是,最简单的是unix的命令 top
# top
top - 14:31:20 up 35 min, 4 users, load average: 2.25, 1.74, 1.68
Tasks: 71 total, 1 running, 70 sleeping, 0 stopped, 0 zombie
Cpu(s): 2.3%us, 1.7%sy, 0.0%ni, 0.0%id, 96.0%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 245440k total, 241004k used, 4436k free, 496k buffers
Swap: 409596k total, 5436k used, 404160k free, 182812k cached
从CPU这行,可以发现CPU的io wait;这里是96.0%。越高就代表CPU用于io wait的资源越多。
2. 找出哪个磁盘正在被写入
上边的top命令从一个整体上说明了I/O wait,但是并没有说明是哪块磁盘影响的,想知道是哪块磁盘引发的问题,可以使用另外一个命令 iostat 命令
$ iostat -x 2 5
avg-cpu: %user %nice %system %iowait %steal %idle
3.66 0.00 47.64 48.69 0.00 0.00
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 44.50 39.27 117.28 29.32 11220.94 13126.70 332.17 65.77 462.79 9.80 2274.71 7.60 111.41
dm-0 0.00 0.00 83.25 9.95 10515.18 4295.29 317.84 57.01 648.54 16.73 5935.79 11.48 107.02
dm-1 0.00 0.00 57.07 40.84 228.27 163.35 8.00 93.84 979.61 13.94 2329.08 10.93 107.02
iostat 会每2秒更新一次,一共打印5次信息, -x 的选项是打印出扩展信息
第一个iostat 报告会打印出系统最后一次启动后的统计信息,这也就是说,在多数情况下,第一个打印出来的信息应该被忽略,剩下的报告,都是基于上一次间隔的时间。举例子来说,这个命令会打印5次,第二次的报告是从第一次报告出来一个后的统计信息,第三次是基于第二次 ,依次类推
所以,一定记住:第一个忽略!
在上面的例子中,sda的%utilized 是111.41%,这个很好的说明了有进程正在写入到sda磁盘中。
除了%utilized 外,我们可以从iostat得到更加丰富的资源信息,例如每毫秒读写请求(rrqm/s & wrqm/s)),每秒读写的((r/s & w/s)。在上边的例子中,我们的项目看起来正在读写非常多的信息。这个对我们查找相应的进程非常有用。
3. 找出导致高IO的进程
# iotop
Total DISK READ: 8.00 M/s | Total DISK WRITE: 20.36 M/s
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
15758 be/4 root 7.99 M/s 8.01 M/s 0.00 % 61.97 % bonnie++ -n 0 -u 0 -r 239 -s 478 -f -b -d /tmp
最简单的方法就是用iotop找出哪个进程用了最多的存储资源,从上面可以看到是bonnie++。
iotop很好用,但是不是默认安装的。
如果没有iotop,下面的方式也可以让你有种方法缩小范围,尽快找到是哪个进程。
ps 命令对内存和CPU有一个统计,但是他没有对磁盘I/O的统计,虽然他没有显示磁盘I/O,但是它显示进行的状态,我们可以用来知道一个进程是否正在等待I/O
主要的进程状态有:
PROCESS STATE CODES
D uninterruptible sleep (usually IO)
R running or runnable (on run queue)
S interruptible sleep (waiting for an event to complete)
T stopped, either by a job control signal or because it is being traced.
W paging (not valid since the 2.6.xx kernel)
X dead (should never be seen)
Z defunct ("zombie") process, terminated but not reaped by its parent.
等待I/O的进程的状态一般是“uninterruptible sleep”,或者“D”,我们可以很容易的查找到正在等待I/O的进程
# for x in `seq 1 1 10`; do ps -eo state,pid,cmd | grep "^D"; echo "----"; sleep 5; done
D 248 [jbd2/dm-0-8]
D 16528 bonnie++ -n 0 -u 0 -r 239 -s 478 -f -b -d /tmp
----
D 22 [kswapd0]
D 16528 bonnie++ -n 0 -u 0 -r 239 -s 478 -f -b -d /tmp
----
D 22 [kswapd0]
D 16528 bonnie++ -n 0 -u 0 -r 239 -s 478 -f -b -d /tmp
----
D 22 [kswapd0]
D 16528 bonnie++ -n 0 -u 0 -r 239 -s 478 -f -b -d /tmp
----
D 16528 bonnie++ -n 0 -u 0 -r 239 -s 478 -f -b -d /tmp
上边的例子会循环的输出状态是D的进程,每5秒一次,一共10次
从输出我们可以知道 bonnie++ 的pid是16528 ,在waiting,bonnie++看起来就是我们想找到的进程,但是,只是从它的状态,我们没有办法证明就是bonnie++引起的I/O等待。
为了证明,我们可以可以查看/proc,每个进程目录下都有一个叫io的文件,里边保存这和iotop类似的信息。
# cat /proc/16528/io
rchar: 48752567
wchar: 549961789
syscr: 5967
syscw: 67138
read_bytes: 49020928
write_bytes: 549961728
cancelled_write_bytes: 0
read_bytes和write_bytes是这个进程从磁盘读写的字节,在这个例子中,bonnie++进程读取了46M的数据并且写入了524MB的数据到磁盘上。
4. 找出哪个文件正在被大量写入
lsof 命令可以展示一个进程打开的所有文件。从这个列表中,我们可以找到哪个文件被写入。
# lsof -p 16528
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
bonnie++ 16528 root cwd DIR 252,0 4096 130597 /tmp
<truncated>
bonnie++ 16528 root 8u REG 252,0 501219328 131869 /tmp/Bonnie.16528
bonnie++ 16528 root 9u REG 252,0 501219328 131869 /tmp/Bonnie.16528
bonnie++ 16528 root 10u REG 252,0 501219328 131869 /tmp/Bonnie.16528
bonnie++ 16528 root 11u REG 252,0 501219328 131869 /tmp/Bonnie.16528
bonnie++ 16528 root 12u REG 252,0 501219328 131869 <strong>/tmp/Bonnie.16528</strong>
# df /tmp
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/mapper/workstation-root 7667140 2628608 4653920 37% /
# pvdisplay
--- Physical volume ---
PV Name /dev/sda5
VG Name workstation
PV Size 7.76 GiB / not usable 2.00 MiB
Allocatable yes
PE Size 4.00 MiB
Total PE 1986
Free PE 8
Allocated PE 1978
PV UUID CLbABb-GcLB-l5z3-TCj3-IOK3-SQ2p-RDPW5S
使用pvdisplay可以看到,pv设备就是/dev/sda5,正是我们前面找到的sda。
参考文档:http://bencane.com/2012/08/06/troubleshooting-high-io-wait-in-linux/
⑵ linux如何获取进程的磁盘IO情况
iotop
这个可以针对单个进程进行查看。
#sudo iotop -p $PID -d 1
idstat
和iotop效果一致,不过这个可以监控内存。
#sudo pidstat -p $PID -d 1
网页链接更多详答细的命令可以查看
⑶ Linux系统I/O模型及select、poll、epoll原理和应用
理解Linux的IO模型之前,首先要了解一些基本概念,才能理解这些IO模型设计的依据
操作系统使用虚拟内存来映射物理内存,对于32位的操作系统来说,虚拟地址空间为4G(2^32)。操作系统的核心是内核,为了保护用户进程不能直接操作内核,保证内核安全,操作系统将虚拟地址空间划分为内核空间和用户空间。内核可以访问全部的地址空间,拥有访问底层硬件设备的权限,普通的应用程序需要访问硬件设备必须通过 系统调用 来实现。
对于Linux系统来说,将虚拟内存的最高1G字节的空间作为内核空间仅供内核使用,低3G字节的空间供用户进程使用,称为用户空间。
又被称为标准I/O,大多数文件系统的默认I/O都是缓存I/O。在Linux系统的缓存I/O机制中,操作系统会将I/O的数据缓存在页缓存(内存)中,也就是数据先被拷贝到内核的缓冲区(内核地址空间),然后才会从内核缓冲区拷贝到应用程序的缓冲区(用户地址空间)。
这种方式很明显的缺点就是数据传输过程中需要再应用程序地址空间和内核空间进行多次数据拷贝操作,这些操作带来的CPU以及内存的开销是非常大的。
由于Linux系统采用的缓存I/O模式,对于一次I/O访问,以读操作举例,数据先会被拷贝到内核缓冲区,然后才会从内核缓冲区拷贝到应用程序的缓存区,当一个read系统调用发生的时候,会经历两个阶段:
正是因为这两个状态,Linux系统才产生了多种不同的网络I/O模式的方案
Linux系统默认情况下所有socke都是blocking的,一个读操作流程如下:
以UDP socket为例,当用户进程调用了recvfrom系统调用,如果数据还没准备好,应用进程被阻塞,内核直到数据到来且将数据从内核缓冲区拷贝到了应用进程缓冲区,然后向用户进程返回结果,用户进程才解除block状态,重新运行起来。
阻塞模行下只是阻塞了当前的应用进程,其他进程还可以执行,不消耗CPU时间,CPU的利用率较高。
Linux可以设置socket为非阻塞的,非阻塞模式下执行一个读操作流程如下:
当用户进程发出recvfrom系统调用时,如果kernel中的数据还没准备好,recvfrom会立即返回一个error结果,不会阻塞用户进程,用户进程收到error时知道数据还没准备好,过一会再调用recvfrom,直到kernel中的数据准备好了,内核就立即将数据拷贝到用户内存然后返回ok,这个过程需要用户进程去轮询内核数据是否准备好。
非阻塞模型下由于要处理更多的系统调用,因此CPU利用率比较低。
应用进程使用sigaction系统调用,内核立即返回,等到kernel数据准备好时会给用户进程发送一个信号,告诉用户进程可以进行IO操作了,然后用户进程再调用IO系统调用如recvfrom,将数据从内核缓冲区拷贝到应用进程。流程如下:
相比于轮询的方式,不需要多次系统调用轮询,信号驱动IO的CPU利用率更高。
异步IO模型与其他模型最大的区别是,异步IO在系统调用返回的时候所有操作都已经完成,应用进程既不需要等待数据准备,也不需要在数据到来后等待数据从内核缓冲区拷贝到用户缓冲区,流程如下:
在数据拷贝完成后,kernel会给用户进程发送一个信号告诉其read操作完成了。
是用select、poll等待数据,可以等待多个socket中的任一个变为可读,这一过程会被阻塞,当某个套接字数据到来时返回,之后再用recvfrom系统调用把数据从内核缓存区复制到用户进程,流程如下:
流程类似阻塞IO,甚至比阻塞IO更差,多使用了一个系统调用,但是IO多路复用最大的特点是让单个进程能同时处理多个IO事件的能力,又被称为事件驱动IO,相比于多线程模型,IO复用模型不需要线程的创建、切换、销毁,系统开销更小,适合高并发的场景。
select是IO多路复用模型的一种实现,当select函数返回后可以通过轮询fdset来找到就绪的socket。
优点是几乎所有平台都支持,缺点在于能够监听的fd数量有限,Linux系统上一般为1024,是写死在宏定义中的,要修改需要重新编译内核。而且每次都要把所有的fd在用户空间和内核空间拷贝,这个操作是比较耗时的。
poll和select基本相同,不同的是poll没有最大fd数量限制(实际也会受到物理资源的限制,因为系统的fd数量是有限的),而且提供了更多的时间类型。
总结:select和poll都需要在返回后通过轮询的方式检查就绪的socket,事实上同时连的大量socket在一个时刻只有很少的处于就绪状态,因此随着监视的描述符数量的变多,其性能也会逐渐下降。
epoll是select和poll的改进版本,更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的只需一次。
epoll_create()用来创建一个epoll句柄。
epoll_ctl() 用于向内核注册新的描述符或者是改变某个文件描述符的状态。已注册的描述符在内核中会被维护在一棵红黑树上,通过回调函数内核会将 I/O 准备好的描述符加入到一个就绪链表中管理。
epoll_wait() 可以从就绪链表中得到事件完成的描述符,因此进程不需要通过轮询来获得事件完成的描述符。
当epoll_wait检测到描述符IO事件发生并且通知给应用程序时,应用程序可以不立即处理该事件,下次调用epoll_wait还会再次通知该事件,支持block和nonblocking socket。
当epoll_wait检测到描述符IO事件发生并且通知给应用程序时,应用程序需要立即处理该事件,如果不立即处理,下次调用epoll_wait不会再次通知该事件。
ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。epoll工作在ET模式的时候,必须使用nonblocking socket,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。
【segmentfault】 Linux IO模式及 select、poll、epoll详解
【GitHub】 CyC2018/CS-Notes
⑷ 怎么查看linux的哪个进程占用磁盘io较多
您好,很高兴为您解答。
服务器cpu使用率不高,load比较高,所以要查看一下IO。硬盘可以通过命令vmstat或iostat获得(也可以用yum 安装dstat获得),网络IO可以用iftop命令获取。但是不知道那个进程使用硬盘IO比较高,通过查找没有找到相关命令,只好自己写个脚本进行统计处理。
本脚本在CentOS6下(kernel2.6以上)python2.6测试通过。
直接运行脚本,默认情况下收集3秒钟数据,显示读写最高的前三个进程。如用参数可以使用命令“python fhip.py 4 5 3”,第一个数位每次收集读写数据的间隔秒数,第二个数是打印出读写最多的n个进程,第三个为运行脚本的次数。因为参数部分写的比较简单那,所以用参数必须3个全写。。
#!/bin/python
#-*-coding:utf-8-*-
#Filename:ind_high_io_process
#Revision:1.0
#Date:2013-3-8
#Author:simonzhang
#web:www.simonzhang.net
#Email:[email protected]
###ENDINITINFO
importos
importre
importsys
importtime
fromstringimportstrip
####
sys_proc_path='/proc/'
re_find_process_number='^d+$'
####
#通过/proc/$pid/io获取读写信息
####
defcollect_info():
_tmp={}
re_find_process_dir=re.compile(re_find_process_number)
foriinos.listdir(sys_proc_path):
ifre_find_process_dir.search(i):
#获得进程名
process_name=open("%s%s/stat"%(sys_proc_path,i),"rb").read().split("")[1]
#读取io信息
rw_io=open("%s%s/io"%(sys_proc_path,i),"rb").readlines()
for_infoinrw_io:
cut_info=strip(_info).split(':')
ifstrip(cut_info[0])=="read_bytes":
read_io=int(strip(cut_info[1]))
ifstrip(cut_info[0])=="write_bytes":
write_io=int(strip(cut_info[1]))
_tmp[i]={"name":process_name,"read_bytes":read_io,"write_bytes":write_io}
return_tmp
defmain(_sleep_time,_list_num):
_sort_read_dict={}
_sort_write_dict={}
#获取系统读写数据
process_info_list_frist=collect_info()
time.sleep(_sleep_time)
process_info_list_second=collect_info()
#将读数据和写数据进行分组,写入两个字典中
forloopinprocess_info_list_second.keys():
second_read_v=process_info_list_second[loop]["read_bytes"]
second_write_v=process_info_list_second[loop]["write_bytes"]
try:
frist_read_v=process_info_list_frist[loop]["read_bytes"]
except:
frist_read_v=0
try:
frist_write_v=process_info_list_frist[loop]["write_bytes"]
except:
frist_write_v=0
#计算第二次获得数据域第一次获得数据的差
_sort_read_dict[loop]=second_read_v-frist_read_v
_sort_write_dict[loop]=second_write_v-frist_write_v
#将读写数据进行排序
sort_read_dict=sorted(_sort_read_dict.items(),key=lambda_sort_read_dict:_sort_read_dict[1],reverse=True)
sort_write_dict=sorted(_sort_write_dict.items(),key=lambda_sort_write_dict:_sort_write_dict[1],reverse=True)
#打印统计结果
print"pidprocessread(bytes)pidprocesswrite(btyes)"
for_numinrange(_list_num):
read_pid=sort_read_dict[_num][0]
write_pid=sort_write_dict[_num][0]
res="%s"%read_pid
res+=""*(8-len(read_pid))+process_info_list_second[read_pid]["name"]
res+=""*(12-len(process_info_list_second[read_pid]["name"]))+"%s"%sort_read_dict[_num][1]
res+=""*(12-len("%s"%sort_read_dict[_num][1]))+write_pid
res+=""*(8-len(write_pid))+process_info_list_second[write_pid]["name"]
res+=""*(12-len("%s"%process_info_list_second[write_pid]["name"]))+"%s"%sort_write_dict[_num][1]
printres
print" "*1
if__name__=='__main__':
try:
_sleep_time=sys.argv[1]
except:
_sleep_time=3
try:
_num=sys.argv[2]
except:
_num=3
try:
loop=sys.argv[3]
except:
loop=1
foriinrange(int(loop)):
main(int(_sleep_time),int(_num))
如若满意,请点击【采纳答案】,如若还有问题,请点击【追问】
希望我的回答对您有所帮助,望采纳!
~ O(∩_∩)O~
⑸ linux查看磁盘io的几种方法
linux查看磁盘io的几种方法
怎样才能快速的定位到并发高是由于磁盘io开销大呢?可以通过三种方式:
第一种:用 top 命令 中的cpu 信息观察
Top可以看到的cpu信息有:
Tasks: 29 total, 1 running, 28 sleeping, 0 stopped, 0 zombie
Cpu(s): 0.3% us, 1.0% sy, 0.0% ni, 98.7% id, 0.0% wa, 0.0% hi, 0.0% si
具体的解释如下:
Tasks: 29 total 进程总数
1 running 正在运行的进程数
28 sleeping 睡眠的进程数
0 stopped 停止的进程数
0 zombie 僵尸进程数
Cpu(s):
0.3% us 用户空间占用CPU百分比
1.0% sy 内核空间占用CPU百分比
0.0% ni 用户进程空间内改变过优先级的进程占用CPU百分比
98.7% id 空闲CPU百分比
0.0% wa 等待输入输出的CPU时间百分比
0.0% hi
0.0% si
0.0% wa 的百分比可以大致的体现出当前的磁盘io请求是否频繁。如果 wa的数量比较大,说明等待输入输出的的io比较多。
第二种:用vmstat
vmstat 命令报告关于线程、虚拟内存、磁盘、陷阱和 CPU 活动的统计信息。由 vmstat 命令生成的报告可以用于平衡系统负载活动。系统范围内的这些统计信息(所有的处理器中)都计算出以百分比表示的平均值,或者计算其总和。
输入命令:
vmstat 2 5
如果发现等待的进程和处在非中断睡眠状态的进程数非常多,并且发送到块设备的块数和从块设备接收到的块数非常大,那就说明磁盘io比较多。
vmstat参数解释:
Procs
r: 等待运行的进程数 b: 处在非中断睡眠状态的进程数 w: 被交换出去的可运行的进程数。此数由 linux 计算得出,但 linux 并不耗尽交换空间
Memory
swpd: 虚拟内存使用情况,单位:KB
free: 空闲的内存,单位KB
buff: 被用来做为缓存的内存数,单位:KB
Swap
si: 从磁盘交换到内存的交换页数量,单位:KB/秒
so: 从内存交换到磁盘的交换页数量,单位:KB/秒
IO
bi: 发送到块设备的块数,单位:块/秒
bo: 从块设备接收到的块数,单位:块/秒
System
in: 每秒的中断数,包括时钟中断
cs: 每秒的环境(上下文)切换次数
CPU
按 CPU 的总使用百分比来显示
us: CPU 使用时间
sy: CPU 系统使用时间
id: 闲置时间
准测
更多vmstat使用信息
第二种:用iostat
安装:
Iostat 是 sysstat 工具集的一个工具,需要安装。
Centos的安装方式是:
yum install sysstat
Ubuntu的安装方式是:
aptitude install sysstat
使用:
iostat -dx 显示磁盘扩展信息
root@fileapp:~# iostat -dx
r/s 和 w/s 分别是每秒的读操作和写操作,而rKB/s 和wKB/s 列以每秒千字节为单位显示了读和写的数据量
如果这两对数据值都很高的话说明磁盘io操作是很频繁。
+++++++++++++++++++++++++++++++++++++
linux wa%过高,iostat查看io状况
1, 安装 iostat
yum install sysstat
之后就可以使用 iostat 命令了,
2,入门使用
iostat -d -k 2
参数 -d 表示,显示设备(磁盘)使用状态;-k某些使用block为单位的列强制使用Kilobytes为单位;2表示,数据显示每隔2秒刷新一次。
tps:该设备每秒的传输次数(Indicate the number of transfers per second that were issued to the device.)。"一次传输"意思是"一次I/O请求"。多个逻辑请求可能会被合并为"一次I/O请求"。"一次传输"请求的大小是未知的。kB_read/s:每秒从设备(drive expressed)读取的数据量;
kB_wrtn/s:每秒向设备(drive expressed)写入的数据量;
kB_read:读取的总数据量;kB_wrtn:写入的总数量数据量;这些单位都为Kilobytes。
指定监控的设备名称为sda,该命令的输出结果和上面命令完全相同。
iostat -d sda 2
默认监控所有的硬盘设备,现在指定只监控sda。
3, -x 参数
iostat还有一个比较常用的选项 -x ,该选项将用于显示和io相关的扩展数据。
iostat -d -x -k 1 10
输出信息的含义
。
4, 常见用法
iostat -d -k 1 10 #查看TPS和吞吐量信息(磁盘读写速度单位为KB)
iostat -d -m 2 #查看TPS和吞吐量信息(磁盘读写速度单位为MB)
iostat -d -x -k 1 10 #查看设备使用率(%util)、响应时间(await) iostat -c 1 10 #查看cpu状态
5, 实例分析
iostat -d -k 1 | grep vda
Device: tps kB_read/s kB_wrtn/s kB_read kB_wrtn
sda10 60.72 18.95 71.53 395637647 1493241908
sda10 299.02 4266.67 129.41 4352 132
sda10 483.84 4589.90 4117.17 4544 4076
sda10 218.00 3360.00 100.00 3360 100
sda10 546.00 8784.00 124.00 8784 124
sda10 827.00 13232.00 136.00 13232 136
上面看到,磁盘每秒传输次数平均约400;每秒磁盘读取约5MB,写入约1MB。
iostat -d -x -k 1
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s rkB/s wkB/s avgrq-sz avgqu-sz await svctm %util
sda 1.56 28.31 7.84 31.50 43.65 3.16 21.82 1.58 1.19 0.03 0.80 2.61 10.29
sda 1.98 24.75 419.80 6.93 13465.35 253.47 6732.67 126.73 32.15 2.00 4.70 2.00 85.25
sda 3.06 41.84 444.90 54.08 14204.08 2048.98 7102.04 1024.49 32.57 2.10 4.21 1.85 92.24
可以看到磁盘的平均响应时间<5ms,磁盘使用率>80。磁盘响应正常,但是已经很繁忙了。
可以看到磁盘的平均响应时间<5ms,磁盘使用率>90。磁盘响应正常,但是已经很繁忙了。
await: 每一个IO请求的处理的平均时间(单位是微秒毫秒)。这里可以理解为IO的响应时间,一般地系统IO响应时间应该低于5ms,如果大于10ms就比较大了
svctm 表示平均每次设备I/O操作的服务时间(以毫秒为单位)。如果svctm的值与await很接近,表示几乎没有I/O等待,磁盘性能很好,
如果await的值远高于svctm的值,则表示I/O队列等待太长, 系统上运行的应用程序将变慢。
%util: 在统计时间内所有处理IO时间,除以总共统计时间
所以该参数暗示了设备的繁忙程度
。一般地,如果该参数是100%表示设备已经接近满负荷运行了(当然如果是多磁盘,即使%util是100%,因为磁盘的并发能力,所以磁盘使用未必就到了瓶颈)。
也可以使用下面的命令,同时显示cpu和磁盘的使用情况
等待时间超过5ms, 磁盘io有问题
⑹ Linux 磁盘IO
磁盘结构与数据存储方式, 数据是如何存储的,又通过怎样的方式被访问?
机械硬盘主要由磁盘盘片、磁头、主轴与传动轴等组成;数据就存放在磁盘盘片中
现代硬盘寻道都是采用CHS( Cylinder Head Sector )的方式,硬盘读取数据时,读写磁头沿径向移动,移到要读取的扇区所在磁道的上方,这段时间称为 寻道时间(seek time) 。 因读写磁头的起始位置与目标位置之间的距离不同,寻道时间也不同 。磁头到达指定磁道后,然后通过盘片的旋转,使得要读取的扇区转到读写磁头的下方,这段时间称为 旋转延迟时间(rotational latencytime) 。然后再读写数据,读写数据也需要时间,这段时间称为 传输时间(transfer time) 。
固态硬盘主要由主控芯片、闪存颗粒与缓存组成;数据就存放在闪存芯片中
通过主控芯片进行寻址, 因为是电信号方式, 没有任何物理结构, 所以寻址速度非常快且与数据存储位置无关
如何查看系统IO状态
查看磁盘空间
调用 open , fwrite 时到底发生了什么?
在一个IO过程中,以下5个API/系统调用是必不可少的
Create 函数用来打开一个文件,如果该文件不存在,那么需要在磁盘上创建该文件
Open 函数用于打开一个指定的文件。如果在 Open 函数中指定 O_CREATE 标记,那么 Open 函数同样可以实现 Create 函数的功能
Clos e函数用于释放文件句柄
Write 和 Read 函数用于实现文件的读写过程
O_SYNC (先写缓存, 但是需要实际落盘之后才返回, 如果接下来有读请求, 可以从内存读 ), write-through
O_DSYNC (D=data, 类似O_SYNC, 但是只同步数据, 不同步元数据)
O_DIRECT (直接写盘, 不经过缓存)
O_ASYNC (异步IO, 使用信号机制实现, 不推荐, 直接用aio_xxx)
O_NOATIME (读取的时候不更新文件 atime(access time))
sync() 全局缓存写回磁盘
fsync() 特定fd的sync()
fdatasync() 只刷数据, 不同步元数据
mount noatime(全局不记录atime), re方式(只读), sync(同步方式)
一个IO的传奇一生 这里有一篇非常好的资料,讲述了整个IO过程;
下面简单记录下自己的理解的一次常见的Linux IO过程, 想了解更详细及相关源码,非常推荐阅读上面的原文
Linux IO体系结构
[站外图片上传中...(image-38a7b-1644137945193)]
Superblock 超级描述了整个文件系统的信息。为了保证可靠性,可以在每个块组中对superblock进行备份。为了避免superblock冗余过多,可以采用稀疏存储的方式,即在若干个块组中对superblock进行保存,而不需要在所有的块组中都进行备份
GDT 组描述符表 组描述符表对整个组内的数据布局进行了描述。例如,数据块位图的起始地址是多少?inode位图的起始地址是多少?inode表的起始地址是多少?块组中还有多少空闲块资源等。组描述符表在superblock的后面
数据块位图 数据块位图描述了块组内数据块的使用情况。如果该数据块已经被某个文件使用,那么位图中的对应位会被置1,否则该位为0
Inode位图 Inode位图描述了块组内inode资源使用情况。如果一个inode资源已经使用,那么对应位会被置1
Inode表 (即inode资源)和数据块。这两块占据了块组内的绝大部分空间,特别是数据块资源
一个文件是由inode进行描述的。一个文件占用的数据块block是通过inode管理起来的 。在inode结构中保存了直接块指针、一级间接块指针、二级间接块指针和三级间接块指针。对于一个小文件,直接可以采用直接块指针实现对文件块的访问;对于一个大文件,需要采用间接块指针实现对文件块的访问
最简单的调度器。它本质上就是一个链表实现的 fifo 队列,并对请求进行简单的 合并 处理。
调度器本身并没有提供任何可以配置的参数
读写请求被分成了两个队列, 一个用访问地址作为索引,一个用进入时间作为索引,并且采用两种方式将这些request管理起来;
在请求处理的过程中,deadline算法会优先处理那些访问地址临近的请求,这样可以最大程度的减少磁盘抖动的可能性。
只有在有些request即将被饿死的时候,或者没有办法进行磁盘顺序化操作的时候,deadline才会放弃地址优先策略,转而处理那些即将被饿死的request
deadline算法可调整参数
read_expire : 读请求的超时时间设置(ms)。当一个读请求入队deadline的时候,其过期时间将被设置为当前时间+read_expire,并放倒fifo_list中进行排序
write_expire :写请求的超时时间设置(ms)
fifo_batch :在顺序(sort_list)请求进行处理的时候,deadline将以batch为单位进行处理。每一个batch处理的请求个数为这个参数所限制的个数。在一个batch处理的过程中,不会产生是否超时的检查,也就不会产生额外的磁盘寻道时间。这个参数可以用来平衡顺序处理和饥饿时间的矛盾,当饥饿时间需要尽可能的符合预期的时候,我们可以调小这个值,以便尽可能多的检查是否有饥饿产生并及时处理。增大这个值当然也会增大吞吐量,但是会导致处理饥饿请求的延时变长
writes_starved :这个值是在上述deadline出队处理第一步时做检查用的。用来判断当读队列不为空时,写队列的饥饿程度是否足够高,以时deadline放弃读请求的处理而处理写请求。当检查存在有写请求的时候,deadline并不会立即对写请求进行处理,而是给相关数据结构中的starved进行累计,如果这是第一次检查到有写请求进行处理,那么这个计数就为1。如果此时writes_starved值为2,则我们认为此时饥饿程度还不足够高,所以继续处理读请求。只有当starved >= writes_starved的时候,deadline才回去处理写请求。可以认为这个值是用来平衡deadline对读写请求处理优先级状态的,这个值越大,则写请求越被滞后处理,越小,写请求就越可以获得趋近于读请求的优先级
front_merges :当一个新请求进入队列的时候,如果其请求的扇区距离当前扇区很近,那么它就是可以被合并处理的。而这个合并可能有两种情况,一个是向当前位置后合并,另一种是向前合并。在某些场景下,向前合并是不必要的,那么我们就可以通过这个参数关闭向前合并。默认deadline支持向前合并,设置为0关闭
在调度一个request时,首先需要选择一个一个合适的cfq_group。Cfq调度器会为每个cfq_group分配一个时间片,当这个时间片耗尽之后,会选择下一个cfq_group。每个cfq_group都会分配一个vdisktime,并且通过该值采用红黑树对cfq_group进行排序。在调度的过程中,每次都会选择一个vdisktime最小的cfq_group进行处理。
一个cfq_group管理了7棵service tree,每棵service tree管理了需要调度处理的对象cfq_queue。因此,一旦cfq_group被选定之后,需要选择一棵service tree进行处理。这7棵service tree被分成了三大类,分别为RT、BE和IDLE。这三大类service tree的调度是按照优先级展开的
通过优先级可以很容易的选定一类Service tree。当一类service tree被选定之后,采用service time的方式选定一个合适的cfq_queue。每个Service tree是一棵红黑树,这些红黑树是按照service time进行检索的,每个cfq_queue都会维护自己的service time。分析到这里,我们知道,cfq算法通过每个cfq_group的vdisktime值来选定一个cfq_group进行服务,在处理cfq_group的过程通过优先级选择一个最需要服务的service tree。通过该Service tree得到最需要服务的cfq_queue。该过程在 cfq_select_queue 函数中实现
一个cfq_queue被选定之后,后面的过程和deadline算法有点类似。在选择request的时候需要考虑每个request的延迟等待时间,选择那种等待时间最长的request进行处理。但是,考虑到磁盘抖动的问题,cfq在处理的时候也会进行顺序批量处理,即将那些在磁盘上连续的request批量处理掉
cfq调度算法的参数
back_seek_max :磁头可以向后寻址的最大范围,默认值为16M
back_seek_penalty :向后寻址的惩罚系数。这个值是跟向前寻址进行比较的
fifo_expire_async :设置异步请求的超时时间。同步请求和异步请求是区分不同队列处理的,cfq在调度的时候一般情况都会优先处理同步请求,之后再处理异步请求,除非异步请求符合上述合并处理的条件限制范围内。当本进程的队列被调度时,cfq会优先检查是否有异步请求超时,就是超过fifo_expire_async参数的限制。如果有,则优先发送一个超时的请求,其余请求仍然按照优先级以及扇区编号大小来处理
fifo_expire_sync :这个参数跟上面的类似,区别是用来设置同步请求的超时时间
slice_idle :参数设置了一个等待时间。这让cfq在切换cfq_queue或service tree的时候等待一段时间,目的是提高机械硬盘的吞吐量。一般情况下,来自同一个cfq_queue或者service tree的IO请求的寻址局部性更好,所以这样可以减少磁盘的寻址次数。这个值在机械硬盘上默认为非零。当然在固态硬盘或者硬RAID设备上设置这个值为非零会降低存储的效率,因为固态硬盘没有磁头寻址这个概念,所以在这样的设备上应该设置为0,关闭此功能
group_idle :这个参数也跟上一个参数类似,区别是当cfq要切换cfq_group的时候会等待一段时间。在cgroup的场景下,如果我们沿用slice_idle的方式,那么空转等待可能会在cgroup组内每个进程的cfq_queue切换时发生。这样会如果这个进程一直有请求要处理的话,那么直到这个cgroup的配额被耗尽,同组中的其它进程也可能无法被调度到。这样会导致同组中的其它进程饿死而产生IO性能瓶颈。在这种情况下,我们可以将slice_idle = 0而group_idle = 8。这样空转等待就是以cgroup为单位进行的,而不是以cfq_queue的进程为单位进行,以防止上述问题产生
low_latency :这个是用来开启或关闭cfq的低延时(low latency)模式的开关。当这个开关打开时,cfq将会根据target_latency的参数设置来对每一个进程的分片时间(slice time)进行重新计算。这将有利于对吞吐量的公平(默认是对时间片分配的公平)。关闭这个参数(设置为0)将忽略target_latency的值。这将使系统中的进程完全按照时间片方式进行IO资源分配。这个开关默认是打开的
target_latency :当low_latency的值为开启状态时,cfq将根据这个值重新计算每个进程分配的IO时间片长度
quantum :这个参数用来设置每次从cfq_queue中处理多少个IO请求。在一个队列处理事件周期中,超过这个数字的IO请求将不会被处理。这个参数只对同步的请求有效
slice_sync :当一个cfq_queue队列被调度处理时,它可以被分配的处理总时间是通过这个值来作为一个计算参数指定的。公式为: time_slice = slice_sync + (slice_sync/5 * (4 - prio)) 这个参数对同步请求有效
slice_async :这个值跟上一个类似,区别是对异步请求有效
slice_async_rq :这个参数用来限制在一个slice的时间范围内,一个队列最多可以处理的异步请求个数。请求被处理的最大个数还跟相关进程被设置的io优先级有关
通常在Linux上使用的IO接口是同步方式的,进程调用 write / read 之后会阻塞陷入到内核态,直到本次IO过程完成之后,才能继续执行,下面介绍的异步IO则没有这种限制,但是当前Linux异步IO尚未成熟
目前Linux aio还处于较不成熟的阶段,只能在 O_DIRECT 方式下才能使用(glibc_aio),也就是无法使用默认的Page Cache机制
正常情况下,使用aio族接口的简要方式如下:
io_uring 是 2019 年 5 月发布的 Linux 5.1 加入的一个重大特性 —— Linux 下的全新的异步 I/O 支持,希望能彻底解决长期以来 Linux AIO 的各种不足
io_uring 实现异步 I/O 的方式其实是一个生产者-消费者模型:
逻辑卷管理
RAID0
RAID1
RAID5(纠错)
条带化
Linux系统性能调整:IO过程
Linux的IO调度
一个IO的传奇一生
理解inode
Linux 文件系统是怎么工作的?
Linux中Buffer cache性能问题一探究竟
Asynchronous I/O and event notification on linux
AIO 的新归宿:io_uring
Linux 文件 I/O 进化史(四):io_uring —— 全新的异步 I/O
⑺ linux怎样查看那个进程占用网络io
1.使用iotop命令
使用该命令有个条件,Linux内核要高于2.6.20的版本,版本过低则没有此命令,执行专效果如下图所属示:
2:block_mp方法
首先,关闭syslog服务,然后开启block_mp,最后正则表达式提取dmesg信息。
/etc/init.d/syslog stop
echo 1 > /proc/sys/vm/block_mp
dmesg | egrep "READ|WRITE|dirtied" | egrep -o '([a-zA-Z]*)' | sort | uniq -c | sort -rn | head
执行结果如下图所示:
注意:操作完成后请关闭block_mp和启动syslog
echo 0 > /proc/sys/vm/block_mp #关闭block_mp
/etc/init.d/syslog start #启动syslog
⑻ 在linux系统中如何查看cpu和io
在 Linux 系统中,可以使用以下命令查看 CPU 信息:
top: 显示系统进程的实时状态
htop: 与 top 类似,但提供了更多的信息和更好的可视化
mpstat: 显示多核 CPU 的状态
lscpu: 显示系统 CPU 的配置信息
查看 IO 信息,可以使用以下命令:
iostat : 用于检测磁盘I/O的使用状况
vmstat : 用于检测虚拟内存的使用状况
mpstat : 用于检测 CPU 和磁盘I/O的使用状况
dstat : 用于检测磁盘I/O,网络,CPU等系统资源的使用状况
需要注意的是这些命令需要安装对应的工具包