Rumah  >  Artikel  >  Tutorial sistem  >  Fahami strategi peruntukan memori Linux dalam satu artikel

Fahami strategi peruntukan memori Linux dalam satu artikel

王林
王林ke hadapan
2024-02-12 11:57:02900semak imbas

Apakah rupa pengedaran memori proses Linux?

Dalam sistem pengendalian Linux, bahagian dalam ruang alamat maya dibahagikan kepada dua bahagian: ruang kernel dan ruang pengguna Sistem dengan digit yang berbeza mempunyai julat ruang alamat yang berbeza. Sebagai contoh, sistem 32-bit dan 64-bit yang paling biasa adalah seperti berikut:

一文读懂 Linux 内存分配策略

Anda boleh lihat di sini:

  • Ruang kernel sistem 32-bit menduduki 1G, yang berada di bahagian atas, dan baki 3G adalah ruang pengguna
  • Ruang kernel dan ruang pengguna sistem 64-bit adalah kedua-duanya 128T, masing-masing menduduki bahagian tertinggi dan terendah keseluruhan ruang memori, dan bahagian tengah yang tinggal tidak ditentukan.

Mari kita bincangkan tentang perbezaan antara ruang kernel dan ruang pengguna:

  • Apabila proses berada dalam mod pengguna, ia hanya boleh mengakses memori ruang pengguna
  • Hanya selepas memasuki keadaan kernel anda boleh mengakses memori dalam ruang kernel

Walaupun setiap proses mempunyai memori maya bebasnya sendiri, Alamat kernel dalam setiap memori maya sebenarnya dikaitkan dengan memori fizikal yang sama. Dengan cara ini, selepas proses bertukar kepada keadaan kernel, ia boleh mengakses memori ruang kernel dengan mudah.

一文读懂 Linux 内存分配策略

Seterusnya, mari kita ketahui lebih lanjut tentang pembahagian ruang maya dan ruang kernel dibahagikan dengan cara yang berbeza.

Mari kita lihat pengagihan ruang pengguna dengan mengambil sistem 32-bit sebagai contoh, saya melukis gambar untuk mewakili hubungan mereka:

.

Anda boleh lihat dari gambar ini bahawa memori ruang pengguna dibahagikan kepada 6 segmen memori berbeza dari rendah ke tinggi:

一文读懂 Linux 内存分配策略
  • Segmen fail program, termasuk kod boleh laku binari
  • Segmen data yang dimulakan, termasuk pemalar statik
  • Segmen data yang tidak dimulakan, termasuk pembolehubah statik yang tidak dimulakan
  • Segmen timbunan, termasuk memori yang diperuntukkan secara dinamik, bermula dari alamat rendah dan berkembang ke atas
  • Segmen pemetaan fail, termasuk perpustakaan dinamik, memori kongsi, dll., bermula dari alamat rendah dan berkembang ke atas (bergantung pada versi perkakasan dan kernel
  • ).
  • Segmen tindanan, termasuk pembolehubah tempatan dan konteks panggilan fungsi, dsb. Saiz tindanan ditetapkan, biasanya 8 MB. Sudah tentu, sistem juga menyediakan parameter supaya kita boleh menyesuaikan saiz

Di antara 6 segmen memori ini, memori segmen pemetaan timbunan dan fail diperuntukkan secara dinamik. Contohnya, menggunakan malloc() atau mmap() pustaka standard C, anda boleh memperuntukkan memori secara dinamik dalam segmen pemetaan timbunan dan fail masing-masing.

Bagaimanakah malloc memperuntukkan memori?

Sebenarnya, malloc() bukan panggilan sistem, tetapi fungsi dalam perpustakaan C, digunakan untuk memperuntukkan memori secara dinamik.

Apabila malloc menggunakan memori, terdapat dua cara untuk memohon memori timbunan daripada sistem pengendalian.

  • Kaedah 1: Peruntukkan memori daripada timbunan melalui panggilan sistem brk()
  • Kaedah 2: Peruntukkan memori dalam kawasan pemetaan fail melalui panggilan sistem mmap()

