site logo
  • Կայքի մասին
  • Ծրագրավորում
  • Ժեշտ
  • Անվտանգություն
  • Հարց ու Պատասխան (ՀուՊ)
Մարտ 11, 2015  |  By Neutrino In

malloc ֊ի անատոմիան։ Մաս 1, համակարգային կանչեր (system calls):

syscall

Թույլ տվեք առանց երկար֊բարակ նախաբանների միանգամից անցնել գործի 🙂

system call ֊ը ինտերֆեյս է, որը թույլ է user-space ծրագրերին դիմել օպերացիոն համակարգի միջուկին (ring 0 ) և պահանջել նրանից որոշակի գործողությունների կատարում, օրինակ ֆայլ բացել, դրա մեջ ինչ֊որ բաներ գրել, նոր պրոցեսս ստեղծել և այլն։ Այլ կերպ ասած, system call ֊ը միջոց է user mode ֊ից անվտանգ փոխազդելու օպերացիոն համակարգի միջուկի հետ։ Տեխնիկական իմաստով system call ֊ը նշանակում է մեքենայական հրաման, որը բարձրացնում են cpu ֊ի արտոնությունների մակարդակը (privilege level) և փոխանցում է կառավարումը միջուկի կոդի նախապես սահմանված հատվածին։

linux ֊ում համակարգային կանչ իրականացնելու համար կարելի է օգտագործել syscall մեքենայական հրամանը կան 0x80  ընդհատումը (x86 համատեղելի մեքենաների համար)։ Դրանց մի միջև կան որոշակի տարբերություններ, բայց մենք դրանց մեջ չենք խորանա և մեր կոդերում կօգտագործենք 0x80 ընդհատումը։

Յուրաքանչյուր համակարգային կանչ ունի որոշակի անուն և համար, որոնք կարելի է նայել հետևյալ տեղից

/usr/include/asm/unistd_32.h

Բացելով այդ ֆայլը՝ կտեսնենք հետևյալ պատկերը

sys_calls_nums

 

Նկարից պարզ երևում է, որ օրինակ նոր պրոցեսս ստեղծելու համար պետք է համակարգային կանչ կատարել  __NR_fork իդենտիֆիկատորով, կամ ֆայլի մեջ ինչ֊որ բան գրելու համար պետք է օգտագործել __NR_write ֊ը։ Համակարգային կանչերի այս իդենտիֆիկատորները պետք է փոխանցել 0x80 ընդհատմանը։

0x80 ընդհատումը կանչելու համար պետք է նախ ռեգիստորներին փոխանցել որոշակի արժեքներ, թե ինչ արժեքներ և որ ռեգիստորներին, կարող ենք նայել ստորև բերված աղյուսակից․

Syscall # Param 1 Param 2 Param 3 Param 4 Param 5 Param 6
eax ebx ecx edx esi edi ebp
Return value
eax

Աղյուսակից երևում է, որ 0x80 ընդհատման պետք է փոխանցել sys_call֊ի համարը կամ իդենտիֆիկատորը և կանչ կատարելու համար անհրաժեշտ մյուս պարամետրերը։ Վերադարձվող արժեքը պահվում է eax ռեգիստորում։

Եկեք, համակարգային կանչի փոքր օրինակ գրենք։ Օրինակ՝ եկեք էկրանին տպենք դասական “hello world”֊ը։

Դրանից առաջ բերենք write ֆունկցիայի C-կան declaration֊ը․

ssize_t write(int fd, const void *buf, size_t count);

Հիմա կանչենք այս ֆունկցիան օգտագործելով system call:

char *hello = "Hello world\n";
void start()
{
    asm volatile (
            "mov $4,%eax;"
            "mov $1,%ebx;"
            "mov hello,%ecx;"
            "mov $12,%edx;"
            "int $0x80"
            );
}

int main()
{
    start();
    return 0;
}

Այստեղ, քանի որ օգտագործվավծ են ցածր մակարդակի մեքենայական հրամաններ, կոմպիլյատորը կարող է մեզ ոչ ձեռնտու ինչ֊որ օպտիմիզացիաներ կատարել, դրա համար օգտագործում ենք volatile keyword֊ը։

