
åçŽãªãã¯ã
ãã¹ãŠã¯åçŽãªãã¯ãã§å§ãŸããŸããïŒïŒè¿äŒŒã³ãŒãïŒ
#define ADD_BYTE(C) do { \ if (offset == capa) { \ if (capa < 16) { \ capa = 16; \ } else { \ capa <<= 1; \ } \ buffer = realloc(buffer, capa); \ assert(buffer != NULL); \ } \ buffer[offset++] = (C); \ } while(0)
Cããã°ã©ãã³ã°èšèªã«ç²ŸéããŠããªã人ã®ããã«ããã®åçŽãªãã¯ãã¯ããã€ãïŒCïŒãåçã«å²ãåœãŠããããããã¡ãŒïŒãããã¡ãŒïŒã«è¿œå ããŸãã èšé²ã®æ¬¡ã®äœçœ®ã¯ãoffsetãã©ã¡ãŒã¿ãŒã䜿çšããŠæ±ºå®ãããŸãã ãããã¡ãŒããã£ã±ãã«ãªããã³ã«ãããªã¥ãŒã ã2åã«ãªããŸãïŒæå°ãµã€ãºã®16ãã€ãããéå§ïŒã
åçãããã¡ã«ãã€ããè¿œå ããŸã-ããã¯ãã»ãšãã©ãã¹ãŠã®ããã°ã©ã ã§æãäžè¬çãªæäœã®1ã€ã§ãïŒæååãé åãªã©ãæäœããããïŒã
ããããåé åžæŠç¥ãããã«å¹æçã§ããããã©ã®ããã«ç解ããã®ã§ããããïŒ ãã®åé¡ãè€éãã®èŠ³ç¹ããèŠãŠãreallocïŒïŒã®è€éããOïŒNïŒã«ãããšã1ãã€ããè¿œå ãããšå¹³å OïŒ1ïŒã®è€éããããã«ããããŸããããã¯éåžžã«è¯ãããšã§ãã
ããããææªã®å Žåã®è€éãã¯äœã§ããïŒãããã¡ãžã®ã¡ã¢ãªã®åå²ãåœãŠãå¿ èŠãªå Žåã¯ã©ãã§ããããïŒ
å¿å ïŒããã¯è¯ãæŠç¥ã§ããïŒ å€§ããªã¢ã¬ã€ïŒ1ã®ã¬ãã€ããªã©ïŒã®ãµã€ãºãå¢ããå§ãããšãæ·±å»ãªããã©ãŒãã³ã¹ã®åé¡ãçºçããŸãã ç¹ã«ããããã¡ãã¹ã¯ãããã¡ã€ã«ãã移åããå¿ èŠãããå Žåã¯ãçµæãæ³åããŠãã ããã
ç§ ïŒããŒã...æ£çŽãªãšãããç§ã¯ããã«ã€ããŠèããããšã¯ãããŸãã...ããããç§ã¯ãã¹ãŠãããã»ã©æããªããšç¢ºä¿¡ããŠããŸãã ã·ã¹ãã ã¯ãã®ã¿ã¹ã¯ã«æ£åžžã«å¯ŸåŠããå¿ èŠããããŸãã
å¿å ïŒãããŠãåãææ°æŠç¥ã§ãã£ãŠããæ¥ç¶ãããæ§é ãããè¯ãéžæè¢ã§ããããã«æããŸãã
ç§ ïŒããããæéã®ç¡é§ã§ãã
å¿å ïŒèšŒæ ïŒ
ãããããã§ããïŒïŒ
ããŠãããªãã¯èªåã®ç«å Žãæ£åœåããå¿ èŠããããŸãã ãã¹ãŠã®ããã°ã©ããšåæ§ã«ãç§ã¯ãšãŠãæ ãè ã§ãã ããæ£ç¢ºã«ã¯ïŒã ããã°ã©ããŒã«ãµããããã®ã§ ãç§ã¯ãšãŠãæ ãè ã§ããã æ azineã¯ãããã¹ããŒãã§å¹ççã«ãªãããã®å€§ããªã€ã³ã»ã³ãã£ãã§ãã ç¹°ãè¿ãã®ã¿ã¹ã¯ãæ¥åžžçãªã¿ã¹ã¯ã解決ããã®ãé¢åã§ãã ãã£ãšã€ã³ããªãžã§ã³ããªãã® ãã§ããã ãéã ãã®ãå¿ èŠã§ã ã ã¯ããããã¯æã æ sometimesãšåŒã°ãããã®ã§ãã ããããç§ã®æèŠã§ã¯ãããã¯å¹çã«ä»ãªããŸããã
ãããã¡ãä¿åããããã®ãããã¯ã®ãªã³ã¯ãªã¹ãã¯ãããé¢åãªè§£æ±ºçã§ãã ãããäžå¯èœã ãšèšã£ãŠããã®ã§ã¯ãããŸãã ã ã ã€ã³ããã·ãã«ã¯ãã©ã³ã¹èªã§ã¯ãªã ãããã¬ãªã³ã¯ãã€ãŠèšã£ãïŒåœŒã¯ã²ã©ãçµãã£ããïŒã 解決çã¯å¯èœã§ãããé¢åã§ãã ãµãã¢ã¬ã€ãã³ããŒãããããã£ã¹ã¯ã«ä¿åãããããã«ã¯å€å°ã®æéãããããŸãã ãããããåé åã®å é ãžã®ãã€ã³ã¿ã®ã€ã³ããã¯ã¹ãç¶æããå¿ èŠããããŸãããããã¯ã®å é ã®ã¢ãã¬ã¹ãšå€ãã®éå±ãªãã®ãååŸããããã«ã2ãåºãšãã察æ°ãååŸããŸãã
ç§ã®æ lazã¯ããã§æãæè¿ãããããšãäœããæããŠãããŸãã çµå±ã®ãšãããå°ããªãããã¯ã«ã€ããŠã¯ãŸã£ããå¿é ããå¿ èŠã¯ãããŸãããã·ã¹ãã ã®ä»®æ³ã¡ã¢ãªã倧ããªãããã¯ãåŠçããŸãã
ãããç解ããŸãããã

