시스템을 어떻게 더 공부해야 능력있는 엔지니어가 될 수 있을지 갈피를 알 수 없는 와중에 명령어가 어떻게 동작하는지, 시스템 콜은 어떻게 볼 수 있는지 알 수 있는 시간이었습니다. 좋은 책을 써주신 저자님께 감사드립니다:)
http://www.yes24.com/Product/Goods/44376723
DevOps와 SE를 위한 리눅스 커널 이야기 - YES24
커널은 오랜 세월 기능이 추가되고 개선되어 오면서 완벽하게 이해하기 힘들 정도로 방대해졌다. 하지만 변하지 않는 기본 기능들이 있다. 이런 근간이 되는 기능에 대한 이해를 바탕으로 시스
www.yes24.com
# 목차
- 시스템 메모리를 확인할 수 있는 여러 명령어
- Active/Inactive LRU 리스트와 페이지 캐시
- Swap 메모리
- Slab 메모리와 버디 시스템(Buddy System)
- 메모리 재할당: 캐시 메모리 해제 vs Swap
- 시스템 메모리를 확인할 수 있는 여러 명렁어
1. free 명령어
root@common-server:~$ free -m
total used free shared buff/cache available
Mem: 385579 10081 308093 2 67404 372950
Swap: 8191 0 8191
- Buff : 성능 향상을 위해 커널에서 사용하는 영역
- Cache : I/O 작업 향상을 위해 page cache와 slab으로 사용하는 영역
* Page와 Buffer
어플리케이션이 요청하는 읽기/쓰기 작업을 위해 커널은 디스크(블록 디바이스)에게 데이터가 위치한 블록 주소를 넘겨주고, 디스크는 받은 주소에 해당하는 섹터에서 데이터를 찾아 다시 넘겨줍니다. 그런데 디스크의 읽기/쓰기 작업은 컴퓨팅 시스템에서 상대적으로 매우 느린편에 속합니다. 그래서 리눅스에서는 한 번 읽은 데이터들을 메모리의 일부인 캐싱 영역(buff/cache)에 적재하여 디스크까지 도달하지 않고도 빠르게 읽기/쓰기 작업을 수행할 수 있습니다.
만약 프로세스의 요청에 의해 읽어야 할 데이터가 파일 자체의 데이터인 경우, 커널은 블록 I/O의 기본 처리 단위인 bio구조체를 만들고 거기에서 page cache 용도로 할당한 메모리 영역을 연결합니다. 그리고 bio 구조체는 드라이버와 통신하여 디스크로부터 데이터를 읽어들여 page cache의 내용을 채웁니다.
반대로 super block이나 inode block과 같이 파일의 이름, 사이즈, 타입 등을 담고 있는 메타데이터를 읽을 때는 _get_blk()와 같은 내부 함수로 저장 장치와 직접 통신합니다. 그리고 이때 가져온 특정 내용을 buffer cache에 저장합니다.
(결론은 그냥 page에는 파일의 데이터가, buffer에는 메타데이터가 저장된다는 것입니다.)
2. ps 명령어
root@common-server:~$ ps -eo pid,ppid,cmd,comm,%mem,%cpu,rss,sz,size,vsize,time --sort=-%mem | head
PID PPID CMD COMMAND %MEM %CPU RSS SZ SIZE VSZ TIME
7057 1 /usr/bin/qemu-kvm -name gue qemu-kvm 1.3 14.8 5149408 4451809 17211064 17807236 4-15:21:34
6539 1 /usr/bin/qemu-kvm -name gue qemu-kvm 1.2 15.9 5027784 4451038 17206664 17804152 4-23:52:19
1170 1 /lib/systemd/systemd-journa systemd-journal 0.0 0.1 283380 118184 34284 472736 01:01:24
6441 1 /usr/local/snet/agent/jre/b java 0.0 0.0 155768 1667779 530512 6671116 00:23:10
6052 1 /usr/bin/dockerd -H fd:// - dockerd 0.0 0.0 80228 1389676 647140 5558704 00:18:21
5569 1 /usr/bin/containerd containerd 0.0 0.0 42976 910522 436436 3642088 00:12:28
5212 1 /usr/sbin/libvirtd libvirtd 0.0 0.0 39124 450693 292736 1802772 00:01:22
68065 68062 (squid-1) -YC -f /etc/squid squid 0.0 0.0 27220 38497 44208 153988 00:01:48
5518 1 /usr/bin/python3 /usr/share unattended-upgr 0.0 0.0 19916 46490 16760 185960 00:00:00
- %mem (pmem) : 물리 메모리 대비 RSS 비율
- RSS (Resident Set Size) : swap을 제외하고 프로세스가 사용하는 물리 메모리 (kB)
- SZ : 프로세스의 사용 중인 물리 이미지 크기 (kB)
- SIZE : swap을 포함하여 프로세스가 사용하는 가상메모리 (kB)
- VSZ (vsize) : SIZE를 KiB로 표현
3. cat /proc/meminfo
root@compute-1-3:~# cat /proc/meminfo
# kibibytes
MemTotal: 395733704 kB
MemFree: 17084980 kB
MemAvailable: 17963920 kB
Buffers: 428400 kB
Cached: 983964 kB
SwapCached: 0 kB
Active: 2127316 kB
Inactive: 820452 kB
Active(anon): 1569788 kB
Inactive(anon): 160356 kB
Active(file): 557528 kB
Inactive(file): 660096 kB
...
Dirty: 428 kB
...
Slab: 1978572 kB
SReclaimable: 1319932 kB
SUnreclaim: 658640 kB
...
HugePages_Total: 348
HugePages_Free: 220
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 1048576 kB
...
- MemTotal : 물리 메모리 총합에서 예약 메모리(커널 바이너리, BIOS 등)를 제외한 값.
- MemAvailable : swap 없이 새로운 App을 실행시킬 수 있는 메모리의 총합.
- SwapCached : swap으로 빠졌다 다시 돌아온 메모리 영역.
- Active(anon) : anon은 anonymous의 약어로, Page Cache를 제외한 heap과 stack 등의 메모리 영역입니다. Active이므로 비교적 최근에 참조되어 swap으로 이동되지 않을 메모리 영역입니다. ('익명'이라는 뜻의 anonymous는 파일로부터 매핑되지 않고 커널로부터 할당된 페이지라는 뜻입니다.)
- Active(file) : 커널이 I/O 성능 향상을 위해 사용하는 영역으로 Buffers와 Cached가 여기에 속합니다.
- Inactive(anon/file) : 참조된 지 오래되어 swap 영역으로 이동될 수 있는 메모리 영역.
- Dirty : I/O 성능 향상을 위해 커널이 캐시 목적으로 사용하는 영역 중 쓰기 작업이 이루어져 실제 블록 디바이스의 블록에 씌어져야 할 영역
- Slab : 커널이 직접 사용하는 영역. (dentry cache, inode cache 등)
- SReclaimable : 재사용 가능한 slab 영역으로 캐시 용도로 사용하는 메모리들이 주로 포함. 메모리 부족 시 해제되어 어플리케이션에 할당 가능합니다.
- SUnreclaim : 재사용 불가능한 slab 영역.
- HugePages_Total : 프로세스나 VM 등이 메모리를 큰 단위의 Block으로 사용하여 캐시 적중률 높이기 위해서 사용하는 것으로, HugePage의 개수입니다.
- Hugepagesize : 한 블록의 HugePage가 가지는 크기입니다. (Page의 기본 사이즈 = 4kB)
* free 명령어의 buffer는 /proc/meminfo의 Buffers의 값을, cache는 /proc/meminfo의 Cache와 Slab 값을 읽어와 계산됩니다.
4. pmap - 메모리의 영역별로 사용량을 확인할 수 있습니다.
- Active/Inactive LRU 리스트와 페이지 캐시
메모리는 디스크에 비해 속도가 빠르지만 저장 가능한 용량을 부족합니다. 그래서 디스크에서 읽을 수 있는 데이터 양에 보다 훨씬 적은 데이터만을 캐싱 영역에 적재할 수 있습니다. 메모리에 적재된 데이터는 Active와 Inactive 2개의 LRU(Least Recently Used) 리스트에서 관리됩니다.
// fs/proc/meminfo.c
static void show_val_kb(struct seq_file *m, const char *s, unsigned long num) {
seq_put_decimal_ull_width(m, s, num << (PAGE_SHIFT - 10), 8);
seq_write(m, " kB\n", 4);
}
...
show_val_kb(m, "Active(anon): ", pages[LRU_ACTIVE_ANON]);
show_val_kb(m, "Inactive(anon): ", pages[LRU_INACTIVE_ANON]);
show_val_kb(m, "Active(file): ", pages[LRU_ACTIVE_FILE]);
show_val_kb(m, "Inactive(file): ", pages[LRU_INACTIVE_FILE]);
// 리눅스 커널에서는 위 4개의 리스트로 페이지를 관리합니다.
...
프로세스가 메모리를 할당 받으면 add_to_page_cache_lru()와 filemap_add_folio() 함수에 의해 호출된 folio_add_lru() 함수에 의해 디스크의 데이터를 페이지의 LRU 리스트와 매핑합니다.
// mm/folio-compat.c
int add_to_page_cache_lru(struct page *page, struct address_space *mapping,
pgoff_t index, gfp_t gfp) {
return filemap_add_folio(mapping, page_folio(page), index, gfp);
}
// mm/filemap.c
int filemap_add_folio(struct address_space *mapping, struct folio *folio,
pgoff_t index, gfp_t gfp) {
void *shadow = NULL;
int ret;
...
folio_add_lru(folio);
}
return ret;
}
* folio 구조체는 page에 대한 wrapper 타입으로 커널 5.16 이후 머지되었습니다. (공부가 더 필요하겠지만, 지금은 그냥 page에 해당하는 자료구조라고 이해하고 넘어가야겠네요 ㅜㅜ)
// include/linux/mm_types.h
/**
* struct folio - Represents a contiguous set of bytes.
* @flags: Identical to the page flags.
* @lru: Least Recently Used list; tracks how recently this folio was used.
* @mlock_count: Number of times this folio has been pinned by mlock().
* @mapping: The file this page belongs to, or refers to the anon_vma for
* anonymous memory.
* ...
*/
struct folio {
union {
struct {
unsigned long flags;
union {
struct list_head lru;
struct {
void *__filler;
unsigned int mlock_count;
};
};
struct address_space *mapping;
pgoff_t index;
...
* address_space 구조체는 디스크의 데이터 섹터와 페이지를 매핑하기 위한 구조체입니다.
// include/linux/fs.h
/**
* struct address_space - Contents of a cacheable, mappable object.
* @host: Owner, either the inode or the block_device.
* @i_pages: Cached pages.
...
*/
struct address_space {
struct inode *host;
struct xarray i_pages;
struct rw_semaphore invalidate_lock;
gfp_t gfp_mask;
atomic_t i_mmap_writable;
...
* atomic_*() 함수들은 하나 이상의 스레드에 의해 참조되는 변수를 수정할 때, 인터럽트 핸들러 등에 의해 변수가 동시에 처리되지 않도록 보장해 줍니다. (mutex - Mutual Exclution)
새로운 페이지가 LRU 리스트에 추가되는 과정을 이렇습니다. 먼저, 참조된 페이지를 Inactive LRU 리스트의 head에 추가되면서 기존의 리스트들은 하나씩 밀리게 되고 tail의 페이지는 캐시에서 해제됩니다. 그리고 해당 페이지가 두 번째로 참조되면 Active LRU 리스트로 옮겨가고 Inactive LRU 리스트는 하나씩 줄어들게 됩니다.
그렇다면 Active → Inactve로의 이동은 언제 일어나는 걸까요? 단순히 Active LRU의 데이터가 오랜 시간 참조되지 않았다고해서 Inactive LRU로 이동하는 것은 아닙니다. 시스템에서 최소 free 메모리를 유지할 수 없게 되어 해제해야 할 메모리를 찾아야하는 상황이 되었을 때, 커널은 kswapd 또는 try_to_free_page() 함수를 통해 LRU 리스트를 스캔하고 Active 리스트에서 오래된 페이지를 Inactive 페이지로 옮긴 후 메모리를 해제합니다.
(만약 Inactive 리스트를 해제해도 충분한 메모리를 확보하지 못하면 OOM(Out of memory)으로 프로세스 킬이 발생하게 됩니다.)
* 시스템에서 유지해야 하는 최소 free 메모리 양
root@common-server:~$ sysctl -a | grep vm.min
vm.min_free_kbytes = 90112
- swap 메모리
시스템에서 메모리가 부족하게 되면 프로세스는 더 이상 연산을 위한 공간을 확보할 수 없게 되어 전체 시스템이 응답 불가 상태에 빠질 수도 있습니다. 이러한 현상을 방지할 수 있는 swap 영역은 물리 메모리가 부족한 경우 디스크의 일부를 메모리처럼 사용하기 위한 공간입니다. 그러나 디스크의 속도는 메모리에 비해 현저히 떨어지므로 swap 영역을 사용하는 것은 시스템 성능 저하로 이어질 수 있습니다.
# 프로세스가 사용하는 전체 swap 영역 확인
root@common-server:~# cat /proc/1234/status
Umask: 0022
State: S (sleeping)
...
VmPeak: 49984 kB
VmSize: 49984 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 3176 kB
VmRSS: 3176 kB
...
VmSwap: 0 kB
# 프로세스의 메모리 영역별 swap 확인
root@common-server:~# cat /proc/3005/smaps
561cee20e000-561cee269000 r-xp 00000000 fd:00 6030095
# 프로세스의 논리 메모리 주소
/usr/sbin/dnsmasq
Size: 364 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 364 kB
Pss: 128 kB
Shared_Clean: 364 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 0 kB
Referenced: 364 kB
Anonymous: 0 kB
LazyFree: 0 kB
AnonHugePages: 0 kB
ShmemPmdMapped: 0 kB
FilePmdMapped: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
Locked: 0 kB
THPeligible: 0
ProtectionKey: 0
VmFlags: rd ex mr mw me dw sd
→ 프로세스는 /proc/<pid> 경로에 자신과 관련된 정보를 저장합니다.
→ smem 명령어로 전체 프로세스 별로 사용 중인 swap을 확인할 수 있습니다.
- slab 메모리
slab 메모리는 시스템 성능 향상을 위해 커널이나 Device driver에서 캐시나 네트워크 소켓을 위해 사용하는 영역입니다. 일반적으로 메모리를 할당하는 버디 시스템은 4KB의 페이지 단위로 메모리를 할당합니다. 그러나 커널은 이렇게 큰 영역을 할당 받을 필요가 없습니다. 그래서 커널은 slab을 기준으로 페이지를 캐시 크기에 맞게 나누어 사용합니다.
slab은 free 명령어의 출력에서 used로 계산됩니다. 만약 프로세스가 사용하는 메모리의 총합이 free의 used와 일치하지 않을 경우 slab 메모리 누수를 의심해볼만 합니다.
# 시스템에서 사용 중인 slab 메모리 확인
root@common-server:~# slabtop -o
dentry는 Directory Entry(디렉토리 간의 부모/자식 관계 구조 등)을 저장합니다. 단순히 cd 명령어로 디렉토리를 이동하거나 ls 명령어로 파일을 조회만 해도 dentry 값은 증가합니다. 특히 파일에 접근하거나 디렉토리를 생성하고 삭제하는 빈도가 높으면 slab이 높아질 수 있습니다.
- buddy 시스템
커널은 메모리 단편화(Fragmentation)를 방지하고 메모리를 효율적으로 관리하기 위해서 버디 시스템을 사용하여 물리 메모리를 다양한 크기의 블록으로 나누어 사용합니다. 예를 들어 프로세스에서 1,024KB의 메모리를 할당 요청하는 경우에 512KB 2개가 아닌 1,024KB의 블록 1개를 할당하는 방식으로 메모리 수요에 알맞은 사이즈를 동적으로 할당할 수 있습니다.
# 가용 메모리 영역에 대한 buddy 정보
root@common-server:~# cat /proc/buddyinfo
- DMA/DMA32 : 오래된 하드웨어 동작을 위해 존재합니다. 요즘에는 이런 영역을 필요로 하는 하드웨어는 잘 없는 것 같습니다.
- Normal : 커널, 프로세스 등이 메모리르 필요로 할 때 사용합니다.
- 메모리 재할당
시스템에서 메모리가 충분한 경우에 커널은 메모리를 page/buffer와 inode, dentry 등의 여러 캐시 용도로 사용하여 시스템 자원을 최대한 이용하고 시스템 성능을 향상시키도록 개발되었습니다. 그러나 프로세스들의 메모리 요구량이 높아지는 등으로 메모리가 부족하게 되면 다음 두 가지 동작을 수행합니다.
1. 캐시 용도의 메모리를 해제하고 프로세스에 재할당
2. swap 영역의 메모릴 프로세스에 할당
* 메모리 재할당 테스트 Case 1: 더미 파일 생성과 메모리 변화 확인
1. 테스트 전의 시스템에는 약 1.6G의 free 메모리가 존재합니다.
root@fuel:~$ free -m
total used free shared buff/cache available
Mem: 12860 3642 1636 1060 7581 7838
Swap: 4095 0 4095
2. vmstat 명령어로 초당 시스템 통계를 확인하면서, 500MB의 더미 파일들을 여러번 생성하고 결과를 확인합니다.
root@fuel:~$ dd if=/dev/zero of=./file_1 bs=1024 count=500000
500000+0 records in
500000+0 records out
512000000 bytes (512 MB) copied, 1.55961 s, 328 MB/s
...
root@fuel:~$ dd if=/dev/zero of=./file_4 bs=1024 count=500000
500000+0 records in
500000+0 records out
512000000 bytes (512 MB) copied, 1.56298 s, 328 MB/s
3. 테스트 결과
다량의 쓰기 작업이 발생하면서 free 영역이 줄어들고 cache 영역이 늘어나는 것을 확인할 수 있습니다.
root@fuel:~$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
...
0 0 0 1156124 512968 7762272 0 0 0 0 425 407 0 0 100 0 0
0 1 0 1154020 512972 7762268 0 0 0 368968 1913 3671 1 2 85 12 1
2 0 0 933064 512972 7983404 0 0 0 40 3157 1712 3 7 87 2 0
0 0 0 642420 512972 8275800 0 0 0 16 3318 1088 3 10 87 0 0
...
1 1 0 390756 512980 8527912 0 0 0 341296 2390 960 1 9 84 3 3
1 0 1024 236724 506932 8687384 0 996 1248 156756 3112 1259 1 13 76 9 1
0 0 1024 236032 506932 8687384 0 0 0 8 619 532 1 0 99 0 0
...
1 1 0 390756 512980 8527912 0 0 0 341296 2390 960 1 9 84 3 3
1 0 1024 236724 506932 8687384 0 996 1248 156756 3112 1259 1 13 76 9 1
0 0 1024 236032 506932 8687384 0 0 0 8 619 532 1 0 99 0 0
root@fuel:~$ free –m
total used free shared buff/cache available
Mem: 12860 3651 163 1052 9045 7837
Swap: 4095 8 4087
→ 테스트 전의 약 7.5G 였던 buff/cache는 쓰기 작업 이후 9G로, free는 1.6G에서 163MB로 바뀐 것을 볼 수 있습니다. 이처럼 단순히 더미 파일 쓰기 작업에 의한 페이지 캐시의 메모리 점유로 free 메모리가 줄어드는 것을 확인할 수 있습니다.
* 메모리 재할당 테스트 Case 2: free가 부족한 상황에서 프로세스가 메모리를 요청하는 경우
1. 앞의 파일 쓰기 작업으로 free 메모리라 페이지 캐시로 사용 중인 상태에서 프로세스가 메모리를 요청하도록 하여 결과를 확인합니다.
MEGABYTE = 1024*1024
DUMMY_STR = ' ' * MEGABYTE
def alloc_max_array():
i = 0
array = []
while True:
try:
array.append(DUMMY_STR + str(i))
except MemoryError:
break
i += 1
def alloc_max_str():
i = 0
while True:
try:
a = ' ' * (i * 10 * MEGABYTE)
del a
except MemoryError:
break
i += 1
alloc_max_str()
alloc_max_array()
2. 테스트 결과
시간이 while문에 의해 메모리를 계속 할당할수록 페이지 캐시로 사용하던 영역을 release하고 free 영역을 확보하는 결과를 확인할 수 있습니다.
root@fuel:~$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 18432 459820 499676 8446724 0 0 0 5 0 0 0 0 99 0 0
1 0 18432 460728 499676 8446728 0 0 0 0 1729 1708 11 2 88 0 0
1 0 18432 460524 499676 8446748 0 0 0 48 2605 2134 11 2 87 0 0
1 0 18432 459796 499676 8446780 0 0 0 40 2038 2008 11 2 87 0 0
1 0 18432 458176 499676 8446808 0 0 0 80 1897 1899 11 1 88 0 0
2 0 18432 455268 499676 8446816 0 0 0 72 2631 1824 12 2 86 0 0
...
1 0 29960 1608688 199780 7458600 0 0 0 0 3387 1266 2 11 86 0 0
1 0 29960 1402288 198960 7448840 0 0 0 0 2342 969 2 11 87 0 0
1 0 29960 1202080 197144 7440704 0 0 0 164 2422 849 3 11 86 0 0
...
1 0 29960 1859268 111100 5872124 0 0 0 0 1886 624 1 11 87 0 0
1 0 29960 1632008 110740 5848652 0 0 0 8 1850 486 3 11 86 0 0
1 0 29960 1628520 110748 5842752 0 0 0 24 1847 426 2 11 87 0 0
root@fuel:~$ free –m
total used free shared buff/cache available
Mem: 12860 3772 3280 1056 5807 7713
Swap: 4095 29 4066
→ free가 부족한 상태(160MB)에서 프로세스가 메모리를 요청하면, 이미 할당되어 있던 buffer와 cache 메모리를 해제하고 이를 프로세스에 할당하는 동작을 수행하게 됩니다. 하지만 이렇게 free 영역이 부족하다 해서 swap 영역을 사용하지는 않습니다. 그렇다면 swap 영역은 언제 사용하는 것 일까요?
- 캐시 메모리 해제 vs Swap
커널 파라미터 중 vm.swappniess와 vm.vfs_cache_pressure라는 변수가 있습니다.
먼저 vm.swappniess는 메모리가 부족하여 재할당이 필요한 경우에 캐시 메모리를 해제하고 거기에 할당할 지, swap 영역을 사용할지에 대한 비율을 결정하는 값입니다. 커널은 이 값이 크다면 swap 영역을 선호하고, 반대로 작다면 캐시 메모리를 비우는 것을 선호합니다. (deafult 값은 60입니다.)
다음으로 vm.vfs_cache_pressure는 캐시 메모리를 해제하고 재할당하여 사용하는 경우 페이지 캐시와 slab의 일부인 dentry 또는 inode 캐시 중 어떤 것을 더 많이 재할당할 것인지 결정하는 값입니다. (default 값은 100입니다.)
vm.vfs_cache_preesure가 어떻게 계산되는지 커널 소스를 보면 아래의 공식으로 표현할 수 있습니다.
// /include/linux/dcache.h
static inline unsigned long vfs_pressure_ratio(unsigned long val) {
return mult_frac(val, sysctl_vfs_cache_pressure, 100);
}
// /include/linux/math.h
// quot=몫, rem=나머지, numer=분자, denom=분모
#define mult_frac(x, numer, denom)( \
{ \
typeof(x) quot = (x) / (denom); \
typeof(x) rem = (x) % (denom); \
(quot * (numer)) + ((rem * (numer)) / (denom)); \
} \
)
실제로 vfs_pressure_ratio() 함수가 호출되는 super_cache_count() 함수를 보면, 현재 시스템의 LRU 리스트에서 dentry와 inode에 해당하는 페이지의 개수를 계산하는 것을 알 수 있습니다. 그리고 여기에 vfs_cache_pressure 값을 반영합니다.
만약, vfs_cache_pressure 값이 default인 100보다 크다면 스캔된 objects의 개수보다 많은 페이지를 해제하게 됩니다.
// fs/super.c
static unsigned long super_cache_count(struct shrinker *shrink, struct shrink_control *sc) {
struct super_block *sb;
long total_objects = 0;
sb = container_of(shrink, struct super_block, s_shrink);
if (!(sb->s_flags & SB_BORN))
return 0;
smp_rmb();
if (sb->s_op && sb->s_op->nr_cached_objects)
total_objects = sb->s_op->nr_cached_objects(sb, sc);
total_objects += list_lru_shrink_count(&sb->s_dentry_lru, sc); // dentry의 개수
total_objects += list_lru_shrink_count(&sb->s_inode_lru, sc); // indeo의 개수
if (!total_objects)
return SHRINK_EMPTY;
total_objects = vfs_pressure_ratio(total_objects); // Scan한 superblock의 개수에 vfs_cache_pressure 값 적용
return total_objects;
}
* list_lru_shrink_count() : 현재 LRU 리스트의 object 개수를 반환하는 함수 list_lru_count_one() 함수를 호출하는 함수
* vm.vfs_cache_pressure의 값이 100 이상이라면, 현재 사용 중인 캐시를 반환하여 성능 저하가 발생할 수 있습니다.
Reference
https://yongshikmoon.github.io/2021/04/18/anon_pages.html
About anonymous page
Reference: https://www.kernel.org/doc/html/latest/admin-guide/mm/concepts.html Page의 종류 페이지는 크게 두 분류로 나뉜다. 익명 페이지(Anonymous page) 파일-기반 페이지(File-backed page)1 파일-기반 페이지는 파일으로
yongshikmoon.github.io
https://brunch.co.kr/@alden/25
리눅스의 페이지 캐시와 버퍼 캐시
ftrace로 커널 읽기 - #2 | 아마 리눅스를 운영하시는 분들은 free 명령을 입력한 후 한 번쯤은 이런 궁금증을 가지셨을 겁니다. buffers와 cached는.. 뭐지? 라는 궁금증이죠. 저도 처음 업무를 하면서부
brunch.co.kr
Virtual Memory: Folio in 5.16
Introduction 저번 개발 주기때 간간이 보이길래 공부했었는데, 5.16에서 folio 패치셋이 드디어 머지되었다. 간단하게 요약해보자면 커널에서 메모리는 페이지 단위로 관리된다. 종종 여러 페이지를
hyeyoo.com
https://www.kernel.org/doc/gorman/html/understand/understand027.html
Page Frame Reclamation
Appendix쟆젨Page Frame Reclamation J.1젨Page Cache Operations This section addresses how pages are added and removed from the page cache and LRU lists, both of which are heavily intertwined. J.1.1젨Adding Pages to the Page Cache J.1.1.1젨Function: add
www.kernel.org
https://nxmnpg.lemoda.net/9/atomic_add
atomic_add(9) manual page
The atomic_add(), atomic_clear(), atomic_set(), and atomic_subtract() operations were introduced in FreeBSD 3.0 . Initially, these operations were defined on the types "char", "short", "int", and "long". The atomic_cmpset(), atomic_load_acq(), atomic_reada
nxmnpg.lemoda.net
https://sbexr.rabexc.org/latest/sources/df/8cd9239465a682.html
Linux v6.4 - include/linux/math.h
sbexr.rabexc.org
'System Engineering > Linux' 카테고리의 다른 글
[Linux] NTPv4(RFC5905)와 chrony 그리고 timex (0) | 2023.07.21 |
---|---|
[커널이야기] 리눅스 더티 페이지와 I/O Throttling (0) | 2023.07.06 |
[커널이야기] Load Average로 시스템 콜 추적하기 (0) | 2023.06.09 |
폐쇄망 환경에서 휴대폰 테더링으로 yum/apt 사용하기 (0) | 2023.04.20 |
[커널이야기] Linux top 명령어와 프로세스 (0) | 2023.04.13 |