Կոդից պարզ երևում է, որ eax ռեգիստորին փոխանցում ենք __NR_write կանչի համարը (4), ebx ―ին փոխանցում ենք stdout֊ի թիվը (1), ecx ֊ին մեր տողը, edx ֊ին էլ տողի երկարությունը և դրանցից հետո նոր կանչում ենք 0x80 ընդհատումը։

Եկեք compile անենք կոդը և համոզվենք որ ամեն ինչ նորմալ  է։  compile անելու համար օգտագործում ենք հետևյալ հրամանը ․

gcc -o main main.c

Բայց իրականում ամեն ինչ այսքան բարդ չէ և gcc֊ն մեզ հնարավորություն է տալիս էականորեն պարզեցնել մեր կոդը։ Այդ նպատակով կարող ենք օգտագործել հետևյալ կոնստրուկցիան

asm ( assembler template
: output operands
/* optional */
: input operands
/* optional */
: list of clobbered registers /* option
);
The syntax after “:” is:
<constraint> (<C expression>), ....

Այստեղ output ֊ում պահում ենք մեր այն արժեքը որն ուզում ենք վերադարձնել, input֊ում տալիս ենք մեր արգումենտները, իսկ երրորդ պարամետրը անպայման չէ և մենք այն չենք դիտարկի։

Վերն ասվածը ավելի լավ պատկերացնելու համար, բերենք նույն մեր hello world ֊ի կոդը այս անգամ օգտագործելով նոր նկարագրված inline assembler ֊ի գրելաձևը

char hello[] = "Hello world\n";

void start()
{
    int retval;
    asm volatile ("int $0x80"
            : "=a" (retval)
            : "a" (4), "b" (1), "c" (hello), "d" (sizeof(hello)-1));
    asm volatile ("int $0x80" : : "a" (1), "b" (0));
}

int main()
{
    start();
    return 0;
}

Այժմ, երբ պարզ է թէ ինչ է իրենից ներկայացնում system call֊ը անցնենք բուն թեմային։ Նաղքան malloc ֊ին անցնելը, մենք պետք է լուծենք մեկ այլ ֆունկցիայի՝ sbrk()֊ի հարցերը։ Ճիշտն ասած, յուրաքանչյուր իրեն հարգող *nix համակարգի libc ֊ում այդ ֆունկցիան արդեն պատրաստի կա իրականացված, բայց քանի որ վերնագրում արդեն օգտագործել ենք անատոմիա բառը, չենք կարող մինչև վերջ չմերկացնել malloc֊ին 🙂 , հակառակ դեպքում էլ ինչ անատոմիա 🙂 sbrk  ֆունկցիան հանդիսանում է *nix համակարգերի memory managment֊ի հիմնական երկու ֆունկցիաներից մեկը (մյուսն է brk ) և նախատեսված է heap ֊ից հիշողություն պոկելու համար և այնուհետև ազատ հիշողությանը կցելու համար։ Ընդ֊որում heap֊ում հիշողության փոփոխությանը կից տեղի է ունենում նաև այսպես կոչված program break֊ի փոփոխություն։ program break ֊ը իրենից ներկայացնում է heap ֊ի ընթացիկ գագաթը։

 

heap

Հետևաբար այդ գագաթի փոփոխությունն էլ կնշանակի հիշողության աճ կամ կրճատում։ Հենց դրա համար էլ նախատեսված է sbrk ֆունկցիան։ Ընդ֊որում heap ֊ի փնթացիկ գագաթը կարող ենք ստանալ sbrk() ֊ին տալով 0 արգումենտը՝

void* start_addr = sbrk(0);

Հիմա, երբ արդեն ունենք heap֊ի գագաթի հասցեն, կարող ենք աճեցնել որոշակի չափով՝ գումարելով այդ հասցեին մեր ուզած չափը

void* new_ptr = sbrk((intptr_t)start_addr + size);

Փաստորեն heap ֊ից հիշողություն ուզելու ընդհանուր ալգորիթմը հետևյալն է (***)․

  1. ստանալ heap֊ի ընթացիկ գագաթի հասցեն start_addr, կանչելով sbrk(0),
  2. վերցնել start_addr + size չափանի հիշողություն։

Մեզ մնում է միայն գրել կոդերը։ Բայց նախ և առաջ կգրենք system_call֊ի կոդը․

int __syscall1( int number, intptr_t p1 ){
    int ret;
    asm volatile ("int $0x80" : "=a" (ret) : "a" (number), "b" (p1));
    return ret;
}

Ամեն ինչ շատ պարզ է, տալիս ենք համակարգային կանչի համարը կամ իդենտիֆիկատորը (sbrk֊ի համար այն __NR_brk է) ու հիշողության չափը որքան որ ուզում ենք վերցնել heap֊ից։ Վերադարձվող արժեքը պահում ենք ret փոփոխականում, որն էլ վերադարձնում ենք ֆունկցիայից։

Հիմա գրենք բուն sbrk() ֆունկցիան, համաձայն (***) ալգորիթմի

void* __sbrk__(intptr_t increment)
{
    /*
     * Calling sbrk() with an increment of 0 can be used to find the current 
     * location of the program break.
     * The program break is the first location after the end of the 
     * uninitialized data segment. Program break means the top of the Heap). 
     * Increasing the program break has the effect of allocating memory 
     * to the process; decreasing the break deallocates memory.
      */
    void *new, *old = (void *)__syscall1(__NR_brk, 0);
    new = (void *)__syscall1(__NR_brk, ((uintptr_t)old) + increment);
    return (((uintptr_t)new) == (((uintptr_t)old) + increment)) ? old :
        (void *)-1;
}

