การเรียกระบบ brk () ทำหน้าที่อะไร?


184

ตามคู่มือโปรแกรมเมอร์ Linux:

brk () และ sbrk () เปลี่ยนตำแหน่งของตัวแบ่งโปรแกรมซึ่งกำหนดจุดสิ้นสุดของส่วนข้อมูลของกระบวนการ

ส่วนข้อมูลหมายถึงอะไรที่นี่ เป็นเพียงส่วนข้อมูลหรือข้อมูล BSS และฮีปรวมกันหรือไม่

ตามวิกิ:

บางครั้งข้อมูลพื้นที่ BSS และกองรวมเรียกว่า "ส่วนข้อมูล"

ฉันไม่เห็นเหตุผลที่จะเปลี่ยนขนาดของกลุ่มข้อมูล ถ้าเป็นข้อมูลBSSและกองรวมกันมันก็สมเหตุสมผลแล้วเพราะกองจะได้พื้นที่มากขึ้น

ซึ่งนำฉันไปที่คำถามที่สองของฉัน ในบทความทั้งหมดที่ฉันอ่านจนถึงตอนนี้ผู้เขียนบอกว่ากองเติบโตขึ้นและกองทับลง แต่สิ่งที่พวกเขาไม่อธิบายก็คือจะเกิดอะไรขึ้นเมื่อฮีปใช้พื้นที่ทั้งหมดระหว่างฮีปกับสแต็ค

ป้อนคำอธิบายรูปภาพที่นี่


1
แล้วคุณจะทำอย่างไรเมื่อคุณไม่มีที่ว่าง? คุณสลับเป็น HDD เมื่อคุณใช้พื้นที่คุณปล่อยให้เป็นข้อมูลประเภทอื่น
Igoris Azanovas

28
@Igoris: คุณกำลังสับสนหน่วยความจำกายภาพ (ซึ่งคุณสามารถสลับไปยังดิสก์ตามต้องการใช้หน่วยความจำเสมือน) และพื้นที่ที่อยู่ เมื่อคุณเติมพื้นที่ที่อยู่ของคุณจำนวนการแลกเปลี่ยนจะไม่ให้ที่อยู่เหล่านั้นกลับมาตรงกลาง
Daniel Pryden

7
เช่นเดียวกับการแจ้งเตือนการbrk()เรียกใช้ระบบมีประโยชน์มากกว่าในภาษาแอสเซมบลีมากกว่าใน C ใน C malloc()ควรใช้แทนbrk()วัตถุประสงค์การจัดสรรข้อมูลใด ๆ - แต่สิ่งนี้ไม่ได้ทำให้คำถามที่เสนอนั้นไม่ถูกต้อง
alecov

2
@Brian: heap เป็นโครงสร้างข้อมูลที่ซับซ้อนสำหรับการจัดการพื้นที่ที่มีขนาดและการจัดเรียงที่แตกต่างกัน, free pool, ฯลฯ . เธรดสแต็กมักจะต่อเนื่องกัน (ในพื้นที่ที่อยู่เสมือน) ของหน้าสมบูรณ์ ในระบบปฏิบัติการส่วนใหญ่จะมีไฟล์ตัวจัดสรรเพจซึ่งเป็นกองซ้อนกองและไฟล์ที่แม็พหน่วยความจำ
Ben Voigt

2
@Brian: ใครบอกว่ามี "กอง" ที่ถูกจัดการโดยbrk()และsbrk()? สแต็คได้รับการจัดการโดยตัวจัดสรรเพจในระดับที่ต่ำกว่ามาก
Ben Voigt

คำตอบ:


233

ในแผนภาพที่คุณโพสต์ "ตัวแบ่ง" - ที่อยู่ที่จัดการโดยbrkและsbrk- เป็นเส้นประที่ด้านบนของฮีป

รูปภาพที่เรียบง่ายของเค้าโครงหน่วยความจำเสมือน