ãšããã§ãreallocïŒïŒãšã¯äœã§ããïŒ
ããã¯ãCã©ã€ãã©ãªã«å®è£ ãããŠããäžè¬çãªPOSIXæºæ ã®æ©èœã§ãã Linuxã®å Žåãããã¯libc.soã©ã€ãã©ãªã§ãããmallocããã³freeé¢é£ã®é¢æ°reallocãå«ãŸããŠããŸãã
nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep -E "T (malloc|realloc|free)$" 000000000007e450 T free 000000000007e030 T malloc 000000000007e4e0 T realloc
æ¬åœã«èå³ããã人ã®ããã«ïŒãTãã¯ãããã¹ãã·ã³ãã«ããæå³ãã倧æåã¯ãã®ã·ã³ãã«ãå ¬éãããŠèŠããããšã瀺ãããã«äœ¿çšãããŸãã ããã°ã©ã ããã¹ãã»ã°ã¡ã³ãã¯code ã ããŒã¿ã»ã°ã¡ã³ãã¯åæåããŒã¿ïŒå€æ°ïŒã bssã»ã°ã¡ã³ãã¯åæåãããŠããªãããŒã¿ïŒå€æ°ïŒããŸãã¯ããŒã¿ïŒå€æ°ïŒã§ãããåæå€ãšããŠãŒããåãåããŸããïŒã
ã¡ã¢ãªã¢ãã±ãŒã¿ã¯ãã¡ã¢ãªã®å²ãåœãŠãåå²ãåœãŠãããã³è§£æŸã«äœ¿çšãããŸãïŒããããšããCaptain ObviousïŒïŒã ãã®ãããªãã£ã¹ããªãã¥ãŒã¿ãŒã¯æ°å€ããããŸãïŒæãæåãªã®ã¯ããã£ã¢ãã±ãŒã¿ãŒã§ã ïŒã
çŽ æŽãããã²ã©ãsbrkåŒã³åºãã䜿çšããŠãç¬èªã®æãåçŽãªã¢ãã±ãŒã¿ãŒãå®è£ ã§ããŸããããã¯ãããŒã¿ã»ã°ã¡ã³ãã®æåŸã«ç©ºã®ã¹ããŒã¹ãè¿œå ããã ãã§ãã
#include <sys/types.h> #include <unistd.h> #include <string.h> void *malloc(size_t size) { return sbrk(size); } void free(void *ptr) { /* ? */ } /* : (, ) */ void *realloc(void *ptr, size_t size) { void *nptr = malloc(size); if (nptr == NULL) { return NULL; } // «old_size» :) // (, ) memcpy(nptr, ptr, old_size); free(ptr); return nptr; }
[泚ã Hacker Newsã®èªè ã®äžäººãæ£ããè¿°ã¹ãããã«ãããã§ã¯ãããšãã°ãããã¯ãéžæãããåŸã«ãããã¯ã®å é ã«æžã蟌ãããšãã§ããold_sizeå€ãå¿ èŠã§ãã ããããããããreallocã§ãšã©ãŒãçºçããå Žåã«ãœãŒã¹ãããã¯ã解æŸãã¹ãã§ã¯ãããŸããã§ãã:)]