Pelaksanaan kaedah satu sangat mudah, iaitu menggunakan fungsi brk() untuk mengalihkan penunjuk "top of heap" ke alamat tinggi untuk mendapatkan ruang ingatan baharu. Seperti yang ditunjukkan di bawah:

一文读懂 Linux 内存分配策略

Kaedah 2 menggunakan kaedah "pemetaan tanpa nama persendirian" dalam panggilan sistem mmap() untuk memperuntukkan sekeping memori dalam kawasan pemetaan fail, iaitu untuk "mencuri" sekeping memori daripada kawasan pemetaan fail. Seperti yang ditunjukkan di bawah:

一文读懂 Linux 内存分配策略

"

Dalam keadaan apakah malloc() memperuntukkan memori melalui brk()? Dalam senario apakah memori diperuntukkan melalui mmap()?

malloc() mempunyai ambang yang ditentukan secara lalai dalam kod sumber:

  • Jika memori yang diperuntukkan oleh pengguna kurang daripada 128 KB, mohon memori melalui brk(
  • Jika memori yang diperuntukkan oleh pengguna lebih besar daripada 128 KB, mohon memori melalui mmap(

Perhatikan bahawa versi glibc yang berbeza mentakrifkan ambang yang berbeza.

malloc() memperuntukkan ingatan fizikal?

Tidak, malloc() memperuntukkan memori maya.

Jika memori maya yang diperuntukkan tidak diakses, memori maya tidak akan dipetakan ke memori fizikal, jadi ia tidak akan menduduki memori fizikal.

Hanya apabila mengakses ruang alamat maya yang diperuntukkan, sistem pengendalian mencari jadual halaman dan mendapati halaman yang sepadan dengan memori maya tidak berada dalam memori fizikal Ia akan mencetuskan gangguan halaman, dan kemudian sistem pengendalian akan mewujudkan a pautan antara ingatan maya dan hubungan pemetaan fizikal antara.

Berapa banyak memori maya yang akan diperuntukkan oleh malloc(1)?

Apabila

malloc() memperuntukkan memori, ia tidak memperuntukkan ruang memori mengikut bilangan bait yang dijangkakan oleh pengguna, tetapi pra-peruntukkan ruang yang lebih besar sebagai kolam memori.

Jumlah ruang khusus yang akan diperuntukkan terlebih dahulu adalah berkaitan dengan pengurus memori yang digunakan oleh malloc Kami akan menggunakan pengurus memori lalai malloc (Ptmalloc2) untuk menganalisis.

Seterusnya, mari lakukan percubaan dan gunakan kod berikut untuk melihat berapa banyak ruang memori yang sebenarnya diperuntukkan oleh sistem pengendalian apabila memohon 1 bait memori melalui malloc.

#include 
#include 

int main() {
  printf("使用cat /proc/%d/maps查看内存分配\n",getpid());
  
  //申请1字节的内存
  void *addr = malloc(1);
  printf("此1字节的内存起始地址:%x\n", addr);
  printf("使用cat /proc/%d/maps查看内存分配\n",getpid());
 
  //将程序阻塞,当输入任意字符时才往下执行
  getchar();

  //释放内存
  free(addr);
  printf("释放了1字节的内存,但heap堆并不会释放\n");
  
  getchar();
  return 0;
}

Laksanakan kod (Biar saya terangkan terlebih dahulu, versi perpustakaan glibc yang saya gunakan ialah 2.17):

一文读懂 Linux 内存分配策略

Kita boleh melihat pengedaran memori proses melalui fail /proc//maps. Saya menapis julat alamat memori dalam fail peta dengan alamat permulaan memori 1-bait ini.

[root@xiaolin ~]# cat /proc/3191/maps | grep d730
00d73000-00d94000 rw-p 00000000 00:00 0                                  [heap]

Memori yang diperuntukkan dalam contoh ini adalah kurang daripada 128 KB, jadi memori digunakan pada ruang timbunan melalui panggilan sistem brk(), supaya anda boleh melihat tanda [timbunan] di hujung kanan.

Anda dapat melihat bahawa julat alamat memori bagi ruang timbunan ialah 00d73000-00d94000, dan saiz julat ini ialah 132KB, yang bermaksud bahawa malloc(1) sebenarnya pra-peruntukkan 132K bait memori.

Sesetengah pelajar mungkin perasan bahawa alamat permulaan memori yang dicetak dalam program ialah d73010, manakala fail peta menunjukkan bahawa alamat permulaan ruang ingatan timbunan ialah d73000 Mengapakah terdapat tambahan 0x10 (16 bait)? Mari tinggalkan soalan ini buat masa ini dan bincangkannya kemudian.

#percuma Adakah memori yang dibebaskan akan dikembalikan kepada sistem pengendalian?

Mari kita laksanakan proses di atas untuk melihat sama ada memori timbunan masih ada selepas memori dikeluarkan melalui fungsi free()?

一文读懂 Linux 内存分配策略

Seperti yang anda boleh lihat dari gambar di bawah, selepas membebaskan memori, ingatan timbunan masih wujud dan belum dikembalikan ke sistem pengendalian.

一文读懂 Linux 内存分配策略

Ini kerana daripada melepaskan 1 bait ini kepada sistem pengendalian, adalah lebih baik untuk menyimpannya di cache dan memasukkannya ke dalam kumpulan memori malloc Apabila proses itu digunakan untuk 1 bait memori sekali lagi, ia boleh digunakan semula secara langsung, yang mana adalah lebih pantas.

Sudah tentu, apabila proses keluar, sistem pengendalian akan menuntut semula semua sumber proses.

Memori timbunan masih wujud selepas ingatan bebas yang dinyatakan di atas adalah untuk ingatan yang digunakan oleh malloc melalui brk().

Jika malloc memohon memori melalui mmap, ia akan dikembalikan kepada sistem pengendalian selepas membebaskan memori secara percuma.

Mari kita lakukan percubaan untuk mengesahkan bahawa kita memohon 128 KB memori melalui malloc, supaya malloc memperuntukkan memori melalui mmap.

#include 
#include 

int main() {
  //申请1字节的内存
  void *addr = malloc(128*1024);
  printf("此128KB字节的内存起始地址:%x\n", addr);
  printf("使用cat /proc/%d/maps查看内存分配\n",getpid());

  //将程序阻塞,当输入任意字符时才往下执行
  getchar();

  //释放内存
  free(addr);
  printf("释放了128KB字节的内存,内存也归还给了操作系统\n");

  getchar();
  return 0;
}

Kod pelaksanaan:

一文读懂 Linux 内存分配策略

Melihat pada pengedaran memori proses, anda boleh mendapati bahawa tiada tanda [kepala] di hujung kanan, menunjukkan bahawa memori tanpa nama diperuntukkan daripada kawasan pemetaan fail melalui mmap melalui pemetaan tanpa nama.

一文读懂 Linux 内存分配策略

Kemudian mari kita bebaskan ingatan ini dan lihat:

一文读懂 Linux 内存分配策略

Semak alamat permulaan memori 128 KB sekali lagi dan anda boleh mendapati ia tidak lagi wujud, menunjukkan bahawa ia telah dikembalikan kepada sistem pengendalian.

一文读懂 Linux 内存分配策略

Berkenaan soalan "Adakah memori yang dipohon oleh malloc dan dikeluarkan secara percuma akan dikembalikan ke sistem pengendalian?", kita boleh membuat ringkasan:

  • Memori yang digunakan oleh malloc melalui brk(), apabila bebas mengeluarkan memori, tidak akan mengembalikan memori ke sistem pengendalian, tetapi akan dicache dalam kolam memori malloc untuk kegunaan seterusnya
  • Apabila percuma membebaskan memori yang diperuntukkan oleh malloc melalui
  • mmap(), akan mengembalikan memori kepada sistem pengendalian, dan memori benar-benar dikeluarkan.

Kenapa tidak semua menggunakan mmap untuk memperuntukkan memori?

Oleh kerana memohon memori daripada sistem pengendalian memerlukan panggilan sistem Untuk melaksanakan panggilan sistem, anda perlu memasukkan keadaan kernel, dan kemudian kembali ke keadaan pengguna.

Jadi, operasi memohon memori harus mengelakkan panggilan sistem yang kerap Jika mmap digunakan untuk memperuntukkan memori, bermakna panggilan sistem mesti dilaksanakan setiap kali.

Selain itu, kerana memori yang diperuntukkan oleh mmap akan dikembalikan kepada sistem pengendalian setiap kali ia dikeluarkan, jadi alamat maya yang diperuntukkan oleh mmap berada dalam keadaan kesalahan halaman setiap kali, dan kemudian apabila alamat maya diakses untuk yang pertama masa, ia akan dicetuskan gangguan halaman.

Dalam erti kata lain,

jika memori kerap diperuntukkan melalui mmap, bukan sahaja keadaan berjalan akan ditukar setiap kali, malah gangguan halaman juga akan berlaku (selepas akses pertama ke alamat maya), yang akan menghasilkan CPU yang besar penggunaan.

Untuk memperbaiki kedua-dua masalah ini, apabila malloc menggunakan memori dalam ruang timbunan melalui panggilan sistem brk(), memandangkan ruang timbunan berterusan, ia secara langsung memperuntukkan memori yang lebih besar sebagai kumpulan memori apabila memori dilepaskan , ia Dicache dalam kolam memori.

Apabila anda memohon memori pada masa akan datang, cuma keluarkan blok memori yang sepadan terus dari kumpulan memori, dan hubungan pemetaan antara alamat maya dan alamat fizikal blok memori ini bukan sahaja mengurangkan bilangan panggilan sistem, Ia juga mengurangkan bilangan gangguan halaman, yang akan sangat mengurangkan penggunaan CPU.

Memandangkan brk sangat hebat, mengapa tidak menggunakan brk untuk semua peruntukan?

Kami menyebut sebelum ini bahawa memori yang diperuntukkan dari ruang timbunan melalui brk tidak akan dikembalikan kepada sistem pengendalian, jadi mari kita pertimbangkan senario sedemikian.

Jika kita memohon tiga keping memori 10k, 20k, dan 30k berturut-turut, jika 10k dan 20k dilepaskan dan menjadi ruang ingatan percuma, jika memori yang digunakan untuk kali seterusnya kurang daripada 30k, maka ruang memori percuma ini boleh digunakan semula .

一文读懂 Linux 内存分配策略Tetapi jika memori yang diminta seterusnya melebihi 30k, tiada ruang memori kosong yang tersedia, dan anda mesti memohon kepada OS, dan memori sebenar yang digunakan akan terus meningkat.

Oleh itu, memandangkan sistem kerap mallocs dan membebaskan, terutamanya untuk blok kecil memori, semakin banyak serpihan yang tidak boleh digunakan akan dijana dalam timbunan, yang membawa kepada "kebocoran memori". Fenomena "kebocoran" ini tidak dapat dikesan menggunakan valgrind.

Jadi, dalam pelaksanaan malloc, perbezaan, kebaikan dan keburukan dalam tingkah laku brk dan mmap dipertimbangkan sepenuhnya, dan blok memori yang besar (128KB) diperuntukkan secara lalai sebelum mmap digunakan untuk memperuntukkan ruang memori.

Fungsi free() hanya lulus dalam alamat memori Mengapa ia boleh tahu berapa banyak memori yang perlu dikosongkan?

Ingat, saya nyatakan sebelum ini bahawa alamat permulaan memori yang dikembalikan kepada mod pengguna oleh malloc adalah 16 bait lebih daripada alamat permulaan ruang timbunan proses?

16 bait tambahan menyimpan maklumat perihalan blok memori, seperti saiz blok memori.

一文读懂 Linux 内存分配策略

Dengan cara ini, apabila fungsi free() dilaksanakan, free akan mengimbangi alamat memori masuk sebanyak 16 bait ke kiri, dan kemudian menganalisis saiz blok memori semasa daripada 16 bait ini, dan secara semula jadi mengetahui betapa besarnya perlu dilepaskan ingatan.

Atas ialah kandungan terperinci Fahami strategi peruntukan memori Linux dalam satu artikel. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:lxlinux.net. Jika ada pelanggaran, sila hubungi admin@php.cn Padam