خبر و ترفند روز

خبر و ترفند های روز را اینجا بخوانید!

نحوه عملکرد تخصیص حافظه در لینوکس

هر برنامه فعال در سیستم شما برای اجرا به حافظه نیاز دارد. پس چگونه حافظه در یک ماشین لینوکس تخصیص و به اشتراک گذاشته می شود؟

در کامپیوترها، برای اینکه یک فرآیند قابل اجرا باشد، باید در حافظه قرار گیرد. برای این کار، یک فیلد باید به یک فرآیند در حافظه اختصاص داده شود. تخصیص حافظه موضوع مهمی است که باید از آن آگاه بود، به ویژه در معماری هسته و سیستم.

بیایید با جزئیات به تخصیص حافظه لینوکس نگاهی بیندازیم و بفهمیم که در پشت صحنه چه می گذرد.

تخصیص حافظه چگونه انجام می شود؟

اکثر مهندسان نرم افزار از جزئیات این فرآیند اطلاعی ندارند. اما اگر شما یک کاندید برنامه نویس سیستم هستید، باید بیشتر در مورد آن بدانید. هنگامی که به فرآیند تخصیص نگاه می کنیم، لازم است کمی به جزئیات در مورد لینوکس و کتابخانه glibc بپردازیم.

زمانی که برنامه ها نیاز به حافظه دارند، باید آن را از سیستم عامل درخواست کنند. این درخواست از هسته به طور طبیعی نیاز به یک فراخوانی سیستم دارد. شما نمی توانید حافظه خود را در حالت کاربر تخصیص دهید.

خانواده توابع malloc() مسئول تخصیص حافظه در زبان C است. سوالی که در اینجا مطرح می شود این است که آیا malloc()، به عنوان یک تابع glibc، یک فراخوانی مستقیم سیستم را انجام می دهد یا خیر.

هیچ فراخوانی سیستمی به نام malloc در هسته لینوکس وجود ندارد. با این حال، دو فراخوانی سیستمی برای تقاضای حافظه برنامه ها وجود دارد که عبارتند از brk و mmap.

از آنجایی که شما از طریق توابع glibc درخواست حافظه در برنامه خود می کنید، ممکن است تعجب کنید که در این مرحله از کدام یک از این نام های سیستمی glibc استفاده می کند. پاسخ هر دو است.

نمودار تخصیص حافظه

اولین تماس سیستمی: brk

هر فرآیند دارای یک فیلد داده پیوسته است. با فراخوانی سیستم brk مقدار شکست برنامه که حد فیلد داده را تعیین می کند افزایش یافته و فرآیند تخصیص انجام می شود.

اگرچه تخصیص حافظه با این روش بسیار سریع است، اما همیشه امکان بازگرداندن فضای استفاده نشده به سیستم وجود ندارد.

به عنوان مثال، در نظر بگیرید که پنج فیلد، هر کدام 16 کیلوبایت، با فراخوانی سیستم brk از طریق تابع malloc() اختصاص می‌دهید. وقتی کار شماره دو این فیلدها تمام شد، امکان برگرداندن منبع (تخصیص) مربوطه وجود ندارد تا سیستم بتواند از آن استفاده کند. زیرا اگر مقدار آدرس را برای نشان دادن محلی که فیلد شماره دو شما شروع می شود کاهش دهید، با فراخوانی به brk، برای فیلدهای شماره سه، چهار و پنج، توزیع را انجام داده اید.

مطلب مرتبط:   3 بهترین مرورگر وب مبتنی بر ترمینال برای لینوکس

برای جلوگیری از از دست دادن حافظه در این سناریو، پیاده سازی malloc در glibc مکان های تخصیص داده شده در فیلد داده پردازش را نظارت می کند و سپس تعیین می کند که آن را با تابع free() به سیستم برگرداند تا سیستم بتواند از فضای خالی برای حافظه بیشتر استفاده کند. تخصیص ها

به عبارت دیگر، پس از تخصیص پنج ناحیه 16 کیلوبایتی، اگر ناحیه دوم با تابع free() برگردانده شود و پس از مدتی مجدداً 16 کیلوبایت منطقه دیگر درخواست شود، به جای بزرگنمایی ناحیه داده از طریق فراخوانی سیستم brk، آدرس قبلی بازگشت.

با این حال، اگر منطقه درخواستی جدید بزرگتر از 16 کیلوبایت باشد، ناحیه داده با اختصاص یک منطقه جدید با فراخوانی سیستم brk بزرگتر می شود زیرا منطقه دو نمی تواند استفاده شود. اگرچه منطقه شماره دو مورد استفاده قرار نمی گیرد، برنامه به دلیل تفاوت اندازه نمی تواند از آن استفاده کند. به دلیل چنین سناریوهایی، وضعیتی به نام تقسیم داخلی وجود دارد و در واقع به ندرت می توانید از تمام قسمت های حافظه به طور کامل استفاده کنید.

برای درک بهتر، سعی کنید برنامه نمونه زیر را کامپایل و اجرا کنید:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char* argv[])
{
        char *ptr[7];
        int n;

        printf("Pid of %s: %d", argv[0], getpid());
        printf("Initial program break : %p", sbrk(0));
        for(n=0; n<5; n++) ptr[n] = malloc(16 * 1024);
        printf("After 5 x 16kB malloc : %p", sbrk(0));
        free(ptr[1]);
        printf("After free of second 16kB : %p", sbrk(0));
        ptr[5] = malloc(16 * 1024);
        printf("After allocating 6th of 16kB : %p", sbrk(0));
        free(ptr[5]);
        printf("After freeing last block : %p", sbrk(0));
        ptr[6] = malloc(18 * 1024);
        printf("After allocating a new 18kB : %p", sbrk(0));
        getchar();
        return 0;
}

