cgroup其他部分 IO + hugepage

cgroup还有其他一些限制特性,如io,pid,hugetlb等,这些用处不多,参见Cgroupv1。下面介绍下与系统性能相关的io和hugepage,cgroup的io介绍参考Cgroup - Linux的IO资源隔离

linux IO

linux io涉及到对文件(磁盘设备)的读写性能,对io的优化主要分为

  • 磁盘相关参数调优
  • 文件系统参数调优,一般为io调度器选择和调度器参数调优
  • 文件系统挂载(mount)参数调优

使用如下命令查看当前系统支持的调度器

# dmesg | grep -i scheduler
[    1.343756] io scheduler noop registered
[    1.343759] io scheduler deadline registered (default)
[    1.343784] io scheduler cfq registered
[    1.343787] io scheduler mq-deadline registered
[    1.343789] io scheduler kyber registered

使用如下命令查看当前磁盘支持的调度器,方括号表示当前生效的调度器

#  cat /sys/block/sda/queue/scheduler
noop [deadline] cfq

调度器的参数位于/sys/block/sda/queue/iosched/

Noop:该算法实现了最简单的FIFO队列,所有I/O请求大致按照先来后到的顺序进行操作,特别适用于固态硬盘

  • 在IO调度器下方有更加智能的IO调度设备。如果您的Block Device Drivers是Raid,或者SAN,NAS等存储设备,这些设备会更好地组织IO请求,不用IO调度器去做额外的调度工作;
  • 上层应用程序比IO调度器更懂底层设备。或者说上层应用程序到达IO调度器的IO请求已经是它经过精心优化的,那么IO调度器就不需要画蛇添足,只需要按序执行上层传达下来的IO请求即可。
  • 对于一些非旋转磁头氏的存储设备,使用Noop的效果更好。因为对于旋转磁头式的磁盘来说,IO调度器的请求重组要花费一定的CPU时间,但是对于SSD磁盘来说,这些重组IO请求的CPU时间可以节省下来,因为SSD提供了更智能的请求调度算法,不需要内核去画蛇添足。这篇文章提及了SSD中使用Noop效果会更好。

CFQ:给所有进程分配等同的块设备使用的时间片,进程在时间片内,可以将产生的IO请求提交给块设备进行处理,时间片结束,进程的请求将排进它自己的队列,等待下次调度的时候进行处理,适用于通用服务器。

Deadline:保证每个IO请求在一定的时间内一定要被服务到,以此来避免某个请求饥饿,deadline在大多数多线程应用场景和数据库应用场景下表现的性能要优于cfq

可以通过直接修改/sys/block/sda/queue/scheduler文件来临时修改调度器类型,永久修改可以参考如下命令

grubby --grub --update-kernel=ALL --args="elevator=deadline"

deadline使用了3个队列,”standerd queue“中按照block号排序,”read FIFO”和“write FIFO“则按照request请求的顺序添加,read FIFO的超时时间为500ms,write FIFO的超时时间为5s。系统会按照顺序处理standerd queue中的请求,当read FIFO或write FIFO超时后会切换到对应的FIFO处理request。这种处理方式用于防止”request starvation“,即系统可能会在某个block上处理多个request,而导致其他block上的request超时。更多详情参见Kernel Korner - I/O Schedulers,需要注意的是Anticipatory I/O Scheduler已经被废弃。

在遇到系统读写过慢时,可以采用如下步骤进行定位。(注:该系统的模拟故障由dd if=/dev/zero of=loadfile bs=1M count=5000触发):

  • 首先使用top命令查看当前io等待情况,如下可以看到当前io等待时间为84.7 
Tasks:  77 total,2 running,1)">75 sleeping,1)">0 stopped,1)">0 zombie
%Cpu(s):  0.7 us,1)">14.6 sy,1)">0.0 ni,1)">0.0 id,1)">84.7 wa,1)">0.0 hi,1)">0.0 si,1)">0.0 st
KiB Mem :  1014908 total,1)">63716 free,1)">69912 used,1)">881280 buff/cache
KiB Swap:        0 total,1)">0 free,1)">0 used.   779008 avail Mem
  • 使用iostat查看具体磁盘使用情况(当前系统只有一块磁盘),主要关注以下几个值

util:采集周期内用于IO操作的时间比率,该值过高直接说明IO占比过高,下面util已经升到了97.19%,IO已经很高了

