Pemacu Linux dijalankan dalam ruang "kernel". Secara amnya, pemacu memanggil kmalloc() untuk memperuntukkan memori untuk struktur data, memanggil vmalloc() untuk memperuntukkan struktur data bagi kawasan swap aktif, memperuntukkan penimbal untuk sesetengah pemacu I/O, atau memperuntukkan ruang untuk modul kmalloc dan vmalloc memperuntukkan memori kernel.
Persekitaran pengendalian tutorial ini: sistem linux7.3, komputer Dell G3.
Pemacu Linux dijalankan dalam ruang "kernel".
Untuk program mikropengawal yang ditulis secara amnya, aplikasi dan pemacu selalunya bercampur pengaturcara Mikropengawal dengan tahap keupayaan tertentu boleh merealisasikan lapisan aplikasi dan pemacu. Dalam sistem Linux, aplikasi dan pemacu telah dipaksa untuk berlapis.
Dalam atur cara pengawal mikro, aplikasi boleh terus mengendalikan daftar asas. Walau bagaimanapun, tingkah laku seperti itu dilarang dalam sistem Linux Contohnya: penulis aplikasi Linux dengan sengaja memanggil pemacu pengurusan kuasa dalam aplikasi dan menutup sistem itu.
Aplikasi Linux khusus memanggil pemacu seperti yang ditunjukkan dalam rajah:
Aplikasi berjalan dalam ruang pengguna, dan pemacu berjalan dalam ruang kernel. Jika aplikasi dalam ruang pengguna ingin mengendalikan kernel, ia mesti menggunakan kaedah "panggilan sistem" untuk memasuki ruang kernel dari ruang pengguna dan mengendalikan lapisan asas.
Kernel juga merupakan program dan juga harus mempunyai ruang memori mayanya sendiri Namun, sebagai program yang menyediakan program pengguna, kernel ruang mempunyai ciri-ciri tersendiri.
Hubungan antara ruang kernel dan ruang pengguna
Dalam sistem 32-bit, ruang maya maksimum program boleh menjadi 4GB, maka maksimum Pendekatan mudah ialah menganggap kernel sebagai program supaya ia mempunyai ruang 4GB yang sama seperti program lain. Walau bagaimanapun, pendekatan ini akan menyebabkan sistem menukar jadual halaman program pengguna dan jadual halaman kernel secara berterusan, sekali gus menjejaskan kecekapan komputer. Cara terbaik untuk menyelesaikan masalah ini ialah dengan membahagikan ruang 4GB kepada dua bahagian: satu bahagian adalah ruang pengguna dan bahagian lain adalah ruang kernel Ini boleh memastikan ruang kernel tetap dan tidak berubah, dan apabila program bertukar , hanya jadual halaman program. Satu-satunya kelemahan pendekatan ini ialah ruang kernel dan ruang pengguna menjadi lebih kecil.
Contohnya: Pada platform perkakasan 32-bit seperti i386, Linux mentakrifkan PAGE_OFFSET malar dalam halaman fail.h:
#ifdef CONFIG_MMU #define __PAGE_OFFSET (0xC0000000) //0xC0000000为3GB #else #define __PAGE_OFFSET (0x00000000) #endif #define PAGE_OFFSET ((unsigned long)__PAGE_OFFSET)
Linux menggunakan PAGE_OFFSET Ruang memori maya 4GB dibahagikan kepada dua bahagian: ruang alamat rendah pada alamat 0~3G-1 ialah ruang pengguna, dengan saiz 3GB ruang alamat tinggi pada alamat 3GB~4GB-1 ialah ruang kernel, dengan saiz; sebanyak 1GB.
Apabila berbilang atur cara dijalankan dalam sistem, hubungan antara berbilang ruang pengguna dan ruang kernel boleh dinyatakan seperti berikut:
Seperti yang ditunjukkan dalam rajah Seperti yang ditunjukkan, program 1, 2...n berkongsi ruang kernel. Sudah tentu, perkongsian di sini merujuk kepada perkongsian masa, kerana pada bila-bila masa, untuk sistem pemproses teras tunggal, hanya satu program boleh dijalankan.
Susun atur keseluruhan ruang kernel
Dalam proses pembangunan Linux, dengan kemas kini peralatan perkakasan dan peningkatan tahap teknikal , ruang kernelnya Pembangunan susun atur juga merupakan proses tampalan berterusan. Akibat daripada ini ialah ruang kernel dibahagikan kepada beberapa kawasan yang berbeza, dan kawasan yang berbeza mempunyai kaedah pemetaan yang berbeza. Biasanya orang beranggapan ruang kernel Linux mempunyai tiga kawasan iaitu kawasan DMA (ZONE_DMA), kawasan normal (ZONE_NORMAL) dan kawasan memori high-end (ZONE_HIGHMEM).
Memori fizikal sebenar yang dikonfigurasikan dalam komputer awal biasanya hanya beberapa MB, jadi Untuk meningkatkan kelajuan kernel mengakses memori alamat fizikal melalui alamat maya, alamat maya dan alamat memori fizikal dalam ruang kernel menggunakan kaedah pemetaan tetap surat-menyurat satu-satu dari rendah alamat ke alamat tinggi Seperti yang ditunjukkan dalam rajah di bawah Paparan:
Seperti yang anda lihat, Kaedah pemetaan tetap ini menjadikan hubungan antara alamat maya dan alamat fizikal sangat mudah, iaitu alamat maya kernel dan alamat fizikal sebenar Terdapat hanya offset berangka tetap PAGE_OFFSET, jadi apabila kernel menggunakan alamat maya untuk mengakses bingkai halaman fizikal, ia hanya perlu menolak PAGE_OFFSET daripada maya alamat untuk mendapatkan alamat fizikal sebenar, yang lebih cepat daripada menggunakan jadual halaman!
Memandangkan pendekatan ini hampir secara langsung menggunakan alamat fizikal, ruang kernel jenis ini berdasarkan kaedah pemetaan tetap juga dipanggil "ruang ingatan fizikal", atau singkatannya ingatan fizikal. Selain itu, Memandangkan kaedah pemetaan tetap ialah pemetaan linear, kawasan ini juga dipanggil kawasan pemetaan linear.
Sudah tentu, dalam kes ini (apabila memori fizikal sebenar komputer kecil), ruang pemetaan tetap kernel hanya menduduki sebahagian daripada keseluruhan ruang kernel 1GB. Contohnya: Apabila mengkonfigurasi sistem komputer x86 dengan 32MB memori fizikal sebenar, kawasan pemetaan tetap kernel ialah ruang 32MB PAGE_OFFSET~ (PAGE_OFFSET 0x02000000). Jadi apa yang perlu dilakukan dengan ruang maya kernel yang tinggal dalam ruang kernel?
Sudah tentu ingatan fizikal masih digunakan dalam pemetaan bukan linear jadual halaman mengikut pengurusan ruang maya biasa. Khususnya, kawasan pemetaan tetap dialih keluar daripada keseluruhan ruang kernel 1GB, dan kemudian kawasan pengasingan 8MB pada permulaan dialih keluar daripada bahagian yang tinggal Selebihnya ialah kawasan pemetaan memori maya biasa yang dipetakan dengan cara yang sama seperti ruang pengguna . Di kawasan ini, bukan sahaja tiada hubungan pemetaan tetap antara alamat maya dan alamat fizikal, tetapi juga memori dinamik diperoleh dengan memanggil fungsi kernel vmalloc(), jadi kawasan ini dipanggil kawasan peruntukan vmalloc, seperti yang ditunjukkan dalam rajah di bawah:
Untuk sistem komputer x86 yang dikonfigurasikan dengan 32MB memori fizikal sebenar, kedudukan permulaan kawasan peruntukan vmalloc ialah PAGE_OFFSET 0x02000000 0x00800000.
Biar saya terangkan di sini: Pemetaan tetap antara ruang kernel dan bingkai halaman fizikal yang disebut di sini pada dasarnya adalah "tempahan" halaman kernel kepada bingkai halaman fizikal halaman "menempati" bingkai halaman fizikal ” bingkai halaman fizikal ini. Iaitu, halaman maya terikat pada bingkai halaman fizikal hanya apabila halaman maya sebenarnya perlu mengakses bingkai halaman fizikal. Biasanya, apabila bingkai halaman fizikal tidak digunakan oleh halaman maya yang sepadan, bingkai halaman boleh digunakan oleh ruang pengguna dan kawasan peruntukan kernel kmalloc yang diperkenalkan kemudian.
Ringkasnya, dalam sistem dengan memori fizikal sebenar yang kecil, saiz memori sebenar ialah sempadan antara kawasan memori fizikal ruang kernel dan kawasan peruntukan vmalloc.
Untuk keseluruhan ruang kernel 1GB, orang ramai juga memanggil 16MB di kepala ruang Zon DMA, iaitu zon ZONE_DMA, kerana pada masa lalu perkakasan tetapkan ruang DMA dalam ruang memori fizikal 16MB yang lebih rendah; zon yang selebihnya dipanggil zon biasa, iaitu ZONE_NORMAL.
Memori tinggi ruang kernel
Dengan perkembangan teknologi komputer, ingatan fizikal sebenar komputer menjadi semakin besar, sehingga kawasan pemetaan tetap kernel (kawasan linear) menjadi lebih besar dan lebih besar. Jelas sekali, jika tiada sekatan dikenakan, apabila memori fizikal sebenar mencapai 1GB, kawasan peruntukan vmalloc (kawasan bukan linear) akan tidak lagi wujud. Oleh itu, kod kernel yang dibangunkan sebelum ini yang dipanggil vmalloc() tidak lagi tersedia. Jelas sekali, untuk menjadi serasi dengan kod kernel awal, ini tidak dibenarkan.
Rajah berikut menunjukkan situasi yang dihadapi oleh ruang kernel ini:
Jelas sekali, sebab bagi masalah di atas ialah situasi sebenar adalah tidak dijangka Memori fizikal boleh melebihi 1GB, jadi tiada had ditetapkan pada sempadan kawasan pemetaan tetap kernel, yang dibenarkan berkembang dengan memori fizikal sebenar.
Cara untuk menyelesaikan masalah di atas ialah: Hadkan had atas kawasan pemetaan tetap ruang kernel supaya ia tidak boleh meningkat sewenang-wenangnya dengan peningkatan memori fizikal. Linux menetapkan bahawa nilai sempadan atas kawasan pemetaan kernel tidak boleh lebih besar daripada high_menory yang berterusan iaitu kurang daripada 1G Apabila memori fizikal sebenar adalah besar, high_memory 3G digunakan sebagai sempadan untuk menentukan kawasan memori fizikal.
Contohnya: untuk sistem x86, nilai high_memory ialah 896M, jadi baki 128MB ruang kernel 1GB ialah kawasan pemetaan bukan linear. Ini memastikan bahawa dalam apa jua keadaan, kernel mempunyai kawasan pemetaan bukan linear yang mencukupi untuk serasi dengan kod awal dan boleh mengakses lebih daripada 1GB memori fizikal sebenar dalam cara memori maya biasa.
Dalam erti kata lain, idea paling asas bagi memori mewah: pinjam bahagian ruang alamat, wujudkan pemetaan alamat sementara dan lepaskannya apabila ruang alamat ini dicapai , ia boleh dikitar semula dan mengakses semua memori fizikal. Apabila komputer mempunyai memori fizikal yang besar, gambarajah skematik ruang kernel adalah seperti berikut:
Adalah kebiasaan untuk Linux memanggil bahagian kernel ini angkasa 3G high_memory~4G-1 High-end memory zone (ZONE_HIGHMEM).
Untuk meringkaskan: Dalam ruang kernel struktur x86, tiga jenis kawasan (dikira daripada 3G) adalah seperti berikut:
Menurut matlamat aplikasi yang berbeza, memori high-end dibahagikan kepada kawasan vmalloc, kawasan pemetaan berterusan dan sementara kawasan pemetaan. Susun atur memori mewah dalam ruang kernel adalah seperti yang ditunjukkan di bawah:
Kawasan pemetaan vmalloc ialah bahagian utama memori mewah Terdapat kawasan pengasingan 8MB antara kepala selang dan ruang pemetaan linear kernel, dan kawasan pengasingan 4KB antara ekor dan berterusan seterusnya. kawasan pemetaan.
Kaedah pemetaan kawasan pemetaan vmalloc adalah sama seperti ruang pengguna Kernel boleh mendapatkan memori di kawasan ini dengan memanggil fungsi vmalloc(). Fungsi fungsi ini adalah bersamaan dengan malloc() dalam ruang pengguna Ruang memori yang disediakan adalah berterusan di alamat maya (perhatikan bahawa alamat fizikal tidak dijamin berterusan).
Jika halaman yang sepadan dengan memori high-end diperoleh melalui alloc_page(), bagaimana untuk mencarinya?
Inti secara khas mengetepikan ruang linear untuk tujuan ini, bermula dari PKMAP_BASE, yang digunakan untuk memetakan memori mewah, iaitu kawasan pemetaan kernel yang berterusan.
Dalam kawasan pemetaan kernel yang berterusan, anda boleh mewujudkan pemetaan jangka panjang antara bingkai halaman fizikal dan halaman maya kernel dengan memanggil fungsi kmap(). Ruang ini biasanya 4MB dan boleh memetakan sehingga 1024 bingkai halaman. Oleh itu, untuk meningkatkan pusing ganti bingkai halaman, fungsi kunmap() harus dipanggil tepat pada masanya untuk melepaskan bingkai halaman fizikal. tidak lagi digunakan.
Kawasan pemetaan sementara juga dipanggil kawasan pemetaan tetap dan kawasan simpanan. Kawasan ini digunakan terutamanya dalam sistem berbilang pemproses Kerana ruang memori yang diperoleh di kawasan ini tidak dilindungi, memori yang diperoleh mesti digunakan dalam masa jika tidak, apabila terdapat permintaan baharu, kandungan pada bingkai halaman akan ditimpa . , jadi kawasan ini dipanggil kawasan pemetaan sementara.
Artikel yang sangat bagus tentang kawasan memori mewah: Ruang pengguna Linux dan penjelasan terperinci ruang kernel bagi memori mewah.
Pengubah suai peruntukan memori kernel gfp
Untuk membuat penerangan yang diperlukan tentang permintaan dalam fungsi permintaan memori kernel, Linux mentakrifkan gfp pengubah peruntukan memori berbilang A. Ia adalah pengubah kelakuan, pengubah kawasan dan pengubah jenis.
Pengubah suai tingkah laku dalam fungsi peruntukan memori menerangkan cara kernel harus memperuntukkan memori. Pengubah suai tingkah laku utama adalah seperti berikut:
修饰符 | 说明 |
__GFP_WAIT | 分配器可以休眠 |
__GFP_HIGH | 分配器可以访问紧急事件缓冲池 |
__GFP_IO | 分配器可以启动磁盘IO |
__GFP_FS | 分配器可以启动文件系统IO |
__GFP_COLD | 分配器应该使用高速缓冲中快要淘汰的页框 |
__GFP_NOWARN | 分配器不发出警告 |
__GFP_REPEAT | 分配失败时重新分配 |
__GFP_NOFAILT | 分配失败时重新分配,直至成功 |
__GFP_NORETRY | 分配失败时不再重新分配 |
Pengubah suai wilayah menunjukkan kawasan mana ruang kernel yang diperlukan untuk dibaca Peruntukkan memori dalam . Secara lalai, pengalokasi memori bermula dari ZONE_NORMAL dalam ruang kernel dan secara beransur-ansur memperuntukkan kawasan memori kepada peminta memori Jika pengguna secara khusus perlu mendapatkan memori daripada ZONE_DMA atau ZONE_HOGNMEM, maka peminta memori perlu menggunakan dua kaedah berikut. dalam fungsi permintaan memori
类型修饰符实质上是上述所述修饰符的联合应用。也就是:将上述的某些行为修饰符和区修饰符,用“|”进行连接并另外取名的修饰符。这里就不多介绍了。
内核常用内存分配及地址映射函数
函数vmalloc()在vmalloc分配区分配内存,可获得虚拟地址连续,但并不保证其物理页框连续的较大内存。与物理空间的内存分配函数malloc()有所区别,vmalloc()分配的物理页不会被交换出去。函数vmalloc()的原型如下:
void *vmalloc(unsigned long size) { return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL); }
void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot) { return kmalloc(size, (gfp_mask | __GFP_COMP) & ~__GFP_HIGHMEM); }
其中,参数size为所请求内存的大小,返回值为所获得内存虚拟地址指针。
与vmalloc()配套的释放函数如下:
void vfree(const void *addr) { kfree(addr); }
其中,参数addr为待释放内存指针。
kmalloc()是内核另一个常用的内核分配函数,它可以分配一段未清零的连续物理内存页,返回值为直接映射地址。由kmalloc()可分配的内存最大不能超过32页。其优点是分配速度快,缺点是不能分配大于128KB的内存页(出于跨平台考虑)。
在linux/slab.h文件中,该函数的原型声明如下:
static __always_inline void *kmalloc(size_t size, gfp_t flags) { struct kmem_cache *cachep; void *ret; if (__builtin_constant_p(size)) { int i = 0; if (!size) return ZERO_SIZE_PTR; #define CACHE(x) \ if (size <= x) \ goto found; \ else \ i++; #include <linux/kmalloc_sizes.h> #undef CACHE return NULL; found: #ifdef CONFIG_ZONE_DMA if (flags & GFP_DMA) cachep = malloc_sizes[i].cs_dmacachep; else #endif cachep = malloc_sizes[i].cs_cachep; ret = kmem_cache_alloc_notrace(cachep, flags); trace_kmalloc(_THIS_IP_, ret, size, slab_buffer_size(cachep), flags); return ret; } return __kmalloc(size, flags); }
其中,参数size为以字节为单位表示的所申请空间的大小;参数flags决定了所分配的内存适合什么场合。
与函数kmalloc()对应的释放函数如下:
void kfree(const void *objp) { struct kmem_cache *c; unsigned long flags; trace_kfree(_RET_IP_, objp); if (unlikely(ZERO_OR_NULL_PTR(objp))) return; local_irq_save(flags); kfree_debugcheck(objp); c = virt_to_cache(objp); debug_check_no_locks_freed(objp, obj_size(c)); debug_check_no_obj_freed(objp, obj_size(c)); __cache_free(c, (void *)objp); local_irq_restore(flags); }
小结一下,kmalloc、vmalloc、malloc的区别:
也就是说:kmalloc、vmalloc这两个函数所分配的内存都处于内核空间,即从3GB~4GB;但位置不同,kmalloc()分配的内存处于3GB~high_memory(ZONE_DMA、ZONE_NORMAL)之间,而vmalloc()分配的内存在VMALLOC_START~4GB(ZONE_HIGHMEM)之间,也就是非连续内存区。一般情况下在驱动程序中都是调用kmalloc()来给数据结构分配内存,而vmalloc()用在为活动的交换区分配数据结构,为某些I/O驱动程序分配缓冲区,或为模块分配空间。
可参考文章:Kmalloc和Vmalloc的区别。
与上述在虚拟空间分配内存的函数不同,alloc_pages()是在物理内存空间分配物理页框的函数,其原型如下:
static inline struct page * alloc_pages(gfp_t gfp_mask, unsigned int order) { if (unlikely(order >= MAX_ORDER)) return NULL; return alloc_pages_current(gfp_mask, order); }
其中,参数order表示所分配页框的数目,该数目为2^order。order的最大值由include/Linux/Mmzone.h文件中的宏MAX_ORDER决定。参数gfp_mask为说明内存页框分配方式及使用场合。
函数返回值为页框块的第一个页框page结构的地址。
调用下列函数可以获得页框的虚拟地址:
void *page_address(struct page *page) { unsigned long flags; void *ret; struct page_address_slot *pas; if (!PageHighMem(page)) return lowmem_page_address(page); pas = page_slot(page); ret = NULL; spin_lock_irqsave(&pas->lock, flags); if (!list_empty(&pas->lh)) { struct page_address_map *pam; list_for_each_entry(pam, &pas->lh, list) { if (pam->page == page) { ret = pam->virtual; goto done; } } } done: spin_unlock_irqrestore(&pas->lock, flags); return ret; }
使用函数alloc_pages()获得的内存应该使用下面的函数释放:
void __free_pages(struct page *page, unsigned int order) { if (put_page_testzero(page)) { if (order == 0) free_hot_page(page); else __free_pages_ok(page, order); } }
kmap()是一个映射函数,它可以将一个物理页框映射到内核空间的可持久映射区。这种映射类似于内核ZONE_NORMAL的固定映射,但虚拟地址与物理地址的偏移不一定是PAGE_OFFSET。由于内核可持久映射区的容量有限(总共只有4MB),因此当内存使用完毕后,应该立即释放。
函数kmap()的函数原型如下:
void *kmap(struct page *page) { might_sleep(); if (!PageHighMem(page)) return page_address(page); return kmap_high(page); }
小结
由于CPU的地址总线只有32位, 32的地址总线无论是从逻辑上还是从物理上都只能描述4G的地址空间(232=4Gbit),在物理上理论上最多拥有4G内存(除了IO地址空间,实际内存容量小于4G),逻辑空间也只能描述4G的线性地址空间。
为了合理的利用逻辑4G空间,Linux采用了3:1的策略,即内核占用1G的线性地址空间,用户占用3G的线性地址空间。所以用户进程的地址范围从0~3G,内核地址范围从3G~4G,也就是说,内核空间只有1G的逻辑线性地址空间。
Jika memori fizikal Linux kurang daripada ruang 1G, kernel biasanya memetakan memori fizikal secara linear ke ruang alamatnya, iaitu, pemetaan satu sama satu, yang boleh meningkatkan kelajuan akses. Walau bagaimanapun, apabila memori fizikal Linux melebihi 1G, mekanisme akses linear tidak mencukupi, kerana hanya 1G memori boleh dipetakan, dan memori fizikal yang tinggal tidak dapat diuruskan oleh kernel Oleh itu, untuk menyelesaikan masalah ini, Linux Alamat kernel dibahagikan kepada dua bahagian, kawasan linear dan kawasan bukan linear Luas linear ditetapkan sebagai maksimum 896M, dan baki 128M adalah kawasan bukan linear. Oleh itu, ingatan fizikal yang dipetakan oleh kawasan linear menjadi memori akhir rendah, dan ingatan fizikal yang selebihnya dipanggil memori akhir tinggi. Tidak seperti kawasan linear, kawasan bukan linear tidak dipetakan memori lebih awal daripada masa, tetapi dipetakan secara dinamik apabila digunakan.
Memori rendah terbahagi kepada dua bahagian: ZONE_DMA: 16MB pada permulaan ruang kernel, ZONE_NORMAL: 16MB~896MB ruang kernel (pemetaan tetap). Selebihnya ialah memori mewah: ZONE_HIGHMEM: ruang kernel 896MB ~ hujung (1G).
Menurut matlamat aplikasi yang berbeza, memori mewah dibahagikan kepada tiga bahagian: kawasan vmalloc, kawasan pemetaan berterusan dan kawasan pemetaan sementara. Kawasan vmalloc diperuntukkan menggunakan fungsi vmalloc(); kawasan pemetaan berterusan menggunakan allc_pages() untuk mendapatkan halaman yang sepadan, dan dipetakan secara langsung menggunakan fungsi kmap() kawasan pemetaan sementara biasanya digunakan untuk keperluan khas.
Ruang kernel (3G~4G) |
|
Kawasan pemetaan sementara | |||||||||||
Kawasan pemetaan berterusan | |||||||||||||
kawasan vmalloc | |||||||||||||
Memori akhir rendah (3G~3G high_memory-1)Pemetaan linear kawasan (Kawasan pemetaan tetap) | ZONE_NORMAL | ||||||||||||
ZONE_DMA | |||||||||||||
Ruang pengguna (0 ~ 3G-1) | Direktori halaman-->Direktori halaman perantaraan-->Jadual halaman |
Atas ialah kandungan terperinci Dalam ruang manakah pemacu linux dijalankan?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!