malloc コードを読みたい変わり者向けの項目です。正直なところ、暇のある人にしかお薦めできないので、普通に読みた方はスキップした方が良いと思いますぅ… まる (´・ω・`)
_int_malloc() 関数さえ理解できれば malloc() の理解も出来るといえるでしょう。
読むのはガチにむっちゃ大変ですぞ…
(´・ω・`)
まあ一行づつ読み解くのは難しいですが、おおまかにどの箇所がどの処理をしているくらいの説明はやってみたいと思いまする…
(´・ω・`)
malloc.c(https://github.com/MacKomatsu/glibc/blob/release/2.27/master/malloc/malloc.c).
3515 /* 3516 ------------------------------ malloc ------------------------------ 3517 */ 3518 3519 static void * 3520 _int_malloc (mstate av, size_t bytes) 3521 { 3522 INTERNAL_SIZE_T nb; /* normalized request size */ 3523 unsigned int idx; /* associated bin index */ 3524 mbinptr bin; /* associated bin */ 3525 3526 mchunkptr victim; /* inspected/selected chunk */ 3527 INTERNAL_SIZE_T size; /* its size */ 3528 int victim_index; /* its bin index */ 3529 3530 mchunkptr remainder; /* remainder from a split */ 3531 unsigned long remainder_size; /* its size */ 3532 3533 unsigned int block; /* bit map traverser */ 3534 unsigned int bit; /* bit map traverser */ 3535 unsigned int map; /* current word of binmap */ 3536 3537 mchunkptr fwd; /* misc temp for linking */ 3538 mchunkptr bck; /* misc temp for linking */ 3539 3540 #if USE_TCACHE 3541 size_t tcache_unsorted_count; /* count of unsorted chunks processed */ 3542 #endif 3543 3544 /* 3545 Convert request size to internal form by adding SIZE_SZ bytes 3546 overhead plus possibly more to obtain necessary alignment and/or 3547 to obtain a size of at least MINSIZE, the smallest allocatable 3548 size. Also, checked_request2size traps (returning 0) request sizes 3549 that are so large that they wrap around zero when padded and 3550 aligned. 3551 */ 3552 3553 checked_request2size (bytes, nb); 3554
bytes のアラインメントを取ります。
3555 /* There are no usable arenas. Fall back to sysmalloc to get a chunk from 3556 mmap. */ 3557 if (__glibc_unlikely (av == NULL)) 3558 { 3559 void *p = sysmalloc (nb, av); 3560 if (p != NULL) 3561 alloc_perturb (p, bytes); 3562 return p; 3563 } 3564
av が NULL かチェックします。
もし av が NULL なら symalloc() 関数をコールします。
sysmalloc() の割り当てが成功したら alloc_perturb() を呼び出してポインターを返します。
3565 /* 3566 If the size qualifies as a fastbin, first check corresponding bin. 3567 This code is safe to execute even if av is not yet initialized, so we 3568 can try it without checking, which saves some time on this fast path. 3569 */ 3570 3571 #define REMOVE_FB(fb, victim, pp) \ 3572 do \ 3573 { \ 3574 victim = pp; \ 3575 if (victim == NULL) \ 3576 break; \ 3577 } \ 3578 while ((pp = catomic_compare_and_exchange_val_acq (fb, victim->fd, victim)) \ 3579 != victim); \ 3580
REMOVE_FB マクロを定義しています。
3581 if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ())) 3582 { 3583 idx = fastbin_index (nb); 3584 mfastbinptr *fb = &fastbin (av, idx); 3585 mchunkptr pp; 3586 victim = *fb; 3587 3588 if (victim != NULL) 3589 { 3590 if (SINGLE_THREAD_P) 3591 *fb = victim->fd; 3592 else 3593 REMOVE_FB (fb, pp, victim); 3594 if (__glibc_likely (victim != NULL)) 3595 { 3596 size_t victim_idx = fastbin_index (chunksize (victim)); 3597 if (__builtin_expect (victim_idx != idx, 0)) 3598 malloc_printerr ("malloc(): memory corruption (fast)"); 3599 check_remalloced_chunk (av, victim, nb); 3600 #if USE_TCACHE 3601 /* While we're here, if we see other chunks of the same size, 3602 stash them in the tcache. */ 3603 size_t tc_idx = csize2tidx (nb); 3604 if (tcache && tc_idx < mp_.tcache_bins) 3605 { 3606 mchunkptr tc_victim; 3607 3608 /* While bin not empty and tcache not full, copy chunks. */ 3609 while (tcache->counts[tc_idx] < mp_.tcache_count 3610 && (tc_victim = *fb) != NULL) 3611 { 3612 if (SINGLE_THREAD_P) 3613 *fb = tc_victim->fd; 3614 else 3615 { 3616 REMOVE_FB (fb, pp, tc_victim); 3617 if (__glibc_unlikely (tc_victim == NULL)) 3618 break; 3619 } 3620 tcache_put (tc_victim, tc_idx); 3621 } 3622 } 3623 #endif 3624 void *p = chunk2mem (victim); 3625 alloc_perturb (p, bytes); 3626 return p; 3627 } 3628 } 3629 } 3630
リクエストサイズに応じたビンにアクセスするためにインデックスを算出します。
ビンの最初のチャンクを削除して、victim がそのチャンクを指すようにします。
victim が NULL ならスモールビン( Small Bin )のチェックに進みます。
victim が NULL でないならチャンクサイズをチェックし、該当するビンにあることを確かめ、そうでないならエラーを投げます。
最後に alloc_perturb() をコールしポインターを返します。
3631 /* 3632 If a small request, check regular bin. Since these "smallbins" 3633 hold one size each, no searching within bins is necessary. 3634 (For a large request, we need to wait until unsorted chunks are 3635 processed to find best fit. But for small ones, fits are exact 3636 anyway, so we can check now, which is faster.) 3637 */ 3638 3639 if (in_smallbin_range (nb)) 3640 { 3641 idx = smallbin_index (nb); 3642 bin = bin_at (av, idx); 3643
リクエストサイズがスモールビン( Small Bin )の範囲内に処理されるコードブロックですね。
リクエストサイズに応じたビンにアクセスするためのインデックスを算出します。
3644 if ((victim = last (bin)) != bin) 3645 {
該当するビンにチャンクが存在しない場合は次のケースに飛びます。
last(bin) つまり bin→bk が victim へ代入されますが、もし bin が同じなら malloc_consolidate() 関数をコールしてから、次のケースにスキップします。
3646 bck = victim->bk; 3647 if (__glibc_unlikely (bck->fd != victim)) 3648 malloc_printerr ("malloc(): smallbin double linked list corrupted");
bin→bk が bin と違うとするなら、victim→bk→fd と victim が同じかチェックします。
もし同じでないなら malloc_printerr() をコールしてエラーを投げます。
3649 set_inuse_bit_at_offset (victim, nb);
victim の次のチャンクのために PREV_INUSE ビットを設定します。
3650 bin->bk = bck; 3651 bck->fd = bin;
このチャンクをビンのリストから削除します。
3652 3653 if (av != &main_arena) 3654 set_non_main_arena (victim); 3655 check_malloced_chunk (av, victim, nb);
av に応じた適切なアリーナのビットをこのチャンクに設定します。
3656 #if USE_TCACHE 3657 /* While we're here, if we see other chunks of the same size, 3658 stash them in the tcache. */ 3659 size_t tc_idx = csize2tidx (nb); 3660 if (tcache && tc_idx < mp_.tcache_bins) 3661 { 3662 mchunkptr tc_victim; 3663 3664 /* While bin not empty and tcache not full, copy chunks over. */ 3665 while (tcache->counts[tc_idx] < mp_.tcache_count 3666 && (tc_victim = last (bin)) != bin) 3667 { 3668 if (tc_victim != 0) 3669 { 3670 bck = tc_victim->bk; 3671 set_inuse_bit_at_offset (tc_victim, nb); 3672 if (av != &main_arena) 3673 set_non_main_arena (tc_victim); 3674 bin->bk = bck; 3675 bck->fd = bin; 3676 3677 tcache_put (tc_victim, tc_idx); 3678 } 3679 } 3680 } 3681 #endif
TCACHE についての説明は割愛します。
3682 void *p = chunk2mem (victim); 3683 alloc_perturb (p, bytes); 3684 return p; 3685 } 3686 }
後は alloc_perturb() をコールしてポインターを返します。
3687 3688 /* 3689 If this is a large request, consolidate fastbins before continuing. 3690 While it might look excessive to kill all fastbins before 3691 even seeing if there is space available, this avoids 3692 fragmentation problems normally associated with fastbins. 3693 Also, in practice, programs tend to have runs of either small or 3694 large requests, but less often mixtures, so consolidation is not 3695 invoked all that often in most programs. And the programs that 3696 it is called frequently in otherwise tend to fragment. 3697 */ 3698 3699 else 3700 { 3701 idx = largebin_index (nb); 3702 if (atomic_load_relaxed (&av->have_fastchunks)) 3703 malloc_consolidate (av); 3704 }
bin→bk と bin が同じ場合に呼ばれます。
リクエストサイズに応じた適切なビンのインデックスを算出します。
もし av がファーストチャンク(Fast Bin のチャンク)を保持しているなら malloc_consolidate() を av に対してコールします。
ここまではファーストビン、スモールビンにはチャンクがないか、リクエストサイズが該当しませんでした。
残りはラージビン( Large Bin )だけですね… (´・ω・`)
3705 3706 /* 3707 Process recently freed or remaindered chunks, taking one only if 3708 it is exact fit, or, if this a small request, the chunk is remainder from 3709 the most recent non-exact fit. Place other traversed chunks in 3710 bins. Note that this step is the only place in any routine where 3711 chunks are placed in bins. 3712 3713 The outer loop here is needed because we might not realize until 3714 near the end of malloc that we should have consolidated, so must 3715 do so and retry. This happens at most once, and only when we would 3716 otherwise need to expand memory to service a "small" request. 3717 */ 3718 3719 #if USE_TCACHE 3720 INTERNAL_SIZE_T tcache_nb = 0; 3721 size_t tc_idx = csize2tidx (nb); 3722 if (tcache && tc_idx < mp_.tcache_bins) 3723 tcache_nb = nb; 3724 int return_cached = 0; 3725 3726 tcache_unsorted_count = 0; 3727 #endif 3728 3729 for (;; ) 3730 {
このループではチャンクをビンに挿入していきます。
3731 int iters = 0; 3732 while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
未整理ビン( Unsorted Bin )にチャンクがないかチェックして victim に代入します。
3733 { 3734 bck = victim->bk; 3735 if (__builtin_expect (chunksize_nomask (victim) <= 2 * SIZE_SZ, 0) 3736 || __builtin_expect (chunksize_nomask (victim) 3737 > av->system_mem, 0)) 3738 malloc_printerr ("malloc(): memory corruption"); 3739 size = chunksize (victim); 3740
victim のチャンクサイズが 2*SIZE_SZ と最大値の範囲内にあるかチェックし、範囲外であればエラーを投げます。
問題がなければ victim のチャンクサイズが size 変数に代入されます。
3741 /* 3742 If a small request, try to use last remainder if it is the 3743 only chunk in unsorted bin. This helps promote locality for 3744 runs of consecutive small requests. This is the only 3745 exception to best-fit, and applies only when there is 3746 no exact fit for a small chunk. 3747 */ 3748 3749 if (in_smallbin_range (nb) && 3750 bck == unsorted_chunks (av) && 3751 victim == av->last_remainder && 3752 (unsigned long) (size) > (unsigned long) (nb + MINSIZE)) 3753 { 3754 /* split and reattach remainder */ 3755 remainder_size = size - nb; 3756 remainder = chunk_at_offset (victim, nb); 3757 unsorted_chunks (av)->bk = unsorted_chunks (av)->fd = remainder; 3758 av->last_remainder = remainder; 3759 remainder->bk = remainder->fd = unsorted_chunks (av); 3760 if (!in_smallbin_range (remainder_size)) 3761 { 3762 remainder->fd_nextsize = NULL; 3763 remainder->bk_nextsize = NULL; 3764 } 3765 3766 set_head (victim, nb | PREV_INUSE | 3767 (av != &main_arena ? NON_MAIN_ARENA : 0)); 3768 set_head (remainder, remainder_size | PREV_INUSE); 3769 set_foot (remainder, remainder_size); 3770 3771 check_malloced_chunk (av, victim, nb); 3772 void *p = chunk2mem (victim); 3773 alloc_perturb (p, bytes); 3774 return p; 3775 }
リクエストサイズがスモールビンの範囲内にあり、未整理ビンのチャンクで、チャンクサイズがリクエストサイズ以上で、victim が最後に残っているチャンクである場合には if 文のブロックが処理されます。
2 つのチャンクに分割します。
1 つめのチャンクは alloc_perturb をコールしてから返されます。
3776 3777 /* remove from unsorted list */ 3778 unsorted_chunks (av)->bk = bck; 3779 bck->fd = unsorted_chunks (av); 3780
未整理ビン( Unsorted Bin )からチャンクを削除します。
3781 /* Take now instead of binning if exact fit */ 3782 3783 if (size == nb) 3784 { 3785 set_inuse_bit_at_offset (victim, size); 3786 if (av != &main_arena) 3787 set_non_main_arena (victim); 3788 #if USE_TCACHE 3789 /* Fill cache first, return to user only if cache fills. 3790 We may return one of these chunks later. */ 3791 if (tcache_nb 3792 && tcache->counts[tc_idx] < mp_.tcache_count) 3793 { 3794 tcache_put (victim, tc_idx); 3795 return_cached = 1; 3796 continue; 3797 } 3798 else 3799 { 3800 #endif 3801 check_malloced_chunk (av, victim, nb); 3802 void *p = chunk2mem (victim); 3803 alloc_perturb (p, bytes); 3804 return p; 3805 #if USE_TCACHE 3806 } 3807 #endif 3808 }
もし victim のチャンクサイズがリクエストサイズと同じなら alloc_perturb をコールしてからポインターを返します。
3809 3810 /* place chunk in bin */ 3811 3812 if (in_smallbin_range (size)) 3813 { 3814 victim_index = smallbin_index (size); 3815 bck = bin_at (av, victim_index); 3816 fwd = bck->fd; 3817 }
もし victim のチャンクサイズがスモールビン( Small Bin )の範囲内にあるなら、チャンクを該当するスモールビンの HEAD の位置に挿入します。
3818 else 3819 { 3820 victim_index = largebin_index (size); 3821 bck = bin_at (av, victim_index); 3822 fwd = bck->fd; 3823 3824 /* maintain large bins in sorted order */ 3825 if (fwd != bck) 3826 { 3827 /* Or with inuse bit to speed comparisons */ 3828 size |= PREV_INUSE; 3829 /* if smaller than smallest, bypass loop below */ 3830 assert (chunk_main_arena (bck->bk)); 3831 if ((unsigned long) (size) 3832 < (unsigned long) chunksize_nomask (bck->bk)) 3833 { 3834 fwd = bck; 3835 bck = bck->bk; 3836 3837 victim->fd_nextsize = fwd->fd; 3838 victim->bk_nextsize = fwd->fd->bk_nextsize; 3839 fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim; 3840 } 3841 else 3842 { 3843 assert (chunk_main_arena (fwd)); 3844 while ((unsigned long) size < chunksize_nomask (fwd)) 3845 { 3846 fwd = fwd->fd_nextsize; 3847 assert (chunk_main_arena (fwd)); 3848 } 3849 3850 if ((unsigned long) size 3851 == (unsigned long) chunksize_nomask (fwd)) 3852 /* Always insert in the second position. */ 3853 fwd = fwd->fd; 3854 else 3855 { 3856 victim->fd_nextsize = fwd; 3857 victim->bk_nextsize = fwd->bk_nextsize; 3858 fwd->bk_nextsize = victim; 3859 victim->bk_nextsize->fd_nextsize = victim; 3860 } 3861 bck = fwd->bk; 3862 } 3863 } 3864 else 3865 victim->fd_nextsize = victim->bk_nextsize = victim; 3866 } 3867 3868 mark_bin (av, victim_index); 3869 victim->bk = bck; 3870 victim->fd = fwd; 3871 fwd->bk = victim; 3872 bck->fd = victim; 3873
ラージビン( Large Bin )にチャンクを挿入します。
まず最後の最少チャンクをチェックします。
次に victim が最後のチャンクより小さい場合は最後の位置に挿入します。
小さくないのであれば、victim 以上のサイズを持つチャンクをループで探索して、挿入します。
もしサイズが同じであれば該当チャンクの次の位置に挿入します。
3874 #if USE_TCACHE 3875 /* If we've processed as many chunks as we're allowed while 3876 filling the cache, return one of the cached ones. */ 3877 ++tcache_unsorted_count; 3878 if (return_cached 3879 && mp_.tcache_unsorted_limit > 0 3880 && tcache_unsorted_count > mp_.tcache_unsorted_limit) 3881 { 3882 return tcache_get (tc_idx); 3883 } 3884 #endif 3885 3886 #define MAX_ITERS 10000 3887 if (++iters >= MAX_ITERS) 3888 break; 3889 }
上記の処理を 10000 回繰り返します。
3890 3891 #if USE_TCACHE 3892 /* If all the small chunks we found ended up cached, return one now. */ 3893 if (return_cached) 3894 { 3895 return tcache_get (tc_idx); 3896 } 3897 #endif
TCACHE は割愛です… (´・ω・`)
3898 3899 /* 3900 If a large request, scan through the chunks of current bin in 3901 sorted order to find smallest that fits. Use the skip list for this. 3902 */ 3903 3904 if (!in_smallbin_range (nb)) 3905 {
リクエストサイズがスモールビンの範囲外かチェックします。
3906 bin = bin_at (av, idx);
ラージビンのインデックスを算出します。
3907 3908 /* skip scan if empty or largest chunk is too small */ 3909 if ((victim = first (bin)) != bin 3910 && (unsigned long) chunksize_nomask (victim) 3911 >= (unsigned long) (nb)) 3912 {
ビンの最初のチャンク(最大のチャンクサイズ)がリクエストサイズより大きいかチェックします。
3913 victim = victim->bk_nextsize; 3914 while (((unsigned long) (size = chunksize (victim)) < 3915 (unsigned long) (nb))) 3916 victim = victim->bk_nextsize; 3917
リクエストサイズ以上の最少サイズチャンクを見つけられるまで反復します。
3918 /* Avoid removing the first entry for a size so that the skip 3919 list does not have to be rerouted. */ 3920 if (victim != last (bin) 3921 && chunksize_nomask (victim) 3922 == chunksize_nomask (victim->fd)) 3923 victim = victim->fd; 3924 3925 remainder_size = size - nb; 3926 unlink (av, victim, bck, fwd);
残りのサイズ( remainder_size )を算出しときます。
unlink をコールして victim をビンから削除します。
3927 3928 /* Exhaust */ 3929 if (remainder_size < MINSIZE) 3930 { 3931 set_inuse_bit_at_offset (victim, size); 3932 if (av != &main_arena) 3933 set_non_main_arena (victim); 3934 }
残りのサイズ( remainder_size )が最少チャンクサイズ(MINSIZE)より小さいなら、一つのチャンクを返します。
3935 /* Split */ 3936 else 3937 { 3938 remainder = chunk_at_offset (victim, nb); 3939 /* We cannot assume the unsorted list is empty and therefore 3940 have to perform a complete insert here. */ 3941 bck = unsorted_chunks (av); 3942 fwd = bck->fd; 3943 if (__glibc_unlikely (fwd->bk != bck)) 3944 malloc_printerr ("malloc(): corrupted unsorted chunks"); 3945 remainder->bk = bck; 3946 remainder->fd = fwd; 3947 bck->fd = remainder; 3948 fwd->bk = remainder; 3949 if (!in_smallbin_range (remainder_size)) 3950 { 3951 remainder->fd_nextsize = NULL; 3952 remainder->bk_nextsize = NULL; 3953 } 3954 set_head (victim, nb | PREV_INUSE | 3955 (av != &main_arena ? NON_MAIN_ARENA : 0)); 3956 set_head (remainder, remainder_size | PREV_INUSE); 3957 set_foot (remainder, remainder_size); 3958 }
残りのサイズ( remainder_size )が最少チャンクサイズ(MINSIZE)以上なら、2 個のチャンクに分割します。
unsorted_chunks(av)→fd→bk == unsorted_chunks(av) かチェックして違うなら、エラーを投げます。
3959 check_malloced_chunk (av, victim, nb); 3960 void *p = chunk2mem (victim); 3961 alloc_perturb (p, bytes); 3962 return p; 3963 } 3964 }
alloc_perturb() をコールした後にポインターを返します。
3965 3966 /* 3967 Search for a chunk by scanning bins, starting with next largest 3968 bin. This search is strictly by best-fit; i.e., the smallest 3969 (with ties going to approximately the least recently used) chunk 3970 that fits is selected. 3971 3972 The bitmap avoids needing to check that most blocks are nonempty. 3973 The particular case of skipping all bins during warm-up phases 3974 when no chunks have been returned yet is faster than it might look. 3975 */ 3976 3977 ++idx; 3978 bin = bin_at (av, idx); 3979 block = idx2block (idx); 3980 map = av->binmap[block]; 3981 bit = idx2bit (idx);
次のビンを調べるためにインデックスをインクリメントします。
av→binmap を使って空のビンをスキップしていきます。
3982 3983 for (;; ) 3984 { 3985 /* Skip rest of block if there are no more set bits in this block. */ 3986 if (bit > map || bit == 0) 3987 { 3988 do 3989 { 3990 if (++block >= BINMAPSIZE) /* out of bins */ 3991 goto use_top; 3992 } 3993 while ((map = av->binmap[block]) == 0); 3994 3995 bin = bin_at (av, (block << BINMAPSHIFT)); 3996 bit = 1; 3997 } 3998
ビンマップのビットが該当する(ビンマップの)ブロックにおいて空であるなら use_top にスキップします。
3999 /* Advance to bin with set bit. There must be one. */ 4000 while ((bit & map) == 0) 4001 { 4002 bin = next_bin (bin); 4003 bit <<= 1; 4004 assert (bit != 0); 4005 }
ビットが設定されたビンを探索します。(一つはあるはず)
4006 4007 /* Inspect the bin. It is likely to be non-empty */ 4008 victim = last (bin); 4009
victim は現在のビンの最後を指させます。
victim は空でないビンの最後のチャンクを指しています。
4010 /* If a false alarm (empty bin), clear the bit. */ 4011 if (victim == bin) 4012 { 4013 av->binmap[block] = map &= ~bit; /* Write through */ 4014 bin = next_bin (bin); 4015 bit <<= 1; 4016 } 4017
ビンマップを使って空のビンをスキップします。
4018 else 4019 { 4020 size = chunksize (victim); 4021 4022 /* We know the first chunk in this bin is big enough to use. */ 4023 assert ((unsigned long) (size) >= (unsigned long) (nb)); 4024 4025 remainder_size = size - nb; 4026 4027 /* unlink */ 4028 unlink (av, victim, bck, fwd); 4029 4030 /* Exhaust */ 4031 if (remainder_size < MINSIZE) 4032 { 4033 set_inuse_bit_at_offset (victim, size); 4034 if (av != &main_arena) 4035 set_non_main_arena (victim); 4036 } 4037 4038 /* Split */ 4039 else 4040 { 4041 remainder = chunk_at_offset (victim, nb); 4042 4043 /* We cannot assume the unsorted list is empty and therefore 4044 have to perform a complete insert here. */ 4045 bck = unsorted_chunks (av); 4046 fwd = bck->fd; 4047 if (__glibc_unlikely (fwd->bk != bck)) 4048 malloc_printerr ("malloc(): corrupted unsorted chunks 2"); 4049 remainder->bk = bck; 4050 remainder->fd = fwd; 4051 bck->fd = remainder; 4052 fwd->bk = remainder; 4053 4054 /* advertise as last remainder */ 4055 if (in_smallbin_range (nb)) 4056 av->last_remainder = remainder; 4057 if (!in_smallbin_range (remainder_size)) 4058 { 4059 remainder->fd_nextsize = NULL; 4060 remainder->bk_nextsize = NULL; 4061 } 4062 set_head (victim, nb | PREV_INUSE | 4063 (av != &main_arena ? NON_MAIN_ARENA : 0)); 4064 set_head (remainder, remainder_size | PREV_INUSE); 4065 set_foot (remainder, remainder_size); 4066 } 4067 check_malloced_chunk (av, victim, nb); 4068 void *p = chunk2mem (victim); 4069 alloc_perturb (p, bytes); 4070 return p; 4071 } 4072 } 4073
2 つのチャンクに分割( Split )します。
そして残りのチャンクは未整理ビン( Unsorted Bin )に挿入します。
4074 use_top: 4075 /* 4076 If large enough, split off the chunk bordering the end of memory 4077 (held in av->top). Note that this is in accord with the best-fit 4078 search rule. In effect, av->top is treated as larger (and thus 4079 less well fitting) than any other available chunk since it can 4080 be extended to be as large as necessary (up to system 4081 limitations). 4082 4083 We require that av->top always exists (i.e., has size >= 4084 MINSIZE) after initialization, so if it would otherwise be 4085 exhausted by current request, it is replenished. (The main 4086 reason for ensuring it exists is that we may need MINSIZE space 4087 to put in fenceposts in sysmalloc.) 4088 */ 4089 4090 victim = av->top;
victim は av→top を指します。
4091 size = chunksize (victim); 4092 4093 if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) 4094 { 4095 remainder_size = size - nb; 4096 remainder = chunk_at_offset (victim, nb); 4097 av->top = remainder; 4098 set_head (victim, nb | PREV_INUSE | 4099 (av != &main_arena ? NON_MAIN_ARENA : 0)); 4100 set_head (remainder, remainder_size | PREV_INUSE); 4101 4102 check_malloced_chunk (av, victim, nb); 4103 void *p = chunk2mem (victim); 4104 alloc_perturb (p, bytes); 4105 return p; 4106 }
victim チャンクのサイズがリクエストサイズ + MINSIZE 以上ならば 2 つのチャンクに分割( Split )する。
片方は p として返し、残りのチャンクは新たなトップチャンクになります。
空のビンが見つからないなら、トップチャンク( Top Chunk )を使ってリクエストを処理してますね。
4107 4108 /* When we are using atomic ops to free fast chunks we can get 4109 here for all block sizes. */ 4110 else if (atomic_load_relaxed (&av->have_fastchunks)) 4111 { 4112 malloc_consolidate (av); 4113 /* restore original bin index */ 4114 if (in_smallbin_range (nb)) 4115 idx = smallbin_index (nb); 4116 else 4117 idx = largebin_index (nb); 4118 }
アリーナ av にファーストチャンクがあるかチェックします。
ある場合は malloc_consolidate(av) コールしてからループに戻ります。
4119 4120 /* 4121 Otherwise, relay to handle system-dependent cases 4122 */ 4123 else 4124 { 4125 void *p = sysmalloc (nb, av); 4126 if (p != NULL) 4127 alloc_perturb (p, bytes); 4128 return p; 4129 } 4130 } 4131 }
チャンクの分割や malloc_consolidate をしない場合は、sysmalloc() をコールしてメモリー領域を割り当てます。
Copyright 2018-2019, by Masaki Komatsu