티스토리 뷰

System Story/CentOS 5,6

Shell - Dirty Cache Disk Flushing

불량펭귄 helperchoi 2015. 3. 10. 19:14

2015.03.10 : 최초작성

2016.05.12 : dirty_info.sh OS Version별  분기처리, Real Memory 사용율, NW Session, Packet Error, Drop Count 추가갱신

 

Linux Kernel은 Disk I/O에 대한 응답성능 향상을 위해 Disk에 적재된 데이터에 대한 Cache 즉 Page 확보를 통해 장치간 성능차를 극복한다고 일전의 게시물을 통해 설명한적이 있다. (http://blog.helperchoi.com/86)

 

Page Cache로 적재된 데이터에 대해서 Update성 I/O 가 인입되면 즉 Dirty Page Cache가 발생되면, Linux kernel 은 다음과 같은 설정값에 의해 해당 데이터를 Disk 영역으로 Flushing 하게된다.

 

[root@s-node01 shell]#
[root@s-node01 shell]# cat /proc/sys/vm/dirty_background_ratio
10
[root@s-node01 shell]# 
[root@s-node01 shell]# cat /proc/sys/vm/dirty_ratio
40
[root@s-node01 shell]#

[root@s-node01 shell]# cat /proc/sys/vm/dirty_expire_centisecs
3000
[root@s-node01 shell]#

[root@s-node01 shell]# cat /proc/sys/vm/dirty_writeback_centisecs
500
[root@s-node01 shell]#

[root@s-node01 shell]# cat /proc/sys/vm/nr_pdflush_threads
2
[root@s-node01 shell]#

 

1. dirty_background_ratio -  기본적으로 Kernel은 시스템 메모리에 적재된 Page Cache의 총량중 Dirty Page의 양이 설정된 임계비율을(Default 10%) 초과 했을 경우 kthread 이하 pdflush thread를 통해 Disk에 동기화 수행을 하게 된다.

 

2. dirty_ratio - Linux Kernel은 dirty_ratio 임계비율을 기준으로 Page Cache의 총량 대비 Dirty Page의 총량이 임계비율을 초과하게되면 kernel은 Memory 영역에 적재된 Dirty Page cache의 동기화 처리 수준이(Disk I/O) 전체 요청량에 비해 떨어진다고 판단을 하고 Kernel 이하의 모든 프로세스에 대하여 쓰기작업에 대한 차단을 수행한다.

즉 시스템의 모든 작업에 영향을 주게되며, 이에 따라 시스템의 Loadaverage는 급증하며, Disk 및 CPU Latency Time이 폭증하는 양상을 띄게 된다.

때문에 일반적인 System Resouce (CPU / Memory) 의 사용량이 평이함에도 시스템에 지연현상이 나타날때는 dirty_ratio 임계비율과 함께 아래와 같이 전체 Page Cache량 대비 Dirty Page 비율을 체크하면 도움이 된다.

 

[root@s-node01 ~]#
[root@s-node01 ~]# cat /proc/meminfo | egrep -i 'cache|dirty'
Cached:         289104 kB
SwapCached:        100 kB
Dirty:              2140 kB
[root@s-node01 ~]#
[root@s-node01 ~]#

 

3. dirty_expire_centisecs - Memory 영역에 적재된 Dirty Page Cache는 dirty_background_ratio 임계비율과는 별개로 dirty_expire_centisecs 값에 의해 생성된지 3000 millisecond(30초)이 지나게 되면 Disk 영역으로 동기화 처리 된다.

 

4. dirty_writeback_centisecs - 상기 1번 항목에서 설명했던 pdflush Thread 가 활성화되는 조건값을 나타내며, 기본값은 5초이다. 즉 pdflush 는 5초에 한번씩 Dirty Page 에 대한 Disk Flushing을 수행하며, pdflush 의 처리시간이 1초이상 초과할때 다음 nr_pdflush_threads 값에 정의된 만큼 추가 Thread를 생성하여 Disk 동기화를 수행하게 된다.

 

5. nr_pdflush_threads - 상기 4번에서 설명한 대로 pdflush Thread의 최소개수를 정의한다. 기본값은 2개이며 최대 8개까지 증가된다.

다만 이는 kernel 2.6.31 버전까지만 해당하며, kernel 2.6.32 부터는 기존 pdflush 성능적 단점을 보완하기 위해 아래와 같이 /proc/sys/vm/nr_pdflush_threads 값은 무의미한 값(0)으로 처리되어 졌으며, OS 할당된 물리 Device의 수만큼 개별 처리 프로세스를 할당하여 필요시 처리하게 되었다.


추가적으로 nr_pdflush_threads 값은 아래와 같이 kernel 이하 sysctl.c  Code에 의해 읽기 전용으로 제한되어 있어 변경이 불가 하다는 사실을 확인하였음.



※ 발췌 - Redhat Support 


The sysctl vm.nr_pdflush_threads is a read-only value and cannot be changed. It is defined in sysctl.c in the kernel source as:


{

.ctl_name       = VM_NR_PDFLUSH_THREADS,

.procname       = "nr_pdflush_threads",

.data           = &nr_pdflush_threads,

.maxlen         = sizeof nr_pdflush_threads,

.mode           = 0444 /* read-only*/,

.proc_handler   = &proc_dointvec,

},


The min and max number of pdflush threads is also hard coded in the kernel as 2 (minimum number of threads) and 8 (maximum number of threads). Whenever all existing  pdflush threads are busy for at least one second, an additional pdflush thread is spawned.   The new ones try to write back data to device queues that are not congested, with the goal to have  each device that is active get its own thread flushing data to that device.  Each time a  second has passed without any pdflush activity, one of the threads is removed until min number of threads is reached. 




※ Kernel 2.6.31 이하  

[root@s-node01 ~]#
[root@s-node01 ~]# uname -a
Linux s-node01 2.6.18-371.11.1.el5 #1 SMP Wed Jul 23 15:12:55 EDT 2014 x86_64 x86_64 x86_64 GNU/Linux
[root@s-node01 ~]#
[root@s-node01 ~]# cat /proc/sys/vm/nr_pdflush_threads
2
[root@s-node01 ~]#
[root@s-node01 ~]# ps -ef | grep -v grep | grep flush
root       131    11  0 Feb27 ?        00:00:00 [pdflush]
root       132    11  0 Feb27 ?        00:00:15 [pdflush]
[root@s-node01 ~]#
[root@s-node01 ~]#

 

※ Kernel 2.6.32

[root@TEST02 ~]#
[root@TEST02 ~]# uname -a
Linux TEST02 2.6.32-358.el6.x86_64 #1 SMP Fri Feb 22 00:31:26 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
[root@TEST02 ~]#

[root@TEST02 ~]#
[root@TEST02 ~]# cat /proc/sys/vm/nr_pdflush_threads
0
[root@TEST02 ~]#
[root@TEST02 ~]#
[root@TEST02 ~]# ps -ef | grep -v grep | grep flush
root       836     2  0  2014 ?        07:01:04 [flush-8:0]
root     59936     2  0 Feb16 ?        01:48:07 [flush-8:16]
root     59937     2  0 Feb16 ?        01:46:43 [flush-8:48]
root     59938     2  0 Feb16 ?        01:41:28 [flush-8:32]
[root@TEST02 ~]#
[root@TEST02 ~]#
[root@TEST02 ~]# grep ^ /sys/class/block/*/dev | grep sd
/sys/class/block/sda/dev:8:0
/sys/class/block/sda1/dev:8:1
/sys/class/block/sda2/dev:8:2
/sys/class/block/sda3/dev:8:3
/sys/class/block/sdb/dev:8:16
/sys/class/block/sdb1/dev:8:17
/sys/class/block/sdc/dev:8:32
/sys/class/block/sdc1/dev:8:33
/sys/class/block/sdd/dev:8:48
/sys/class/block/sdd1/dev:8:49
[root@TEST02 ~]#
[root@TEST02 ~]#
[root@TEST02 ~]# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda3       1.8T  1.4T  353G  80% /
tmpfs            24G     0   24G   0% /dev/shm
/dev/sda1       985M   44M  891M   5% /boot
/dev/sdb1       1.8T  1.3T  509G  71% /data2
/dev/sdc1       1.8T  1.2T  514G  71% /data3
/dev/sdd1       1.8T  1.3T  485G  73% /data4
[root@TEST02 ~]#

 

 

6. Shell Script를 통한 Dirty Page에 의한 시스템 부하 감시 모니터링

1) 1회성 실행 예시