倧ããªãããã¯ã§äœãèµ·ããããç解ããã«ã¯ãLinuxäžã®Glibcããã詳ããç¥ãå¿ èŠããããŸãã
Glibcã®çŽ¹ä»
ææ°ã®glibcãããŠã³ããŒãã㊠ããœãŒã¹ããªãŒã調ã¹ãã ãã§ãã
èå³æ·±ãmallocãã£ã¬ã¯ããªã1ã€ãããŸãã ãã®äžã®malloc.cãã¡ã€ã«ãèŠã€ããŠããšãã£ã¿ãŒã§éããŸãã
/* : * (>= 512 ), FIFO (. . ). * (<= 64 ) , . * , , , . * (>= 128 ) , . , , : http://gee.cs.oswego.edu/dl/html/malloc.html */
ãã®éšåã«æãé¢å¿ããããŸãïŒã éåžžã«å€§ããªãªã¯ãšã¹ãïŒããã©ã«ãã§ã¯128 Kb以äžïŒã®å ŽåããµããŒããããŠããå Žåãã·ã¹ãã ã¯ã¡ã¢ãªãããã³ã°ã䜿çšããŸã ãã
128 Kbã®ãããå€ã¯malloptïŒïŒé¢æ°ã«ãã£ãŠèšå®ãããŸãã
M_MMAP_THRESHOLD , , ( ), M_MMAP_THRESHOLD, mmap(2), sbrk(2).
ãã®ãããç§ãèšã£ãããã«ãã·ã¹ãã èªäœã®ä»®æ³ã¡ã¢ãªãµãŒãã¹ã倧ããªãããã¯ãåŠçããŸãã
æ¬è³ªçã«ãããã¯æ¬¡ã®ããšãæå³ããŸãã
- mallocã¯mmapã䜿çšããŠå®è£ ãããŸãã
- freeã¯munmapã䜿çšããŠå®è£ ãããŸãã
- reallocã¯mremapã䜿çšããŠå®è£ ãããŸãïŒäœïŒïŒPOSIXã«ã¯ãŸã ãããŸãããïŒçå£ã«ïŒïŒ
ããŠãmremapã®ç¥èãå°ãåºããŠã¿ãŸãããã
ããã§ãç§ãã¡ã¯man mremapã«ã€ããŠäœãç¥ã£ãŠããŸããïŒ
mremap() Linux. mremap() . , realloc(3).
ããã éåžžã«å¹æçãªreallocã§ãã ã©ã®ãããå¹æçã§ããïŒ
æåã«ãmmapãmunmapãmremapãglibcã©ã€ãã©ãªã«èšè¿°ãããŠããŸãã å®éããšã³ããªãã€ã³ãã®ã¿ïŒ
nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep -E "(mmap|munmap|mremap)$" 00000000000e4350 W mmap 00000000000e9080 W mremap 00000000000e4380 W munmap
ãã®å Žåãããã©ã«ãã®ãšã³ããªãã€ã³ãã¯ã匱ããæåã§ããããšã«æ³šæããŠãã ããã ã€ãŸããåçãªã³ã¯äžã«ä»ã®èª°ãã«ãã£ãŠäžæžããããå¯èœæ§ããããŸãã äŸïŒ
ldd /tmp/sample linux-vdso.so.1 (0x00007584a8aaa000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007584a86e6000) /lib64/ld-linux-x86-64.so.2 (0x00007584a8aac000
...æåã¯linux-vdso.so.1ã©ã€ãã©ãªã§ãªãŒããŒã©ã€ãã§ããŸã-ããã¯ãã¹ãŠã®Linuxããã°ã©ã ã«è¡šç€ºãããéæ³ã®ã©ã€ãã©ãªã§ã ã·ã¹ãã ã³ãŒã«ãå«ãäžéšã®ã³ãŒã«ãé«éåã§ããŸãã
ãããã«ãããglibcã©ã€ãã©ãªã®ãã£ã©ã¯ã¿ãŒã¯ãã·ã¹ãã ã«ãŒãã«ïŒsyscallïŒãåŒã³åºãããã®ãã£ãã«ã§ãããglibcãŸãã¯vdsoã«ãªããŸãïŒããã©ã«ãã®å®è£ ãåç §ããŠãã ããïŒsysdeps / unix / sysv / linux / mmap64.cïŒã äŸïŒ
void * __mmap64 (void *addr, size_t len, int prot, int flags, int fd, off64_t offset) { // void *result; result = (void *) INLINE_SYSCALL (mmap2, 6, addr, len, prot, flags, fd, (off_t) (offset >> page_shift)); return result; }

ãããã£ãŠãæåã®è³ªåã¯glibcã§ã¯ãªããLinuxã«ãŒãã«ã«é¢é£ããŠããŸãã
ã³ã¢ãç¥ã
ææ°ã®ã«ãŒãã«ããŒãžã§ã³ãããŠã³ããŒããã mremapã®ä»çµã¿ãç°¡åã«èŠãŠã¿ãŸãããã
ããã¥ã¢ã«ïŒ The Linux Kernel Howto ïŒãèŠããšãéåžžã«èå³æ·±ããã£ã¬ã¯ããªãèŠã€ãããŸãïŒ
mmãã£ã¬ã¯ããªã«ã¯ãã¡ã¢ãªãæäœããããã«å¿ èŠãªãã¹ãŠã®ã³ãŒããå«ãŸããŠããŸãã ã¢ãŒããã¯ãã£åºæã®ã¡ã¢ãªç®¡çã³ãŒãã¯ãarch / * / mm /ãšãã圢åŒã®ãã£ã¬ã¯ããªã«é 眮ãããŸãïŒäŸïŒarch / i386 / mm / fault.cïŒã
çŽ æŽãããã ããããå¿ èŠã§ãïŒ
ããã«èå³æ·±ããã¡ã€ã«ããããŸãïŒmm / mremap.cã ãã®äžã«ãmremapé¢æ°ãžã®ã·ã¹ãã ã³ãŒã«ã®ãšã³ããªãã€ã³ãããããŸãã ããã«ïŒ
/* * ( ) , , * ( MREMAP_MAYMOVE VM) * * MREMAP_FIXED 5 1999 . (Benjamin LaHaise) * MREMAP_MAYMOVE. */ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len, unsigned long, new_len, unsigned long, flags, unsigned long, new_addr) {
ïŒglibcãŸãã¯å¯Ÿå¿ããvdsoãã£ãã«ãä»ããŠïŒãŠãŒã¶ãŒã³ãŒãã§å¯Ÿå¿ããã·ã¹ãã ã³ãŒã«ãè¡ããšãã« ãã«ãŒãã«ã«å ¥ããŸãã
ãã®é¢æ°ã®ã³ãŒããåŠç¿ãããšãããŸããŸãªåŒæ°ã®ãã§ãã¯ãšäºçŽ°ãªã±ãŒã¹ã®åŠçãããããŸãïŒããšãã°ãã¡ã¢ãªãããã¯ã®åæž -ãããã¯ã®æåŸã§ããŒãžã解æŸããã ãã§ãïŒã
次ã«ãã«ãŒãã«ã¯è¡šç€ºé åãæ¡å€§ããŠæ¡å€§ããããšããŸãïŒsbrkã䜿çšããCã©ã€ãã©ãªå ã®ã¢ãã±ãŒã¿ãŒãåæ§ã§ãïŒã æ¡åŒµãæåããå Žåãé¢æ°ã¯çµæãè¿ããŸãã
ãããã®äºçŽ°ãªã±ãŒã¹ã¯ãã¹ãŠOïŒ1ïŒã®è€éãã§å®è£ ãããŠããŸãïŒã«ãŒãã«ã«å ¥ãã³ã¹ããèæ ®ããŠããå²ã蟌ã¿ã¯äœ¿çšãããªããªããŸãããããã§ã䜿çšãããŸãïŒã
ãããŠã ææªã®ã·ããªãªã¯äœã§ããïŒ
/* * * . */ ret = -ENOMEM; if (flags & MREMAP_MAYMOVE) { unsigned long map_flags = 0; if (vma->vm_flags & VM_MAYSHARE) map_flags |= MAP_SHARED; new_addr = get_unmapped_area(vma->vm_file, 0, new_len, vma->vm_pgoff + ((addr - vma->vm_start) >> PAGE_SHIFT), map_flags); if (new_addr & ~PAGE_MASK) { ret = new_addr; goto out; } map_flags = vma->vm_flags; ret = move_vma(vma, addr, old_len, new_len, new_addr); if (!(ret & ~PAGE_MASK)) { track_exec_limit(current->mm, addr, addr + old_len, 0UL); track_exec_limit(current->mm, new_addr, new_addr + new_len, map_flags); } }
äžèŠãããšãã«ãŒãã«ã¯åçŽãªã¢ãã±ãŒã¿ãŒãšåãããšãè¡ããŸãã
- æ°ããé åã匷調衚瀺ããŸãã
- å ã®é åãæ°ããé åã«ã³ããŒããŠãã解æŸããŸãïŒã€ãŸãã移åæäœãå®è¡ãããŸãïŒã
ãããã詳ããèŠãŠã¿ãŸãããã
move_vmaã®åŒã³åºãã¯æ¬¡ã®ãšããã§ãã
static unsigned long move_vma(struct vm_area_struct *vma, unsigned long old_addr, unsigned long old_len, unsigned long new_len, unsigned long new_addr) { ... new_pgoff = vma->vm_pgoff + ((old_addr - vma->vm_start) >> PAGE_SHIFT); new_vma = copy_vma(&vma, new_addr, new_len, new_pgoff, &need_rmap_locks); if (!new_vma) return -ENOMEM; moved_len = move_page_tables(vma, old_addr, new_vma, new_addr, old_len, need_rmap_locks);

ããã«ã¯2ã€ã®èå³æ·±ã課é¡ããããŸãã
- copy_vma;
- move_page_tablesã
copy_vmaé¢æ°ã¯ãmm / mmap.cãã¡ã€ã«ã«èšè¿°ãããŠããŸãã ã¿ã€ãvm_area_structã®æ§é ã移åããŸã-ããã¯ãã¡ã¢ãªã®ãããã¯ãèšè¿°ããã«ãŒãã«ã®å éšæ§é ã§ãã
å®çŸ©ã¯ããã«ãããŸãïŒinclude / linux / mm_types.hã ãã®å°ããªæ§é ã«ã¯ãé åã«é¢ãããã¹ãŠã®æ å ±ãå«ãŸããŸãïŒéå§ã¢ãã¬ã¹ãšçµäºã¢ãã¬ã¹ããã£ã¹ã¯äžã®ãã¡ã€ã«ïŒãã¡ã€ã«ã衚瀺ããããã«ã¡ã¢ãªé åã䜿çšãããå ŽåïŒãªã©ã
ãããã£ãŠããã®ç§»åæäœã¯éåžžã«ç°¡åã§ã-OïŒ1ïŒã
move_page_tablesé¢æ°ã¯äœãããŸããïŒ
æãèå³æ·±ãã®ã¯ãã®ã«ãŒãã«ããããã§ãïŒ
... for (; old_addr < old_end; old_addr += extent, new_addr += extent) { ... move_ptes(vma, old_pmd, old_addr, old_addr + extent, new_vma, new_pmd, new_addr, need_rmap_locks); ...
ãã®ã³ãŒãã¯å°ãè€éã§ãïŒããã«ãã³ã¡ã³ãã¯ãããŸããïŒããåºæ¬çã«ãããã¯åºæ¬çãªç®è¡æŒç®ãšèšç®ã§ãã
move_ptesé¢æ°ã«ã¯ãæäœã¬ãã«ã®å éšã«ãŒããå«ãŸããŸãã
... for (; old_addr < old_end; old_pte++, old_addr += PAGE_SIZE, new_pte++, new_addr += PAGE_SIZE) { if (pte_none(*old_pte)) continue; pte = ptep_get_and_clear(mm, old_addr, old_pte); pte = move_pte(pte, new_vma->vm_page_prot, old_addr, new_addr); ... set_pte_at(mm, new_addr, new_pte, pte); } ...
ããã§ã¯ããã®ãµã€ã¯ã«ã§äœãèµ·ããã®ã§ããããïŒïŒ ã¡ã¢ãªé åã«å¯Ÿå¿ããããŒãžããŒãã«ã®è¡ïŒ PTE ã ããŒãžããŒãã«ãšã³ã㪠ïŒãå¥ã®å Žæã«ç§»åããã ãã§ãã æ¬è³ªçã«ããããã¯åããŒãžã«å²ãåœãŠãããæŽæ°ã§ãã
ããã§ãç§ãã¡ã«ã¯äœããããŸãããå®éãã«ãŒãã«ã¯ãããããããããã¯ã®ããŒã¿ã«è§ŠããŸããã§ãã ã ãšãªã¢å šäœã移åããã«ã¯ãæ°ãããã亀æããã ãã§ååã§ããã
æ£åŒã«ã¯ãè€éãã¯ãŸã OïŒNïŒã§ããã
- 4 KBïŒãŸãã¯éåžžã«å€§ããªããŒãžã®å Žåã¯2 MBïŒã®ããŒãžå šäœãã³ããŒãã代ããã«ãã«ãŒãã«æ§é å ã§æŽæ°ã亀æããŸãã
- ãã³ãŒã«ããã¡ã¢ãªã«è§Šããããšãªããããããã«ãŒãã«ã¡ã¢ãªãæäœããŸããããŒãžãã¡ã€ã«ã«ããã·ã¥ãããããŒã¿ã¯ã¯ããã«å°ãªããªããŸãã
ãããã£ãŠãOïŒNïŒã䜿çšããŸããã倧ããªéãããããŸãã
ããããšããã§ãOïŒNïŒãå€ãã®å ŽåçŽããããããšãç¥ã£ãŠããŸããïŒ
ãã®å ŽåãNã®æ倧å€ã¯2 48 ïŒä»®æ³ç©ºéã®æ倧ãµã€ãºïŒã§ãã ã»ãšãã©ã®ã³ã³ãã¥ãŒã¿ãŒã¯ãããæ°ã®ã¬ãã€ãã®ã¡ã¢ãªã§åäœããŸã-ã»ãšãã©ã®ã¢ãŒããã¯ãã£ã§ã¯64 GBãè¶ ããŸããïŒã€ãŸãã2 36ãã€ãïŒã ããã°ããŒãžã¯2 21ãã€ãã§ãmove_ptesã®æäœã®æ倧æ°ã¯2 15ã§ãïŒã¯ãããã㯠32,768æäœã®ã¿ã§ãïŒã
ãããã£ãŠãææªã®å Žåãã³ã¹ãã¯åžžã«æ¥µããŠäœããäœããã®çç±ã§æåãããããçããŸããã§ããã
ãŸããèªãããšããå§ãããŸãïŒMel Gormanã«ããLinux Virtual Memory Managerã®æ¬ãç解ãã ã
ãã€ã¢ã·ãªã«ã»ã ãã¬ãã«ãïŒ å¿é ããªãã§ã æ azineãšreallocããã¹ãŠã§ãã