Չնայած ամեն ինչ արդեն գրել եմ և կոդի մեկնաբանություններում էլ լավ նկարագրված է, բայց ամեն դեպքում մի անգամ էլ անցնենք ֆունկցիայի մարմնի վրայով։ Առագին տողով՝ կանչելով մեր գրած __sbrk__ ֆունկցիան, ստանում ենք heap ֊ի գագաթի հասցեն և արդեն sbrk֊ի երկրորդ կանչով վերցնում ենք մեզ անհրաժեշտ հիշողությունը։ Եթե heap ֊ում այլևս չկա ազատ հիշողություն, ապա վերադարձնում ենք -1: Այսքանը համակարգային կանչերի  և malloc֊ի implementation֊ի համար երևի թե ամենակրիտիկական թեմայից։ Մյուս մասում, որը անպայման կլինի, կգրենք malloc ֊ն ու free ֊ն 🙂 Ամբողջ կոդերը կարող ենք նայել այստեղից :

malloc ֊ի անատոմիան։ Մաս 1, համակարգային կանչեր (system calls):, 9.6 out of 10 based on 12 ratings
*Nix-եր Assembler C և C++ heap malloc memory management system call Ժեշտ Օպերացիոն համակարգեր
Previous StoryՊատմություն մեծ֊մեծ ֆայլերի մասին
Next Storymalloc ֊ի անատոմիան։ Մաս 2, malloc ֊ի իրականացումը։

Comments: no replies

Join in: leave your comment Cancel Reply

(will not be shared)

Որոնում

Նշագրեր

*Nix-եր (18) android (17) C++ (19) C և C++ (27) Excel (10) html (10) Network Administration (16) System Administration (28) Windows 7 (14) Ալգորիթմներ (15) Անվտանգություն (29) ԳՆՈՒ/Լինուքս (16) Թեյնիկներին (57) Ժեշտ (44) Լակոնիկ (21) Լինուքս/Յունիքս հրամաններ (29) Լուսանկարչություն և մշակում (15) Խելախոսներ (19) Ծրագրավորման լեզուներ (16) Ծրագրավորում (64) Ծրագրեր (48) Հայականացում (28) Հումոր (11) Ուսումնական նյութեր (34) Սոցցանցային Հմտություններ (19) Վեբ (25) Վերլուծություն (10) Վորդպրես (21) ՏՏ և փիլիսոփայություն (21) Տվյալների բազաներ (12) Օպերացիոն համակարգեր (27) Օֆիսային ծրագրեր (22) անդրոիդ (16) բաշ (10) ինտերնետ (11) խելախոսներ (13) համացանց (15) հայատառ (10) հայերեն (11) հայերեն ստեղնաշար (11) հայկական սոֆթ (11) ստեղնաշար (10) սքրիփթ (14) վինդոուս (12) տեսանյութ (23)
Copyright ©2017 ThemeFuse. All Rights Reserved