await:平均每次IO请求等待时间(包括等待时间和处理时间,毫秒为单位),该值过高说明IO队列可能比较长,IO队列长度可以参考avgqu-sz(平均等待处理的IO请求队列长度)

svctm:平均每次IO请求的处理时间(毫秒为单位),正常情况下该值与await相差不大,差距越大,越说明IO过高。

IO有读写之分,如下例中明显写操作IO影响了性能,且设备为磁盘sda(iostat -x -k -d 1 2

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               0.00     0.00    1.04  227.08     4.17 108850.00   954.34   124.40 1056.26   87.00 1060.71   4.26  97.19
    •  使用iotop -P查看当前IO占用率最高的进程,可以看到是一个PID为15162的进程,其IO达到了80.05%
Total DISK READ :       0.00 B/s | Total DISK WRITE :     403.22 M/s
Actual DISK READ:       0.00 B/s | Actual DISK WRITE:     411.90 M/s
   PID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO>    COMMAND
 15162 be/4 root        0.00 B/s  403.22 M/s  0.00 % 80.05 % dd if=/dev/zero...
 ...

使用lsof查看该进程打开的文件,可以看到/home/loadfile的大小达到了4564156416

# lsof -p 15394
lsof: WARNING: can't stat() fuse.gvfsd-fuse file system /run/user/1000/gvfs
      Output information may be incomplete.
COMMAND   PID USER   FD   TYPE DEVICE   SIZE/OFF     NODE NAME
dd      15394 root  cwd    DIR  253,1)">0         63 50444361 /home
dd      15394 root  rtd    DIR  0        238       64 /
dd      15394 root  txt    REG  0      74952 50454589 /usr/bin/dd
dd      15394 root  mem    REG  0  106075056 17088179 /usr/lib/locale/locale-archive
dd      0    2151672   162056 /usr/lib64/libc-2.17.so
dd      0     163400   162049 /usr/lib64/ld-15394 root    0r   CHR    1,1)">5        0t0     6479 /dev/zero
dd      15394 root    1w   REG  0 4564156416 52606435 /home/loadfile
dd      15394 root    2u   CHR  136,1)">0        0t0        3 /dev/pts/0

确定/home/loadfile文件的挂载路径,为根路径

# df /home/
Filesystem              1K-blocks     Used Available Use% Mounted on
/dev/mapper/centos-root  17811456 10109916   7701540  57% /

查看根路径挂载的磁盘(本例中为一块磁盘,可以忽略该步骤),为发生IO性能问题的磁盘sda,到此基本可以确认是因为进程15162引发了IO问题。注:可以使用如“time dd if=/dev/zero bs=1M count=2048 of=direct_2G”命令测试磁盘性能

# lsblk
NAME            MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda               8:0   20G   disk
├─sda1            1    0    1G  0 part /boot
└─sda2            2    0   19G   part
  ├─centos-root 253:0   17G  0 lvm  /
  └─centos-swap 0    2G   lvm  [SWAP]

 

linux 大页

Linux采用了通用的分页模型,用以减少进程使用的页表以及增加对内存的索引。在Linux-2.6.10版本中,Linux采用了三级分页模型. 而从2.6.11开始普遍采用了四级分页模型,32位系统中取消了PUD和PMD,为二级分页模型。注:每个进程都有其自身的页面目录PGD

    1. 页全局目录(Page Global Directory)
    2. 页上级目录(Page Upper Directory)
    3. 页中间目录(Page Middle Directory)
    4. 页表(Page Table)

下面是一个线性地址包含的各字段以及各字段的用途

注:CR3中含有页目录表物理内存基地址,因此该寄存器也被称为页目录基地址寄存器PDBR(Page-Directory Base address Register),用于定位进程PGD

对于32位系统来说,如果采用4K的分页大小,每个PGD含1024个目录,每个目录含1024个PT,每个PT可寻址4K物理内存,总计4G。从上述可以看出,当进程需要访问实际物理内存时需要经过多级页才行,为了增加地址访问效率,linux使用了一种页缓存,TLB(translation lookaside buffer)。当需要访问物理地址时会首先从TLB中寻找,若找到则称为TLB hit,否则称为TLB miss。

 

这篇文章描述了使用系统默认4k分页下出现的性能问题,总结下来就是:64位系统下,一个进程访问的内存空间变大时,其PGD里面的页表项也会变大,如一个进程访问一个12G内存时,其页表需要24M,当300个进程同时需要访问这12G内存时,总的页表达到了7200MB,这么大的页表不仅占用大量内存空间,页增加了地址访问的负担(TLB miss也会大大增加)。解决办法是使用大页内存,通过增加单个分页大小,进而减少总体需要的分页的数目(同时也提升了TLB hit的几率)