เอกสารที่คุณอ่านอธิบายสิ่งนี้ว่าเป็นจุดสิ้นสุดของ "ส่วนข้อมูล" เพราะในแบบดั้งเดิม (pre-shared-libraries ก่อนล่วงหน้าmmap) Unix ส่วนข้อมูลนั้นต่อเนื่องกับฮีป ก่อนที่โปรแกรมจะเริ่มต้นเคอร์เนลจะโหลดบล็อก "text" และ "data" ลงใน RAM เริ่มต้นที่ address zero (อันที่จริงแล้วข้างบน address zero เล็กน้อยดังนั้นตัวชี้ NULL จะไม่ชี้ไปที่อะไรเลย) และตั้งค่า break address เป็น จุดสิ้นสุดของส่วนข้อมูล การเรียกครั้งแรกไปยังmallocจะใช้sbrkเพื่อย้ายตัวแบ่งและสร้างฮีประหว่างส่วนบนของส่วนข้อมูลและที่อยู่ตัวแบ่งใหม่ที่สูงกว่าดังที่แสดงในแผนภาพและการใช้ในภายหลังmallocจะใช้เพื่อทำให้กองใหญ่ขึ้น เท่าที่จำเป็น

ในขณะเดียวกันสแต็กจะเริ่มที่ด้านบนของหน่วยความจำและโตขึ้น สแต็คไม่ต้องการการเรียกระบบที่ชัดเจนเพื่อทำให้ใหญ่ขึ้น มันเริ่มต้นด้วย RAM ที่จัดสรรให้มากที่สุดเท่าที่จะทำได้ (นี่เป็นวิธีการแบบดั้งเดิม) หรือมีพื้นที่ของที่อยู่ที่สงวนไว้ด้านล่างสแต็กซึ่งเคอร์เนลจะจัดสรร RAM โดยอัตโนมัติเมื่อสังเกตเห็นความพยายามในการเขียน (นี่คือวิธีการที่ทันสมัย) ไม่ว่าจะด้วยวิธีใดอาจมีหรือไม่มีภูมิภาค "การ์ด" ที่ด้านล่างของพื้นที่ที่อยู่ที่สามารถใช้สำหรับสแต็ก หากภูมิภาคนี้มีอยู่ (ระบบที่ทันสมัยทั้งหมดทำสิ่งนี้) จะไม่มีการแมปอย่างถาวร ถ้าอย่างใดอย่างหนึ่งสแต็กหรือฮีปพยายามที่จะเติบโตขึ้นมาคุณจะได้รับการแบ่งเซ็กเมนต์ ตามเนื้อผ้าแม้ว่าเคอร์เนลไม่ได้พยายามที่จะบังคับใช้ขอบเขต กองสามารถเติบโตเป็นกองหรือกองสามารถเติบโตเป็นกองและวิธีที่พวกเขาจะเขียนลวกข้อมูลของกันและกันและโปรแกรมจะผิดพลาด ถ้าคุณโชคดีมากมันจะพังทันที

ฉันไม่แน่ใจว่าหมายเลข 512GB ในแผนภาพนี้มาจากที่ใด มันแสดงถึงพื้นที่ที่อยู่เสมือน 64 บิตซึ่งไม่สอดคล้องกับแผนที่หน่วยความจำที่ง่ายมากที่คุณมี พื้นที่ที่อยู่ 64- บิตที่แท้จริงมีลักษณะดังนี้:

ลดความซับซ้อนของพื้นที่ที่อยู่

              Legend:  t: text, d: data, b: BSS

