| 커널 메모리 관리 

[정리]

1. 주제

- ㅇ

2. 전체 내용 요약

1) 커널에서 메모리 할당 하는 방법

2) buddy system 확인

3) slab allocator

4) vmalloc 할당 및 회수 과정

 

[1) 커널에서 메모리 할당 하는 방법]

총 4가지(?) 더 있을 것이며, 조사 필요

alloc_pages : 페이지 자체 할당

kmem_cache_alloc : 자주쓰는 자료구조 할당 ex) task_struct

kmalloc : 요청된 사이즈만큼 물리연속된 메모리 영역 할당. 메모리에 선 할당되어 있는 영역에서 할당을 해주기 떄문에 연속적으로 할당 할 수 있음

vmalloc : 요청된 사이즈만큼 물리적으로 연속된 메모리 영역 할당 보장 할 수 없음. 후매핑용 영역에서 할당하기 때문에 물리적으로 연속됨을 보장 할 수 없는 것임

 

다른 함수들에서 page가 필요한 경우 __get_free_pages를 호출하게 됨. 이때 몇개의 page가 필요한지 order를 통해 전달하게 되며, alloc_pages() 함수를 통해 실제 page관리 주체인 struct page를 할당 받게 됨. 

이후 struct page는 page_address()내에서 virtual address를 return하며 상위 함수에서 요청한 page를 전달하게 됨

struct page는 kernel에서 page를 관리하는 주체를 말하는 것이며, page내 vitrual 변수가 실제 메모리에 할당된 page를 가르키게 됨

즉, _get_free_pages()는 데이터를 로딩할 가상 메모리 주소를 받게 된 것이며, alloc_pages()는 실제 가상 메모리를 관리할 struct page를 할당 받게 된 것임

# cd /sys/kernel/debug/tracing
# echo 1 > events/kmem/mm_page_alloc/enable
# echo 1 > options/stacktrace
# cat trace_pipe

alloc_pages 수행시 orders를 통해 몇 page를 할당 했는지 확인 가능함. order =0 1page 할당

order에 대한 내용은 뒤에서 buddy system에서 좀 더 자세하게 나옴

 

[2) buddy system 확인]

<buddy info 보기 쉬운 python 스크립트>
#!/usr/bin/env python
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 textwidth=79 autoindent

"""
Python source code
Last modified: 15 Feb 2014 - 13:38
Last author: lmwangi at gmail  com
Displays the available memory fragments
by querying /proc/buddyinfo
Example:
# python buddyinfo.py
"""
import optparse
import os
import re
from collections import defaultdict
import logging


class Logger:
    def __init__(self, log_level):
        self.log_level = log_level

    def get_formatter(self):
        return logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

    def get_handler(self):
        return logging.StreamHandler()

    def get_logger(self):
        """Returns a Logger instance for the specified module_name"""
        logger = logging.getLogger('main')
        logger.setLevel(self.log_level)
        log_handler = self.get_handler()
        log_handler.setFormatter(self.get_formatter())
        logger.addHandler(log_handler)
        return logger