可以看出,大页内存一般用于64位系统或32位系统启用了PAE(内存地址扩展)的情况。页表的使用情况可以参见/proc/meminfo中的PageTables字段

下面描述来自这里

典型地,内存管理器在 x86 系统上处理的内存页为 4 KB。实际的页大小是与体系结构相关的。对大部分用途来说,内存管理器以这样大小的页来管理内存是最有效的。不过,有一些应用程序要使用特别多的内存。大型数据库就是其中一个常见的例子。由于每个页都要由每个进程映射,必须创建页表条目来将虚拟地址映射到物理地址。如果您的一个进程要使用 4KB 的页来映射 1 GB 内存,这将用到 262,144 个页表条目来保持对那些页的追踪。如果每个页表条目消耗 8 个字节,那些每映射 1 GB 内存需要 2 MB 的开销。这本身就已经是非常可观的开销了,不过,如果有多个进程共享那些内存时,问题会变得更严重。在这种情况下,每个映射到同一块 1 GB 内存的进程将为页表条目付出自己 2 MB 的代价。如果有足够多的进程,内存在开销上的浪费可能会超过应用程序请求使用的内存数量。

解决这一问题的一个方法是使用更大的页。大部分新的处理器都支持至少一个小的和一个大的内存页大小。在 x86 上,大内存页的大小是 4 MB,或者,在物理地址扩展(PAE)打开的系统上是 2 MB。假定在前面的中使用页大小为 4 MB 的大内存页,同样 1 GB 内存只用 256 个页表条目就可以映射,而不需要 262,144 个。这样开销从 2 MB 变为 2,048 个字节。

大内存页的使用还可以通过减少 变换索引缓冲(translation lookaside buffer,TLB)的失败次数来提高性能。TLB 是一种页表的高速缓存,让那些在表中列出的页可以更快地进行虚拟地址到物理地址的转换。大内存页可以用更少的实际页来提供更多的内存,相当于较小的页大小,使用的大内存页越多,就有越多的内存可以通过 TLB 引用。

 

TIPS:

  • 衡量IO资源的时候,一般使用两个单位,一个是数据读写的带宽,另一个是数据读写的IOPS。带宽就是以时间为单位的读写数据量,比如,100Mbyte/s。而IOPS是以时间为单位的读写次数。
  • docker对io的的设置参数为--blkio-weight和--blkio-weight-device,一个是针对所有设备的,另一个是针对特定设备的,设置的都是相对比例,设置范围为0-1000
  • IO调度算法主要用于在机械硬盘场景下减少磁头寻址造成的性能浪费,磁头寻址时间一般以毫秒为单位。3~15ms

 

参考:

https://blog.csdn.net/gzh0222/article/details/8666657

https://www.ibm.com/developerworks/cn/linux/l-lo-io-scheduler-optimize-performance/index.html

https://zorro.gitbooks.io/poor-zorro-s-linux-book/content/linuxde-io-diao-du.html

http://bencane.com/2012/08/06/troubleshooting-high-io-wait-in-linux/

https://www.ibm.com/developerworks/cn/linux/l-memmod/index.html

https://www.ibm.com/developerworks/cn/linux/l-cn-hugetlb/

https://github.com/gatieme/LDD-LinuxDeviceDrivers/blob/master/study/kernel/02-memory/02-pagetable/01-develop/README.md

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