นี่ไม่ได้เป็นการขยายจากระยะไกลและไม่ควรตีความว่าระบบปฏิบัติการใด ๆ ที่ได้รับ (หลังจากที่ฉันวาดมันฉันค้นพบว่า Linux ทำให้การปฏิบัติการใกล้กับศูนย์มากกว่าที่ฉันคิดและไลบรารีที่ใช้ร่วมกัน ที่ที่อยู่สูงอย่างน่าประหลาดใจ) พื้นที่สีดำของแผนภาพนี้ไม่มีการแมป - การเข้าถึงใด ๆ ที่ทำให้เกิด segfault ทันที - และพวกมันมีขนาดมหึมาเมื่อเทียบกับพื้นที่สีเทา พื้นที่สีเทาอ่อนเป็นโปรแกรมและไลบรารีที่ใช้ร่วมกัน (อาจมีไลบรารีที่ใช้ร่วมกันหลายสิบ) แต่ละคนมีอิสระส่วนข้อความและข้อมูล (และส่วน "bss" ซึ่งยังมีข้อมูลทั่วโลก แต่เริ่มต้นได้ที่ all-bits-zero แทนที่จะใช้พื้นที่ในการปฏิบัติการหรือไลบรารีบนดิสก์) ฮีปไม่จำเป็นต้องต่อเนื่องกับเซ็กเมนต์ข้อมูลของปฏิบัติการอีกต่อไป - ฉันวาดมันด้วยวิธีนี้ แต่ดูเหมือนว่า Linux อย่างน้อยก็ไม่ได้ทำเช่นนั้น สแต็กไม่ได้ถูกตรึงไว้ที่ส่วนบนของพื้นที่ที่อยู่เสมือนอีกต่อไปและระยะห่างระหว่างฮีปและสแต็คนั้นมากจนคุณไม่ต้องกังวลกับการข้ามมัน

การแบ่งยังคงเป็นขีด จำกัด สูงสุดของกอง แต่สิ่งที่ผมไม่ได้แสดงให้เห็นว่าอาจจะมีหลายสิบของการจัดสรรเป็นอิสระของหน่วยความจำออกมีในบางสีดำที่ทำด้วยแทนmmap brk(ระบบปฏิบัติการจะพยายามทำให้สิ่งเหล่านี้อยู่ห่างไกลจากbrkพื้นที่เพื่อไม่ให้ชนกัน)


7
+1 สำหรับคำอธิบายโดยละเอียด คุณรู้หรือไม่ว่าmallocยังคงใช้brkหรือถ้ามันใช้mmapเพื่อให้ "คืน" บล็อกหน่วยความจำแยกต่างหาก?
Anders Abel

18
มันขึ้นอยู่กับการใช้งานที่เฉพาะเจาะจง แต่ IIUC จำนวนมากในปัจจุบันmallocใช้brkพื้นที่สำหรับการจัดสรรขนาดเล็กและการจัดสรรรายบุคคลmmapสำหรับการจัดสรรขนาดใหญ่ (พูด,> 128K) ดูตัวอย่างการอภิปรายของ MMAP_THRESHOLD ในmalloc(3)manpage ของLinux
zwol

1
แน่นอนคำอธิบายที่ดี แต่อย่างที่คุณบอกว่า Stack ไม่อยู่ที่ด้านบนสุดของพื้นที่ที่อยู่เสมือน สิ่งนี้เป็นจริงสำหรับพื้นที่ที่อยู่ 64 บิตเท่านั้นหรือเป็นจริงแม้แต่สำหรับพื้นที่ที่อยู่ 32 บิต และถ้าสแต็คตั้งอยู่ที่ด้านบนของพื้นที่ที่อยู่จะมีการแมปหน่วยความจำแบบไม่ระบุชื่อที่ไหน มันอยู่ที่ด้านบนของพื้นที่ที่อยู่เสมือนก่อนที่จะสแต็ค
nik

3
@Nikhil: มันซับซ้อน ระบบ 32 บิตส่วนใหญ่วางสแต็กที่ด้านบนสุดของพื้นที่ที่อยู่โหมดผู้ใช้ซึ่งมักจะเป็นเพียง 2 หรือ 3G ต่ำกว่าของพื้นที่ที่อยู่เต็ม (พื้นที่ที่เหลือถูกสงวนไว้สำหรับเคอร์เนล) ตอนนี้ฉันไม่สามารถนึกถึงสิ่งที่ไม่ได้ แต่ไม่รู้ทั้งหมดเลย CPU 64 บิตส่วนใหญ่ไม่อนุญาตให้คุณใช้พื้นที่ 64 บิตทั้งหมด ที่อยู่สูง 10 ถึง 16 บิตจะต้องเป็นศูนย์ทั้งหมดหรือทั้งหมด โดยทั่วไปสแต็กจะอยู่ใกล้กับด้านบนของที่อยู่ต่ำที่สามารถใช้งานได้ ฉันไม่สามารถทำให้คุณมีกฎmmap; มันขึ้นอยู่กับระบบปฏิบัติการเป็นอย่างมาก
zwol

