做平面設(shè)計的一般瀏覽什么網(wǎng)站百度關(guān)鍵詞怎么做排名
介紹
在Linux中二進(jìn)制的程序從磁盤加載到內(nèi)存,運行起來后用戶態(tài)是使用pid來唯一標(biāo)識進(jìn)程,對于內(nèi)核都是以task_struct表示。二進(jìn)制程序中的數(shù)據(jù)段、代碼段、堆都能提現(xiàn)在task_struct中。每一個進(jìn)程都有自己的虛擬地址空間,虛擬地址空間包含幾種區(qū)域,具體參照如下
在內(nèi)核中進(jìn)程分配內(nèi)存時候并非立馬給定虛擬內(nèi)存對應(yīng)的物理內(nèi)存,而是分配虛擬內(nèi)存的使用權(quán)。只有當(dāng)進(jìn)程真正訪問申請的虛擬內(nèi)存才會分配物理頁幀并建立頁表映射。就如下面的代碼malloc僅僅是在當(dāng)前的進(jìn)程的地址空間內(nèi)分配虛擬內(nèi)存的使用權(quán),分配物理頁幀是在memset函數(shù)訪問虛擬內(nèi)存的時候。
void *ptr=malloc(sizeof(int));
memset(ptr,0,sizeof(int));
進(jìn)程虛擬地址空間
之前聊過task_struct用來表示內(nèi)核中的進(jìn)程或者線程,在task_struct中有一個進(jìn)程內(nèi)存空間的描述符,用來描述進(jìn)程的內(nèi)部虛擬空間布局。這個結(jié)構(gòu)非常大,我們會從task_struct->mm_struct->vm_area_struct從上往下的順序簡單介紹下
// 內(nèi)核中用來表示進(jìn)程或者線程的數(shù)據(jù)結(jié)構(gòu)
struct task_struct {// 進(jìn)程的內(nèi)存空間描述符struct mm_struct *active_mm;
};// 進(jìn)程的虛擬內(nèi)存空間描述符號
struct mm_struct {struct {// 進(jìn)程是使用的所有虛擬內(nèi)存的鏈表struct vm_area_struct *mmap; /* list of VMAs */// 鏈表中的節(jié)點組成的紅黑樹struct rb_root mm_rb;// 當(dāng)前進(jìn)程最大的虛擬地址空間大小unsigned long task_size; /* size of task vm space */// 頁表的物理地址pgd_t * pgd;// 二進(jìn)制代碼的虛擬內(nèi)存區(qū)域是從start_code到end_code來表示// 初始化區(qū)域虛擬內(nèi)存用start_data和end_data來表示unsigned long start_code, end_code, start_data, end_data;// 動態(tài)變化的堆虛擬內(nèi)存區(qū)域是從start_brk到brkunsigned long start_brk, brk, start_stack;// 參數(shù)列表的虛擬內(nèi)存區(qū)域是從arg_start到arg_end// 環(huán)境變量的虛擬內(nèi)促區(qū)域是從env_start到env_endunsigned long arg_start, arg_end, env_start, env_end;} __randomize_layout;
};// 用來表示各個虛擬內(nèi)存區(qū)域的結(jié)構(gòu)
struct vm_area_struct {// 虛擬內(nèi)存的起始地址unsigned long vm_start; /* Our start address within vm_mm. */// 虛擬內(nèi)存的結(jié)束地址unsigned long vm_end; /* The first byte after our end addresswithin vm_mm. */// 進(jìn)程所使用的各個虛擬內(nèi)存區(qū)域通過vm_prev和vm_next鏈表鏈接起來struct vm_area_struct *vm_next, *vm_prev;// 當(dāng)查找虛擬地址存在于哪個區(qū)域時鏈表性能顯然不行,通過vm_rb構(gòu)建的紅黑樹查找struct rb_node vm_rb;// 指向?qū)儆谀囊粋€mm_struct結(jié)構(gòu)用來表示從屬關(guān)系struct mm_struct *vm_mm; /* The address space we belong to. */// 內(nèi)存區(qū)域的標(biāo)記pgprot_t vm_page_prot;unsigned long vm_flags; /* Flags, see mm.h. */// vma的操作函數(shù)const struct vm_operations_struct *vm_ops;// 文件映射的偏移量unsigned long vm_pgoff; // 如果是文件映射vm_file則是表示對應(yīng)的文件指針struct file * vm_file; /* File we map to (can be NULL). */void * vm_private_data; /* was vm_pte (shared mem) */} __randomize_layout;
相關(guān)視頻推薦
2024,徹底搞懂計算機的底層原理,linux內(nèi)核源碼分析教程,六大模塊全面分析(內(nèi)存管理、進(jìn)程管理、設(shè)備驅(qū)動、網(wǎng)絡(luò)協(xié)議棧、文件系統(tǒng)、中斷管理及基礎(chǔ))https://www.bilibili.com/video/BV1GT4y1t7Hs/
免費學(xué)習(xí)地址:Linux C/C++開發(fā)(后端/音視頻/游戲/嵌入式/高性能網(wǎng)絡(luò)/存儲/基礎(chǔ)架構(gòu)/安全)
需要C/C++ Linux服務(wù)器架構(gòu)師學(xué)習(xí)資料加qun579733396獲取(資料包括C/C++,Linux,golang技術(shù),Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協(xié)程,DPDK,ffmpeg等),免費分享
內(nèi)存操作
這里涉及到的是文件映射和堆內(nèi)存分配兩種的情況
文件映射
用戶態(tài)的文件映射是通過mmap系統(tǒng)調(diào)用進(jìn)行實現(xiàn),它可以繞靠文件系統(tǒng)的過程,利用內(nèi)存指針快速訪問文件數(shù)據(jù)。mmap新的系統(tǒng)調(diào)用對應(yīng)的是內(nèi)核中ksys_mmap_pgoff.
// mmap的系統(tǒng)調(diào)用的實現(xiàn),底層是調(diào)用ksys_mmap_pgoff的函數(shù)
SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len,unsigned long, prot, unsigned long, flags,unsigned long, fd, unsigned long, off)
{if (off & ~PAGE_MASK)return -EINVAL;return ksys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
}
// ksys_mmap_pgoff的具體定義如下
unsigned long ksys_mmap_pgoff(unsigned long addr, unsigned long len,unsigned long prot, unsigned long flags,unsigned long fd, unsigned long pgoff)
{struct file *file = NULL;unsigned long retval;// 匿名文件映射,設(shè)置映射的文件if (!(flags & MAP_ANONYMOUS)) {audit_mmap_fd(fd, flags);file = fget(fd);// 大頁方式} else if (flags & MAP_HUGETLB) {struct ucounts *ucounts = NULL;struct hstate *hs;hs = hstate_sizelog((flags >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK);file = hugetlb_file_setup(HUGETLB_ANON_FILE, len,VM_NORESERVE,&ucounts, HUGETLB_ANONHUGE_INODE,(flags >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK);}// 最核心的映射函數(shù)實現(xiàn)retval = vm_mmap_pgoff(file, addr, len, prot, flags, pgoff);return retval;
}// 調(diào)用底層的do_mmap函數(shù)實現(xiàn)映射
unsigned long vm_mmap_pgoff(struct file *file, unsigned long addr,unsigned long len, unsigned long prot,unsigned long flag, unsigned long pgoff)
{ret = do_mmap(file, addr, len, prot, flag, pgoff, &populate,&uf);return ret;
}// 最底層的文件映射的實現(xiàn)
unsigned long do_mmap(struct file *file, unsigned long addr,unsigned long len, unsigned long prot,unsigned long flags, unsigned long pgoff,unsigned long *populate, struct list_head *uf)
{struct mm_struct *mm = current->mm;vm_flags_t vm_flags;int pkey = 0;// 在線性區(qū)間找到未被使用并且足夠大的地址空間addr = get_unmapped_area(file, addr, len, pgoff, flags);// 傳入prot和flags設(shè)置vm_flagsvm_flags = calc_vm_prot_bits(prot, pkey) | calc_vm_flag_bits(flags) |mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;// 如果是文件映射,則通過find_inode找到inode并檢查文件if (file) {struct inode *inode = file_inode(file);}addr = mmap_region(file, addr, len, vm_flags, pgoff, uf){// 檢查虛擬地址空間容量限制if(!may_expand_vm(mm, vm_flags, len >> PAGE_SHIFT)){}// 檢查是否有當(dāng)前的vma有重疊如果有則進(jìn)行munmap操作munmap_vma_range(mm, addr, len, &prev, &rb_link, &rb_parent, uf);// 與現(xiàn)有的vma進(jìn)行合并vma = vma_merge(mm, prev, addr, addr + len, vm_flags,NULL, file, pgoff, NULL, NULL_VM_UFFD_CTX);if (vma) {goto out; }// 申請新的vm_area_struct結(jié)構(gòu)vma = vm_area_alloc(mm);// 將新的vm_area_struct插入到mm_struct中的鏈表、紅黑樹以及對應(yīng)文件的地址空間上的adress_space->i_mmap或者address_space->i_mmap_nolinear中vma_link(mm, vma, prev, rb_link, rb_parent);}return addr;
}
堆內(nèi)存
在用戶態(tài)申請內(nèi)存和釋放內(nèi)存通過malloc/free庫函數(shù)進(jìn)行,它們的底層還是通過SYSCALL_DEFINE1(brk, unsigned long, brk)系統(tǒng)調(diào)用來完成。堆內(nèi)存的擴(kuò)大可以通過SYSCALL_DEFINE1(brk, unsigned long, brk)進(jìn)行,如果需要縮小的空間則通過do_munmap實現(xiàn)。如果分配空間大于128KB(glibc源碼中定義的MMAP_THRESHOLD),malloc使用sys_mmap2實現(xiàn)內(nèi)存申請。不論是malloc還是calloc申請的是線性虛擬地址而非物理地址,連續(xù)的空間也是指的虛擬地址空間的連續(xù)。
// brk系統(tǒng)調(diào)用的實現(xiàn)
SYSCALL_DEFINE1(brk, unsigned long, brk)
{origbrk = mm->brk;// 檢查資源的限制if (check_data_rlimit(rlimit(RLIMIT_DATA), brk, mm->start_brk,mm->end_data, mm->start_data))goto out;// page的對齊newbrk = PAGE_ALIGN(brk);oldbrk = PAGE_ALIGN(mm->brk);if (oldbrk == newbrk) {mm->brk = brk;goto success;}// 如果是是釋放操作則執(zhí)行__do_munmap調(diào)整指針的位置if (brk <= mm->brk) {int ret;mm->brk = brk;ret = __do_munmap(mm, newbrk, oldbrk-newbrk, &uf, true);goto success;}// 對應(yīng)malloc的實現(xiàn),申請新的vm_area_struct、插入到mm_struct中的list和rb樹中// do_b rk_flags可以理解是mmap簡單版的實現(xiàn)if (do_brk_flags(oldbrk, newbrk-oldbrk, 0, &uf) < 0)goto out;mm->brk = brk;success:populate = newbrk > oldbrk && (mm->def_flags & VM_LOCKED) != 0;if (downgraded)mmap_read_unlock(mm);elsemmap_write_unlock(mm);userfaultfd_unmap_complete(mm, &uf);if (populate)mm_populate(oldbrk, newbrk - oldbrk);return brk;out:mmap_write_unlock(mm);return origbrk;
}