[root@TEST01 shell]#
[root@TEST01 shell]# ./dirty_info.sh

[ Total Memory ] : 509728KB
[ Free Memory ] : 20272KB
[ Page Cache ] : 384628KB
[ Dirty Page ] : 148KB
[ Flush Thread ] : 2
[ Dirty Ratio LIMIT ] : 40/100%
[ Dirty Ratio Status ] : .03/100%
[ Swap Memory Status ] : 728KB / 1048568KB
[ System Loadaverage ] : 1.06 (1 Core System)
[ Dirty Page Status ] : NORMAL

[root@TEST01 shell]#

 

2) 지속 모니터링 예시

[root@TEST01 shell]#
[root@TEST01 shell]# watch -n1 -d "./dirty_info.sh"

Every 1.0s: ./dirty_info.sh                                                                                                                                                                                                                           Thu Mar 12 15:32:33 2015


[ Total Memory ] : 509728KB
[ Free Memory ] : 5660KB
[ Page Cache ] : 385116KB
[ Dirty Page ] : 206244KB
[ Flush Thread ] : 8
[ Dirty Ratio LIMIT ] : 40/100%
[ Dirty Ratio Status ] : 53.55/100%
[ Swap Memory Status ] : 728KB / 1048568KB
[ System Loadaverage ] : 5.37 (1 Core System)
[ Dirty Page Status ] : CRITICAL

 