3
@RiccardoBestetti มันเปลืองพื้นที่ที่อยู่แต่ก็ไม่เป็นอันตราย - พื้นที่ที่อยู่เสมือนแบบ 64- บิตนั้นใหญ่มากถ้าคุณเบิร์นผ่านกิกะไบต์ของมันทุกวินาทีมันคงต้องใช้เวลา 500 ปีกว่าที่จะหมด [1] โปรเซสเซอร์ส่วนใหญ่ไม่อนุญาตให้ใช้ที่อยู่เสมือนมากกว่า 2 ^ 48 ถึง 2 ^ 53 บิต (ข้อยกเว้นเดียวที่ฉันรู้คือ POWER4 ในโหมดตารางหน้าเพจที่แฮช) ไม่เสียแรมทางกายภาพ ที่อยู่ที่ไม่ได้ใช้จะไม่ถูกกำหนดให้กับ RAM
zwol

26

ตัวอย่างที่เรียกใช้น้อยที่สุด

การเรียกระบบ brk () ทำอะไรได้บ้าง?

ขอเคอร์เนลให้คุณอ่านและเขียนหน่วยความจำต่อเนื่องที่เรียกว่าฮีป

ถ้าคุณไม่ถามมันอาจเป็นการแยกคุณออก

โดยไม่ต้องbrk:

#define _GNU_SOURCE
#include <unistd.h>

int main(void) {
    /* Get the first address beyond the end of the heap. */
    void *b = sbrk(0);
    int *p = (int *)b;
    /* May segfault because it is outside of the heap. */
    *p = 1;
    return 0;
}

ด้วยbrk:

#define _GNU_SOURCE
#include <assert.h>
#include <unistd.h>

int main(void) {
    void *b = sbrk(0);
    int *p = (int *)b;

    /* Move it 2 ints forward */
    brk(p + 2);

    /* Use the ints. */
    *p = 1;
    *(p + 1) = 2;
    assert(*p == 1);
    assert(*(p + 1) == 2);

    /* Deallocate back. */
    brk(b);

    return 0;
}

GitHub ต้นน้ำ

ด้านบนอาจไม่ตีหน้าใหม่และไม่แยก segfault แม้จะไม่มีbrkดังนั้นนี่เป็นเวอร์ชั่นที่ก้าวร้าวมากขึ้นซึ่งจัดสรร 16MiB และมีแนวโน้มที่จะ segfault โดยไม่ต้องbrk:

#define _GNU_SOURCE
#include <assert.h>
#include <unistd.h>

int main(void) {
    void *b;
    char *p, *end;

    b = sbrk(0);
    p = (char *)b;
    end = p + 0x1000000;
    brk(end);
    while (p < end) {
        *(p++) = 1;
    }
    brk(b);
    return 0;
}

ทดสอบบน Ubuntu 18.04

การสร้างภาพพื้นที่ที่อยู่เสมือน

ก่อนbrk:

+------+ <-- Heap Start == Heap End

หลังbrk(p + 2):

+------+ <-- Heap Start + 2 * sizof(int) == Heap End 
|      |
| You can now write your ints
| in this memory area.
|      |
+------+ <-- Heap Start

หลังbrk(b):

+------+ <-- Heap Start == Heap End

เพื่อให้เข้าใจถึงช่องว่างที่อยู่ได้ดีขึ้นคุณควรทำความคุ้นเคยกับการเพจ: การเพจจิ้ง x86 ทำงานอย่างไร? .

ทำไมเราต้องการทั้งสองอย่างbrkและsbrk?

brkสามารถนำไปใช้กับการsbrkคำนวณ + ออฟเซ็ตซึ่งทั้งสองมีอยู่เพื่อความสะดวก