最近一直在开发Apworks框架的案例代码,同时也在一起修复Apworks框架中的Bug和一些设计上的不足。遇到的一个普遍问题是,代码的调试过程需要依赖很多外部系统,比如MongoDB、PostgreSQL、RabbitMQ等。当然可以在本机逐一安装这些服务,然后对服务进行配置,使其满足自己开发调试
最近每天都在空闲时间努力编写Apworks框架的案例代码WeText。在文本发布和处理微服务中,我打算使用微软的SQL Server for Linux来做演示,于是也就在自己的docker-compose中加入了MS SQL Server的服务。其实在Docker中运行SQL Server是非常容
在《Kubernetes中分布式存储Rook-Ceph部署快速演练》文章中,我快速介绍了Kubernetes中分布式存储Rook-Ceph的部署过程,这里介绍如何在部署于Kubernetes的ASP.NET Core MVC的应用程序中使用Rook-Ceph所创建的存储对象。 构建ASP.NET C
最近在项目中有涉及到Kubernetes的分布式存储部分的内容,也抽空多了解了一些。项目主要基于Rook-Ceph运行,考虑到Rook-Ceph部署也不那么简单,官方文档的步骤起点也不算低,因此,在整合官方文档的某些步骤的基础上,写篇文章简单总结一下。 Rook-Ceph是Kubernetes中分布
CentOS下Docker与.netcore(一) 之 安装 CentOS下Docker与.netcore(二) 之 Dockerfile CentOS下Docker与.netcore(三)之 三剑客之一Docker-Compose CentOS下Docker与.netcore(四)之 三剑客之一D
CentOS下Docker与.netcore(一) 之 安装 CentOS下Docker与.netcore(二) 之 Dockerfile CentOS下Docker与.netcore(三)之 三剑客之一Docker-Compose CentOS下Docker与.netcore(四)之 三剑客之一D
构建镜像最具挑战性的一点是使镜像大小尽可能的小。Dockerfile中的每条指令都为图像添加了一个图层,您需要记住在移动到下一层之前清理任何不需要的工件。对于多阶段构建,您可以在Dockerfile中使用多个FROM语句。每个FROM指令可以使用不同的基础,并且每个指令都开始一个新的构建。您可以选择
本文介绍compose配置文件参数的使用,熟练编写compose文件 [root@docker lnmp]# cat lnmp.yaml version: '3' services: nginx: build: /root/docker_demo/nginx/ ports: - &q
环境 docker-machine主机:192.168.1.9 docker主机:192.168.1.10 步骤: 安装docker-machine 创建ssh密钥对,实现两主机无密登录 创建docker主机,命名host1 变更docker环境变量 运行容器查看两端是否同步 镜像容器同步测试成功
CentOS下Docker与.netcore(一) 之 安装 CentOS下Docker与.netcore(二) 之 Dockerfile CentOS下Docker与.netcore(三)之 三剑客之一Docker-Compose CentOS下Docker与.netcore(四)之 三剑客之一D
https://blog.csdn.net/wanglei_storage/article/details/77508620 实践中会发现,生产环境中使用单个 Docker 节点是远远不够的,搭建 Docker 集群势在必行。然而,面对 Kubernetes, Mesos 以及 Swarm 等众多容
1.引言 紧接上篇.NET Core容器化@Docker,这一节我们先来介绍如何使用Nginx来完成.NET Core应用的反向代理,然后再介绍多容器应用的部署问题。 2. Why Need Nginx .NET Core中默认的Web Server为Kestrel。 Kestrel is grea
docker rm `docker ps -a | grep Exited | awk '{print $1}'` 删除异常停止的docker容器 docker rmi -f `docker images | grep '<none>' | awk &#3
什么是Docker Compose 在微服务盛行的今天,我们通常是这么定义Compose的:对容器的统一启动和关闭的编排工具。 但是我以前还是有个疑惑,谁会用Compose在一台服务器上部署多个服务呢?干脆直接用单体服务就行了!直到我遇到了以下的一个需求,让我明白了在一台服务器上不得不用多个服务的时
CentOS下Docker与.netcore(一) 之 安装 CentOS下Docker与.netcore(二) 之 Dockerfile CentOS下Docker与.netcore(三)之 三剑客之一Docker-Compose CentOS下Docker与.netcore(四)之 三剑客之一D
很多时候,我们在本地开发过程中程序运行很正常,但是发布到线上之后由于环境的原因,可能会有一些异常。通常我们会通过日志来分析问题,除了日志还有一种常用的调试手段就是:附加进程。 VS中的附加进程非常强大,目前提供了9种常用的附加方式。 在当前.Net Core支持跨平台的大背景下,其中Linux环境和
https://www.cnblogs.com/bigberg/p/8867326.html 一、简介 Docker有个编排工具docker-compose,可以将组成某个应该的多个docker容器编排在一起,同时管理。同样在Swarm集群中,可以使用docker stack 将一组相关联的服务进行
.Net6中想实现对某个网址截屏,可通过Selenium模拟访问网址并实现截图。 实现 安装Nuget包 <PackageReference Include="Selenium.Chrome.WebDriver" Version="85.0.0" /&g
原文 https://www.cnblogs.com/gispathfinder/p/5871043.html 我们在使用docker run创建Docker容器时,可以用--net选项指定容器的网络模式,Docker有以下4种网络模式: host模式,使用--net=host指定。 co