class BuddyInfo(object):
    """BuddyInfo DAO"""
    def __init__(self, logger):
        super(BuddyInfo, self).__init__()
        self.log = logger
        self.buddyinfo = self.load_buddyinfo()

    def parse_line(self, line):
        line = line.strip()
        #self.log.debug("Parsing line: %s" % line)
        parsed_line = re.match("Node\s+(?P<numa_node>\d+).*zone\s+(?P<zone>\w+)\s+(?P<nr_free>.*)", line).groupdict()
        #self.log.debug("Parsed line: %s" % parsed_line)
        return parsed_line

    def read_buddyinfo(self):
        buddyhash = defaultdict(list)
        buddyinfo = open("/proc/buddyinfo").readlines()
        for line in map(self.parse_line, buddyinfo):
            numa_node =  int(line["numa_node"])
            zone = line["zone"]
            free_fragments = map(int, line["nr_free"].split())
            max_order = len(free_fragments)
            fragment_sizes = self.get_order_sizes(max_order)
            usage_in_bytes =  [block[0] * block[1] for block in zip(free_fragments, fragment_sizes)]
            buddyhash[numa_node].append({
                "zone": zone,
                "nr_free": free_fragments,
                "sz_fragment": fragment_sizes,
                "usage": usage_in_bytes })
        return buddyhash

    def load_buddyinfo(self):
        buddyhash = self.read_buddyinfo()
        #self.log.info(buddyhash)
        return buddyhash

    def page_size(self):
        return os.sysconf("SC_PAGE_SIZE")

    def get_order_sizes(self, max_order):
        return [self.page_size() * 2**order for order in range(0, max_order)]

    def __str__(self):
        ret_string = ""
        width = 20
        for node in self.buddyinfo:
            ret_string += "Node: %s\n" % node
            for zoneinfo in self.buddyinfo.get(node):
                ret_string += " Zone: %s\n" % zoneinfo.get("zone")
                ret_string += " Free KiB in zone: %.2f\n" % (sum(zoneinfo.get("usage")) / (1024.0))
                ret_string += '\t{0:{align}{width}} {1:{align}{width}} {2:{align}{width}}\n'.format(
                        "Fragment size", "Free fragments", "Total available KiB",
                        width=width,
                        align="<")
                for idx in range(len(zoneinfo.get("sz_fragment"))):
                    ret_string += '\t{order:{align}{width}} {nr:{align}{width}} {usage:{align}{width}}\n'.format(
                        width=width,
                        align="<",
                        order = zoneinfo.get("sz_fragment")[idx],
                        nr = zoneinfo.get("nr_free")[idx],
                        usage = zoneinfo.get("usage")[idx] / 1024.0)

        return ret_string

def main():
    """Main function. Called when this file is a shell script"""
    usage = "usage: %prog [options]"
    parser = optparse.OptionParser(usage)
    parser.add_option("-s", "--size", dest="size", choices=["B","K","M"],
                      action="store", type="choice", help="Return results in bytes, kib, mib")

    (options, args) = parser.parse_args()
    logger = Logger(logging.DEBUG).get_logger()
    #logger.info("Starting....")
    #logger.info("Parsed options: %s" % options)
    #print logger
    buddy = BuddyInfo(logger)
    print buddy

if __name__ == '__main__':
    main()

$ cat /proc/buddyinfo

위의 코드 결과들에 대해 이해하기 위해 위의 자료를 보자

기본적으로 처음 시작시 order=10(4MB) 크기 단위로만 page 묶음이 존재하며,

추후 task에 의해 order 10보다 작은 크기의 page가 요청될 경우 order 10의 메모리는 분할된 후 요청된 크기에 맞게 메모리를 할당하게 됨

여기서 처음 조각을 지속적으로 분할해 사용한다고 해서 rmqueue라는 명칭을 사용함

분할 ex) 2^10 -> 2^9  * 2 형태로 분할

회수 ex) 이웃한 2^9 * 2 -> 2^10

분할 <-> 회수 의 과정이 지속적으로 반복 됨

 

$ vim ${linux}/mm/page_allo.c
struct page *rmqueue()
struct page *__rmqueue_smallest()

<rmqueue를 통해 page 분할 후 할당 하는 부분에 대해 실습이 이루어 지지 않음, 추가적으로 진행 필요>

 

 

# cd /sys/kernel/debug/tracing
$ echo 1 > events/kmem/mm_page_free/enable   
$ echo 1 > options/stacktrace
$ cat trace

$ vim ${linux}/mm/page_allo.c

page 해제는 free_pages() - > __free_one_page() 최종적으로 버디에 기반 메모리 해제 작업 수행됨

위 함수를 거쳐서 최종적으로 버디시스템 기반 메모리 해지작업 수행

 

+ Recent posts