ในส่วนแบ็คเอนด์เคอร์เนล Linux v5.0 มีการเรียกระบบเดียวbrkที่ใช้ในการดำเนินการทั้งสองอย่าง: https://github.com/torvalds/linux/blob/v5.0/arch/x86/entry/syscalls/syscall_64 tbl # L23

12  common  brk         __x64_sys_brk

brkPOSIX คืออะไร

brkเคยเป็น POSIX แต่มันถูกลบใน POSIX 2001 ดังนั้นจึงจำเป็นต้อง_GNU_SOURCEเข้าถึง glibc wrapper

การลบนี้น่าจะเกิดจากการแนะนำmmapซึ่งเป็น superset ที่อนุญาตให้มีการจัดสรรหลายช่วงและตัวเลือกการจัดสรรเพิ่มเติม

ฉันคิดว่าไม่มีกรณีที่ถูกต้องที่คุณควรใช้brkแทนmallocหรือmmapทุกวันนี้

brk VS malloc

brkmallocเป็นหนึ่งในความเป็นไปได้ของการใช้เก่า

mmapเป็นรุ่นใหม่ที่มีประสิทธิภาพมากขึ้น stricly กลไกซึ่งมีแนวโน้มที่ทุกระบบ POSIX mallocใช้อยู่ในปัจจุบันในการดำเนินการ นี่คือที่ทำงานได้น้อยที่สุดmmapเช่นการจัดสรรหน่วยความจำ

ฉันสามารถผสมbrkและ malloc ได้หรือไม่?

หากคุณmallocนำไปใช้กับbrkฉันไม่ทราบว่าวิธีการที่ไม่สามารถระเบิดสิ่งต่าง ๆ เนื่องจากbrkจัดการหน่วยความจำช่วงเดียวเท่านั้น

อย่างไรก็ตามฉันไม่พบอะไรเกี่ยวกับเรื่องนี้ในเอกสาร glibc เช่น:

สิ่งที่อาจจะเป็นเพียงแค่ทำงานที่นั่นผมคิดว่าตั้งแต่มีแนวโน้มที่จะใช้สำหรับการmmapmalloc

ดูสิ่งนี้ด้วย:

ข้อมูลเพิ่มเติม

ภายในเคอร์เนลจะตัดสินว่ากระบวนการสามารถมีหน่วยความจำขนาดนั้นหรือไม่และหน้าหน่วยความจำ earmarks สำหรับการใช้งานนั้น

สิ่งนี้อธิบายถึงการเปรียบเทียบสแต็กกับฮีป: ฟังก์ชันของคำแนะนำการกด / ป๊อปที่ใช้กับการลงทะเบียนในแอสเซมบลี x86 คืออะไร


4
ตั้งแต่pเป็นตัวชี้การพิมพ์intสิ่งนี้ไม่ควรbrk(p + 2);?
Johan Boulé

หมายเหตุเล็ก ๆ : การแสดงออกใน for-loop ของรุ่นก้าวร้าวน่าจะเป็น*(p + i) = 1;
lima.sierra

โดยวิธีการทำไมเราต้องใช้brk(p + 2)แทนเพียงแค่เพิ่มมันด้วยsbrk(2)? brk จำเป็นจริงๆเหรอ?
Yi Lin Liu

1
@YiLinLiu ฉันคิดว่ามันเป็นแค่สอง frontends C ที่คล้ายกันมากสำหรับแบ็กเอนด์เคอร์เนลเดียว ( brksyscall) brkจะสะดวกกว่าเล็กน้อยในการกู้คืนสแต็กที่จัดสรรไว้ก่อนหน้านี้
Ciro Santilli 郝海东冠状病六四事件法轮功

1
@CiroSantilli 新疆改造中心 996ICU 六四事件พิจารณาขนาดของ int เป็น 4 ไบต์และขนาดของ int * เป็น 4 ไบต์ (บนเครื่อง 32 บิต) ฉันสงสัยว่ามันไม่ควรเพิ่มขึ้นเพียง 4 ไบต์ (แทน 8 - (2 * ขนาดของ int)) ไม่ควรชี้ไปที่ที่จัดเก็บฮีปถัดไป - ซึ่งจะอยู่ห่าง 4 ไบต์ (ไม่ใช่ 8) แก้ไขให้ถูกต้องหากฉันพลาดบางสิ่งที่นี่
Saket Sharad