[ Operations Notice ] : Kernel will block all write operations !!!
[ Loadaverage Notice ] : Latency time of all process increased !!!

 

 

3) Script 내용

[root@TEST01 shell]# vi ./dirty_info.sh

 

#!/bin/bash
#Make by Kwang Min Choi / http://blog.helperchoi.com / helperchoi@gmail.com
#TEST OS : CentOS 5.x, 6.x, 7.x

export LANG=C
export LC_ALL=C

CHECK_OS=`uname -a | cut -d "/" -f 2 | tr '[A-Z]' '[a-z]'`

if [ $CHECK_OS = "linux" ];
then
 if [ -e "/etc/oracle-release" ]
 then
  export OS_VERSION=`cat /etc/oracle-release`
  export OS_TYPE="OEL"
 else
  export OS_VERSION=`cat /etc/redhat-release`
  export OS_TYPE="RHEL"
 fi
fi


KERNEL_MAJOR_CHECK=`uname -r | cut -d "-" -f 1 | cut -d "." -f 1`
KERNEL_MINOR_CHECK=`uname -r | cut -d "-" -f 1 | cut -d "." -f 3`

LOG_DIR=/root/collect
LOG_NAME=${LOG_DIR}/`date +%Y-%m-%d`_collect.log
declare -a ARRAY_TIME
declare -a ARRAY_MEMLIST
ARRAY_TIME=("${ARRAY_TIME[@]}" "`date`")
 
for MEM_LIST in `cat /proc/meminfo | awk '{print $2}'`
do
 ARRAY_MEMLIST=("${ARRAY_MEMLIST[@]}" "`echo ${MEM_LIST}`")