هنگامی که برنامه را اجرا می کنید، نتیجه ای مشابه خروجی زیر دریافت خواهید کرد:

Pid of ./a.out: 31990
Initial program break : 0x55ebcadf4000
After 5 x 16kB malloc : 0x55ebcadf4000
After free of second 16kB : 0x55ebcadf4000
After allocating 6th of 16kB : 0x55ebcadf4000
After freeing last block : 0x55ebcadf4000
After allocating a new 18kB : 0x55ebcadf4000

خروجی brk با strace به صورت زیر خواهد بود:

brk(NULL) = 0x5608595b6000
brk(0x5608595d7000) = 0x5608595d7000

همانطور که می بینید 0x21000 به آدرس انتهایی فیلد داده اضافه شده است. شما می توانید این را از مقدار 0x5608595d7000 درک کنید. بنابراین تقریباً 0x21000 یا 132 کیلوبایت حافظه اختصاص داده شد.

مطلب مرتبط:   چگونه اسکنر اثر انگشت خود را با PAM در لینوکس راه اندازی کنیم

در اینجا باید به دو نکته مهم توجه کرد. اولی تخصیص بیش از مقدار مشخص شده در کد نمونه است. دیگری این است که کدام خط کد باعث فراخوانی brk شده است که تخصیص را فراهم می کند.

تصادفی سازی طرح بندی فضای آدرس: ASLR

هنگامی که برنامه مثال بالا را یکی پس از دیگری اجرا می کنید، هر بار مقادیر آدرس متفاوتی را مشاهده خواهید کرد. تغییر فضای آدرس به صورت تصادفی از این طریق کار حملات امنیتی را به طور قابل توجهی پیچیده می کند و امنیت نرم افزار را افزایش می دهد.

با این حال، در معماری های 32 بیتی، به طور کلی از هشت بیت برای تصادفی کردن فضای آدرس استفاده می شود. افزایش تعداد بیت ها مناسب نخواهد بود زیرا ناحیه آدرس پذیر بیش از بیت های باقی مانده بسیار کم خواهد بود. همچنین استفاده از تنها ترکیبات 8 بیتی کار را به اندازه کافی برای مهاجم سخت نمی کند.

از طرف دیگر، در معماری های 64 بیتی، از آنجایی که بیت های زیادی وجود دارد که می توان برای عملکرد ASLR اختصاص داد، تصادفی بسیار بزرگ تری ارائه می شود و درجه امنیت افزایش می یابد.

هسته لینوکس همچنین دستگاه های مبتنی بر اندروید را تامین می کند و ویژگی ASLR در اندروید 4.0.3 و بالاتر به طور کامل فعال می شود. حتی به همین دلیل به تنهایی، اشتباه نیست اگر بگوییم یک گوشی هوشمند 64 بیتی مزیت امنیتی قابل توجهی نسبت به نسخه های 32 بیتی دارد.

با غیرفعال کردن موقت ویژگی ASLR با دستور زیر، به نظر می رسد که برنامه آزمایشی قبلی هر بار که اجرا می شود همان مقادیر آدرس را برمی گرداند:

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

برای بازگرداندن آن به حالت قبلی کافی است به جای 0 در همان فایل 2 بنویسید.

مطلب مرتبط:   8 نکته و ترفند برای اینکه لپ‌تاپ خود را شبیه جدید کنید

دومین تماس سیستمی: mmap

mmap دومین تماس سیستمی است که برای تخصیص حافظه در لینوکس استفاده می شود. با تماس mmap، فضای آزاد در هر ناحیه از حافظه به فضای آدرس فرآیند فراخوانی نگاشت می شود.

در یک تخصیص حافظه که به این روش انجام می شود، وقتی می خواهید پارتیشن دوم 16 کیلوبایتی را با تابع free() در مثال قبلی brk برگردانید، مکانیزمی برای جلوگیری از این عملیات وجود ندارد. بخش حافظه مربوطه از فضای آدرس فرآیند حذف می شود. علامت گذاری شده است که دیگر استفاده نمی شود و به سیستم بازگردانده می شود.

از آنجا که تخصیص حافظه با mmap در مقایسه با موارد با brk بسیار کند است، تخصیص brk مورد نیاز است.

با mmap، هر منطقه آزاد حافظه به فضای آدرس فرآیند نگاشت می شود، بنابراین محتویات فضای اختصاص داده شده قبل از تکمیل این فرآیند، بازنشانی می شود. اگر بازنشانی به این روش انجام نشود، داده‌های متعلق به فرآیندی که قبلاً از ناحیه حافظه مربوطه استفاده می‌کرده نیز می‌تواند توسط فرآیند نامرتبط بعدی قابل دسترسی باشد. این امر صحبت در مورد امنیت در سیستم ها را غیرممکن می کند.

اهمیت تخصیص حافظه در لینوکس

تخصیص حافظه به خصوص در بهینه سازی و مسائل امنیتی بسیار مهم است. همانطور که در مثال های بالا مشاهده شد، عدم درک کامل این موضوع می تواند به معنای از بین بردن امنیت سیستم شما باشد.

حتی مفاهیم مشابه push و pop که در بسیاری از زبان های برنامه نویسی وجود دارد بر اساس عملیات تخصیص حافظه است. توانایی استفاده و تسلط بر حافظه سیستم هم در برنامه نویسی سیستم جاسازی شده و هم در توسعه یک معماری سیستم ایمن و بهینه بسیار حیاتی است.

اگر می خواهید انگشتان پای خود را در توسعه هسته لینوکس غوطه ور کنید، ابتدا به زبان برنامه نویسی C تسلط داشته باشید.