10

คุณสามารถใช้brkและsbrkตัวคุณเองเพื่อหลีกเลี่ยง "malloc ค่าใช้จ่าย" ทุกคนมักจะบ่นเกี่ยวกับ แต่คุณไม่สามารถใช้วิธีนี้ร่วมกับmallocดังนั้นจึงเป็นเรื่องที่เหมาะสมเมื่อคุณไม่ต้องทำfreeอะไรเลย เพราะคุณทำไม่ได้ นอกจากนี้คุณควรหลีกเลี่ยงการเรียกใช้ไลบรารีที่อาจใช้เป็นการmallocภายใน กล่าวคือ strlenอาจปลอดภัย แต่fopenอาจจะไม่

โทรเช่นเดียวกับที่คุณจะเรียกsbrk mallocส่งคืนตัวชี้ไปยังตัวแบ่งปัจจุบันและเพิ่มการแบ่งตามจำนวนนั้น

void *myallocate(int n){
    return sbrk(n);
}

ขณะที่คุณสามารถจัดสรรของแต่ละบุคคลไม่ฟรี (เพราะไม่มีmalloc โสหุ้ยจำ) คุณสามารถปลดปล่อยพื้นที่ทั้งหมดโดยการเรียกbrkกับค่าส่งกลับโดยสายแรกที่sbrkจึงrewinding brk

void *memorypool;
void initmemorypool(void){
    memorypool = sbrk(0);
}
void resetmemorypool(void){
    brk(memorypool);
}

คุณสามารถสแต็กภูมิภาคเหล่านี้ทิ้งพื้นที่ล่าสุดโดยย้อนกลับการหยุดพักเพื่อเริ่มต้นของพื้นที่


อีกหนึ่งสิ่ง ...

sbrkนอกจากนี้ยังจะเป็นประโยชน์ในกอล์ฟรหัสเพราะมัน 2 mallocตัวอักษรที่สั้นกว่า


7
-1 เพราะ: malloc/ freeแน่นอนที่สุดสามารถ (และทำ) ให้หน่วยความจำกลับไปที่ระบบปฏิบัติการ พวกเขาอาจไม่ได้ทำเมื่อคุณต้องการให้พวกเขา แต่นั่นเป็นเรื่องของการวิเคราะห์พฤติกรรมที่ไม่สมบูรณ์สำหรับกรณีการใช้งานของคุณ ที่สำคัญกว่านั้นคือไม่ปลอดภัยในการโทรsbrkด้วยอาร์กิวเมนต์ที่ไม่ใช่ศูนย์ในโปรแกรมใด ๆ ที่อาจเคยเรียกใช้mallocและฟังก์ชั่นไลบรารี C เกือบทั้งหมดได้รับอนุญาตให้โทรmallocภายใน สิ่งเดียวที่จะไม่แน่นอนคือฟังก์ชั่นความปลอดภัยสัญญาณ async
zwol

และด้วย "ไม่ปลอดภัย" ฉันหมายถึง "โปรแกรมของคุณจะพัง"
zwol

ฉันได้แก้ไขเพื่อเอาหน่วยความจำกลับมาmallocโม้และกล่าวถึงอันตรายของฟังก์ชั่นห้องสมุดภายในโดยใช้
luser droog

1
หากคุณต้องการจัดสรรหน่วยความจำอย่างแฟนซีให้ยึดฐานไว้ที่ด้านบนสุดของ malloc หรือด้านบนของ mmap อย่าแตะ brk และ sbrk พวกเขาเป็นพระธาตุจากอดีตที่ทำอันตรายมากกว่าดี (แม้แต่ manpages บอกคุณให้ชัดเจนของพวกเขา!)
Eloff