done

DIRTY_FLUSH_RATIO=`cat /proc/sys/vm/dirty_background_ratio`
DIRTY_CRITICAL_RATIO=`cat /proc/sys/vm/dirty_ratio`
DIRTY_CRITICAL_LIMIT=`echo "$DIRTY_CRITICAL_RATIO - 5" | bc`
CPU_CORE=`cat /proc/cpuinfo | grep -i process | wc -l`
LOAD_AVERAGE=`cat /proc/loadavg | awk '{print $1}'`
LOAD_AVERAGE_LIMIT=`echo "scale=3; ${CPU_CORE} * 2.5" | bc`
export LOAD_DIFF=`echo "${LOAD_AVERAGE} >= ${LOAD_AVERAGE_LIMIT}" | bc`
CPU_IDLE=`sar -p 1 1 | grep Aver | awk '{print $8}'`
CPU_USAGE=`echo "100 - ${CPU_IDLE}" | bc`
RF_USAGE=`df -hP | awk '$6 ~ /\/$/ {print $5}'`
MEM_TSIZE=${ARRAY_MEMLIST[0]}
MEM_FSIZE=${ARRAY_MEMLIST[1]}
CACHE_SIZE=${ARRAY_MEMLIST[3]}

if [ ${KERNEL_MAJOR_CHECK} -eq 2 -a ${KERNEL_MINOR_CHECK} -lt 32 ]
then
 PDFLUSH_THREAD=`ps -ef | grep -v grep | grep flush | wc -l`
 SWAP_SIZE=${ARRAY_MEMLIST[11]} 
 SWAP_FREE=${ARRAY_MEMLIST[12]}
 DIRTY_SIZE=${ARRAY_MEMLIST[13]} 
 CONTEXT_SWITCH=`sar -w 1 1 | awk '$1 ~ /^[0-9]/ {print $2}' 2>&1 | sed -n '2,2p'`
 
elif [ ${KERNEL_MAJOR_CHECK} -eq 2 -a ${KERNEL_MINOR_CHECK} -ge 32 ]
then
 PDFLUSH_THREAD=`ps -ef | grep -v grep | grep flush- | wc -l`
 SWAP_SIZE=${ARRAY_MEMLIST[13]} 
 SWAP_FREE=${ARRAY_MEMLIST[14]}
 DIRTY_SIZE=${ARRAY_MEMLIST[15]} 
 CONTEXT_SWITCH=`sar -w 1 1 | awk '$1 ~ /^[0-9]/ {print $3}' 2>&1 | sed -n '2,2p'`

elif [ ${KERNEL_MAJOR_CHECK} -eq 3 ]
then
 if [ ${OS_TYPE} = "OEL" ]
 then 
  SWAP_SIZE=${ARRAY_MEMLIST[13]} 
  SWAP_FREE=${ARRAY_MEMLIST[14]}
  DIRTY_SIZE=${ARRAY_MEMLIST[15]} 
 else
  CACHE_SIZE=${ARRAY_MEMLIST[4]}
  SWAP_SIZE=${ARRAY_MEMLIST[15]} 
  SWAP_FREE=${ARRAY_MEMLIST[16]}
  DIRTY_SIZE=${ARRAY_MEMLIST[17]} 
 fi

 PDFLUSH_THREAD=`ps -ef | grep -v grep | grep kworker | wc -l`
 CONTEXT_SWITCH=`sar -w 1 1 | awk '$1 ~ /^[0-9]/ {print $3}' 2>&1 | sed -n '2,2p'`
fi