3
นี่มันโง่ หากคุณต้องการหลีกเลี่ยงค่าใช้จ่าย malloc สำหรับการจัดสรรขนาดเล็กจำนวนมากให้ทำการจัดสรรครั้งใหญ่หนึ่งครั้ง (ด้วย malloc หรือ mmap ไม่ใช่ sbrk) และทำการแจกแจงด้วยตัวคุณเอง หากคุณเก็บโหนดของต้นไม้ไบนารีไว้ในอาร์เรย์คุณสามารถใช้ดัชนี 8b หรือ 16b แทนตัวชี้ 64b นี้ทำงานได้ดีเมื่อคุณไม่ได้มีการลบโหนดใด ๆ จนกว่าคุณพร้อมที่จะลบทุกโหนด (เช่นสร้างพจนานุกรมเรียงได้ทันที) การใช้sbrkสิ่งนี้มีประโยชน์เฉพาะสำหรับการเขียนโค้ดกอล์ฟเนื่องจากการใช้งานด้วยตนเองmmap(MAP_ANONYMOUS)นั้นดีกว่าในทุก ๆ ด้านยกเว้นขนาดซอร์สโค้ด
Peter Cordes

3

มีการแม็พหน่วยความจำส่วนตัวแบบไม่ระบุชื่อที่กำหนดพิเศษ (โดยปกติจะอยู่นอกเหนือจาก data / bss แต่ Linux สมัยใหม่จะปรับตำแหน่งด้วย ASLR) ในหลักการก็ไม่ได้ดีไปกว่าการทำแผนที่อื่น ๆ ที่คุณสามารถสร้างขึ้นด้วยmmapแต่ Linux มีการเพิ่มประสิทธิภาพบางอย่างที่ทำให้มันเป็นไปได้ที่จะขยายจุดสิ้นสุดของการทำแผนที่นี้ (โดยใช้brksyscall) ขึ้นด้วยต้นทุนที่ลดลงล็อคเมื่อเทียบกับสิ่งที่mmapหรือmremapจะเกิดขึ้น สิ่งนี้ทำให้มันน่าสนใจสำหรับmallocการใช้งานที่จะใช้เมื่อใช้งานฮีปหลัก


คุณหมายถึงความเป็นไปได้ที่จะขยายจุดสิ้นสุดของการทำแผนที่นี้ขึ้นใช่หรือไม่
zwol

ใช่แก้ไขแล้ว ขอโทษด้วยกับเรื่องนั้น!
. GitHub หยุดช่วยน้ำแข็ง

0

ฉันสามารถตอบคำถามที่สองของคุณ Malloc จะล้มเหลวและส่งคืนพอยน์เตอร์พอยน์เตอร์ นั่นเป็นเหตุผลที่คุณตรวจสอบพอยน์เตอร์พอยน์เตอร์เสมอเมื่อทำการจัดสรรหน่วยความจำแบบไดนามิก


แล้วการใช้ brk และ sbrk คืออะไร?
nik

3
@NikhilRathod: malloc()จะใช้brk()และ / หรือsbrk()ภายใต้ประทุน - และคุณก็สามารถทำได้เช่นกันหากคุณต้องการใช้เวอร์ชันที่ปรับแต่งเองของmalloc()คุณ
Daniel Pryden

@Daniel Pryden: brk และ sbrk ทำงานอย่างไรกับฮีปเมื่ออยู่ระหว่างสแต็กและส่วนข้อมูลดังแสดงในแผนภาพด้านบน สำหรับการทำงานกองนี้ควรจะอยู่ในที่สุด ฉันถูกไหม?
nik

2
@Brian: Daniel กล่าวว่าระบบปฏิบัติการจัดการส่วนสแต็ไม่ใช่ตัวชี้สแต็ค... สิ่งที่แตกต่างกันมาก ประเด็นก็คือไม่มี sbrk / brk syscall สำหรับกลุ่มสแต็ก - ลินุกซ์จัดสรรหน้าโดยอัตโนมัติเมื่อพยายามที่จะเขียนไปยังจุดสิ้นสุดของกลุ่มสแต็ก
Jim Balter

1
และไบรอันคุณตอบเพียงครึ่งหนึ่งของคำถาม อีกครึ่งหนึ่งเป็นสิ่งที่เกิดขึ้นหากคุณพยายามดันลงไปในกองซ้อนเมื่อไม่มีที่ว่างเหลืออยู่ ... คุณจะได้รับการแบ่งเซ็กเมนต์
Jim Balter

0

ฮีปจะอยู่ในส่วนสุดท้ายของโปรแกรม brk()ใช้เพื่อเปลี่ยน (ขยาย) ขนาดของฮีป เมื่อฮีปไม่สามารถเติบโตได้อีกต่อไปการmallocโทรใด ๆก็จะล้มเหลว


คุณกำลังบอกว่าไดอะแกรมทั้งหมดในอินเทอร์เน็ตเช่นเดียวกับคำถามของฉันนั้นผิด ถ้าเป็นไปได้โปรดช่วยชี้ให้ฉันเห็นไดอะแกรมที่ถูกต้อง
nik

2
@ Nikkhil โปรดทราบว่าส่วนบนของไดอะแกรมนั้นคือจุดสิ้นสุดของหน่วยความจำ ด้านบนของสแต็คเลื่อนลงบนไดอะแกรมเมื่อสแต็คโตขึ้น ด้านบนของฮีปเลื่อนขึ้นบนไดอะแกรมขณะที่ขยาย
Brian Gordon

0

ส่วนของข้อมูลคือส่วนของหน่วยความจำที่เก็บข้อมูลสแตติกทั้งหมดของคุณอ่านจากไฟล์เรียกทำงานเมื่อเปิดใช้งานและมักจะเต็มไปด้วยศูนย์


นอกจากนี้ยังเก็บข้อมูลสแตติกที่ไม่ได้กำหนดค่าเริ่มต้น (ไม่มีอยู่ในปฏิบัติการ) ซึ่งอาจเป็นขยะ
luser droog

ข้อมูลสแตติกแบบ Uninitialized ( .bss) ถูกเตรียมใช้งานเป็น all-bits-zero โดย OS ก่อนที่โปรแกรมจะเริ่ม นี่คือการรับรองจริงโดยมาตรฐาน C บางระบบฝังตัวอาจจะไม่รำคาญผมคิดว่า (ผมไม่เคยเห็นหนึ่ง แต่ผมไม่ได้ทำงานทั้งหมดที่ฝังตัว)
zwol

@zwol: Linux มีตัวเลือกเวลาคอมไพล์ที่จะไม่ส่งกลับศูนย์หน้าmmapแต่ฉันคิดว่า.bssจะยังคงเป็นศูนย์ พื้นที่ BSS อาจเป็นวิธีที่กะทัดรัดที่สุดในการแสดงความจริงว่าโปรแกรมต้องการอาร์เรย์ zerod
Peter Cordes

1
@PeterCordes สิ่งที่มาตรฐาน C กล่าวคือตัวแปรทั่วโลกที่ประกาศโดยไม่มี initializer จะถือว่าเป็นถ้าเริ่มต้นเป็นศูนย์ การปรับใช้ AC ที่ทำให้ตัวแปรดังกล่าว.bssไม่เป็นศูนย์.bssจึงไม่เป็นไปตามข้อกำหนด แต่ไม่มีอะไรบังคับให้มีการนำ C ไปใช้.bssเลยหรือแม้แต่มีสิ่งนั้น
zwol

@PeterCordes นอกจากนี้เส้นแบ่งระหว่าง "C ดำเนินการ" และโปรแกรมที่สามารถเลือนมากเช่นมักจะมีก้อนเล็ก ๆ ของรหัสจากการดำเนินการเชื่อมโยงแบบคงที่ปฏิบัติการในแต่ละที่นั้นก่อนจะวิ่งmain; โค้ดนั้นอาจเป็นศูนย์ออกจาก.bssพื้นที่แทนที่จะให้เคอร์เนลทำและจะยังคงเป็นไปตามนั้น
zwol

0

malloc ใช้การเรียกระบบ brk เพื่อจัดสรรหน่วยความจำ

ประกอบด้วย

int main(void){

char *a = malloc(10); 
return 0;
}

เรียกใช้โปรแกรมอย่างง่าย ๆ ด้วย strace มันจะเรียกระบบ brk

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.