MEM_USIZE=`echo "((${MEM_TSIZE} - ${MEM_FSIZE} - ${CACHE_SIZE}) * 100) / ${MEM_TSIZE}" | bc`
SWAP_USE=`echo "${SWAP_SIZE} - ${SWAP_FREE}" | bc`
EXPR1=`echo "${DIRTY_SIZE} * 100" | bc`
EXPR2=`echo "scale=2; ${EXPR1} / ${CACHE_SIZE}" | bc`
export EXPR_DIFF1=`echo "scale=3; ${EXPR2} >= ${DIRTY_CRITICAL_RATIO}" | bc`
export EXPR_DIFF2=`echo "scale=3; ${EXPR2} >= ${DIRTY_CRITICAL_LIMIT}" | bc`
OPEN_FILE=`sudo /usr/sbin/lsof | wc -l`
RUN_PROC=`ps -ef | wc -l`
RUN_THREAD=`ps -eL | wc -l`
 
echo
echo "$(date) / [ OS Version ] : ${OS_VERSION} / `uname -r`"
echo "$(date) / [ CPU Usage ] : ${CPU_USAGE} %"
echo "$(date) / [ Real Memory Usage ] : ${MEM_USIZE} %"
echo "$(date) / [ Root Filesystem Usage ] : ${RF_USAGE}"
echo "$(date) / [ Total Memory ] : ${MEM_TSIZE} KB"
echo "$(date) / [ Free Memory ] : ${MEM_FSIZE} KB"
echo "$(date) / [ Page Cache ] : ${CACHE_SIZE} KB"
echo "$(date) / [ Dirty Page ] : ${DIRTY_SIZE} KB"
echo "$(date) / [ Disk Flush Thread ] : ${PDFLUSH_THREAD}"
echo "$(date) / [ Dirty Ratio LIMIT ] : ${DIRTY_CRITICAL_RATIO}%"
echo "$(date) / [ Dirty Ratio Status ] : ${EXPR2} / 100%"
echo "$(date) / [ Swap Memory Status ] : ${SWAP_USE} KB / ${SWAP_SIZE} KB"
echo "$(date) / [ Open File Count ] : ${OPEN_FILE}"
echo "$(date) / [ Process Total Count ] : ${RUN_PROC}"
echo "$(date) / [ Thread Total Count ] : ${RUN_THREAD}"
echo "$(date) / [ CPU Context Switch ] : ${CONTEXT_SWITCH}"
echo "$(date) / [ System Loadaverage ] : ${LOAD_AVERAGE} (${CPU_CORE} Core System)"
echo "$(date) / [ Network Reassembles Failed ] : `netstat -s | grep -i 'reassembles failed'`"
 
if [ ${EXPR_DIFF1} -eq 1 ]
then
 echo "$(date) / [ Dirty Page Status ] : CRITICAL"
 echo
 echo "$(date) / [ Operations Notice ] : Kernel will block all write operations !!!"
elif [ ${EXPR_DIFF2} -eq 1 ]
then
 echo "$(date) / [ Dirty Page Status ] : WARNING"
 echo
else
 echo "$(date) / [ Dirty Page Status ] : NORMAL"
 echo
fi
 
if [ ${LOAD_DIFF} -eq 1 ]
then
 echo "[ Loadaverage Notice ] : Latency time of all process increased !!!"
 echo
fi

for STAT in `netstat -na | grep tcp | awk '{print $6}' | sort -u`
do
 echo "$(date) / ${STAT} - `netstat -na | awk '$6 ~ /^'"$STAT"'$/ {print $0}' | wc -l`"
done
echo

for LIST in `ip addr | grep -w mtu | cut -d ":" -f 2 | egrep -v 'lo|inet|virbr|sit'`; do echo "${LIST} - `ethtool ${LIST} | egrep 'Link detected'`" && echo; done | grep yes | awk '{print $1}' | xargs -i{} netstat -n -I={}
echo

 

참고문서 - https://www.thomas-krenn.com/en/wiki/Linux_Page_Cache_Basics#As_of_Version_2.6.32:_per-backing-device_based_writeback

 

 

 

 

반응형
댓글
댓글쓰기 폼