Как написать свою оперативную систему. Давай напишем ядро! Создаем простейшее рабочее ядро операционной системы

рТЙЧЕФУФЧХА ЧУЕИ УЧПЙИ ЮЙФБФЕМЕК!

рТЕДЩДХЭЙЕ ЧЩРХУЛЙ НПЗМЙ ВЩФШ ОЕУЛПМШЛП ЪБРХФБООЩНЙ. оБЮБМШОБС ЪБЗТХЪЛБ, Assembler, BIOS. уЕЗПДОС НЩ ОБЛПОЕГ РЕТЕИПДЙН Л ВПМЕЕ ЙОФЕТЕУОПК Й РПОСФОПК ЮБУФЙ - НЩ ОБЮЙОБЕН РЙУБФШ СДТП. й РЙУБФШ НЩ ЕЗП ВХДЕН ОБ СЪЩЛЕ ЧЩУПЛПЗП ХТПЧОС уЙ.

ч ОБЮБМШОЩК ЪБЗТХЪЮЙЛ ПУФБМПУШ ЧОЕУФЙ ЧУЕЗП РБТХ ДПРПМОЕОЙК Й ПО ВХДЕФ РПМОПУФША ЗПФПЧ ЗТХЪЙФШ МАВЩЕ 32-ВЙФОЩЕ СДТБ.

пРТЕДЕМЕОЙЕ ПВЯЈНБ ПРЕТБФЙЧОПК РБНСФЙ

лПОЕЮОП, НПЦОП РПДУЮЙФБФШ ПВЯЈН РБНСФЙ ЧТХЮОХА Ч СДТЕ - РЕТЕВЙТБФШ БДТЕУБ ПФ 0x100000 Й РЩФБФШУС ЪБРЙУБФШ ФХДБ ЪОБЮЕОЙЕ ПФМЙЮОПЕ ПФ ОХМС Й 0xFF. еУМЙ РТЙ ЮФЕОЙЙ НЩ РПМХЮБЕН РПМХЮЕООПЕ ЪОБЮЕОЙЕ, ФП ЧУЈ ИПТПЫП, ЙОБЮЕ РБНСФШ ЛПОЮЙМБУШ - ЪБРПНЙОБЕН БДТЕУ РПУМЕДОЕЗП ХДБЮОПЗП ЮФЕОЙС, ЬФП Й ВХДЕФ ПВЯЈНПН ПРЕТБФЙЧОПК РБНСФЙ. пДОБЛП ФБЛПК УРПУПВ ЙНЕЕФ ДЧБ ОЕДПУФБФЛБ:

1) еЗП УМЕДХЕФ ЙУРПМШЪПЧБФШ ДП ЧЛМАЮЕОЙС УФТБОЙЮОПК БДТЕУБГЙЙ, ЮФПВЩ ЙНЕФШ ДПУФХР ЛП ЧУЕК ЖЙЪЙЮЕУЛПК РБНСФЙ, МЙВП ХУФТБЙЧБФШ ЪБРЙУШ ЮЕТЕЪ "ПЛОП" ЧТЕНЕООПК УФТБОЙГЩ. мЙЫОСС ФТБФБ ЧТЕНЕОЙ, РТЙ ХУМПЧЙЙ, ЮФП ФЕУФЙТПЧБОЙЕ РБНСФЙ BIOS Й ФБЛ ЧЩРПМОСЕФ РТЙ ОБЮБМШОПК ЙОЙГЙБМЙЪБГЙЙ, Б НЩ ДЕМБЕН ДЧПКОХА ТБВПФХ.

2) чУЈ ИПТПЫП РПЛБ РБНСФШ РТЕДУФБЧМСЕФ УПВПК ОЕРТЕТЩЧОЩК ХЮБУФПЛ БДТЕУПЧ, ОП ОБ УПЧТЕНЕООЩИ УЙУФЕНБИ У ВПМШЫЙН ПВЯЈНПН РБНСФЙ ЬФП РТБЧЙМП НПЦЕФ ВЩФШ ОБТХЫЕОП. л ФПНХ ЦЕ BIOS РЙЫЕФ Ч УБНХА ПВЩЮОХА РБНСФШ ФБВМЙГЩ ACPI, ЛПФПТЩЕ РТЙЗПДСФУС ПРЕТБГЙПООПК УЙУФЕНЕ Й ОЕ УФПЙФ ЙИ ЪБФЙТБФШ ДП РТПЮФЕОЙС.

йЪ ЬФПЗП УМЕДХЕФ, ЮФП МХЮЫЕ УРТПУЙФШ РТП ПВЯЈН ПРЕТБФЙЧОПК РБНСФЙ Х BIOS, ВМБЗП ПО РТЕДПУФБЧМСЕФ ЧУЕ ОЕПВИПДЙНЩЕ ЖХОЛГЙЙ.

йУФПТЙЮЕУЛЙ РЕТЧПК ЖХОЛГЙЕК ПРТЕДЕМЕОЙС ПВЯЈНБ ПРЕТБФЙЧОПК РБНСФЙ ВЩМП РТЕТЩЧБОЙЕ 0x12. пОП ОЕ РТЙОЙНБЕФ ОЙЛБЛЙИ ЧИПДОЩИ РБТБНЕФТПЧ, Ч ОБ ЧЩИПДЕ Ч ТЕЗЙУФТЕ AX УПДЕТЦЙФУС ТБЪНЕТ ВБЪПЧПК РБНСФЙ Ч ЛЙМПВБКФБИ. вБЪПЧБС РБНСФШ - ФЕ УБНЩЕ 640 лв ДПУФХРОЩЕ Ч ТЕБМШОПН ТЕЦЙНЕ. уЕКЮБУ ЧЩ ХЦЕ ОЕ УНПЦЕФЕ ОБКФЙ ЛПНРШАФЕТ, ЗДЕ ВЩ ВЩМП НЕОЕЕ 640 лв РБНСФЙ, ОП НБМП МЙ. йУРПМШЪПЧБФШ ЕЈ ОБН УНЩУМБ ОЕФ - ЕУМЙ РТПГЕУУПТ РПДДЕТЦЙЧБЕФ ЪБЭЙЭЈООЩК ТЕЦЙН, ФП ЧТСД МЙ Х ОЕЗП ВХДЕФ НЕОШЫЕ ОЕУЛПМШЛЙИ НЕЗБВБКФ РБНСФЙ.

пВЯЈНЩ РБНСФЙ ТПУМЙ Й 640 лв УФБМП НБМП. фПЗДБ РПСЧЙМБУШ ОПЧБС ЖХОЛГЙС - РТЕТЩЧБОЙЕ 0x15 AH=0x88. пОБ ЧПЪЧТБЭБЕФ Ч AX ТБЪНЕТ ТБУЫЙТЕООПК РБНСФЙ (УЧЩЫЕ 1 нв) Ч ЛЙМПВБКФБИ Ч AX. ьФБ ЖХОЛГЙС ОЕ НПЦЕФ ЧПЪЧТБЭБФШ ЪОБЮЕОЙС ВПМШЫЕ 15 нв (15 + 1 ЙФПЗП 16 нв).

лПЗДБ Й 16 нв УФБМП ОЕДПУФБФПЮОП РПСЧЙМБУШ ОПЧБС ЖХОЛГЙС - РТЕТЩЧБОЙЕ 0x15, AX=0xE801. пОБ ЧПЪЧТБЭБЕФ ТЕЪХМШФБФЩ БЦ Ч 4 ТЕЗЙУФТБИ:

AX - ТБЪНЕТ ТБУЫЙТЕООПК РБНСФЙ ДП 16 нв Ч ЛЙМПВБКФБИ
BX - ТБЪНЕТ ТБУЫЙТЕООПК РБНСФЙ УЧЕТИ 16 нв Л ВМПЛБИ РП 64 лв
CX - ТБЪНЕТ УЛПОЖЙЗХТЙТПЧБООПК ТБУЫЙТЕООПК РБНСФЙ ДП 16 нв Ч ЛЙМПВБКФБИ
DX - ТБЪНЕТ УЛПОЖЙЗХТЙТПЧБООПК ТБУЫЙТЕООПК РБНСФЙ УЧЕТИ 16 нв Ч ВМПЛБИ РП 64 лв

юФП ФБЛПЕ "УЛПОЖЙЗХТЙТПЧБООБС" РБНСФШ РТПЙЪЧПДЙФЕМЙ BIOS УХДС РП ЧУЕНХ ОЕ ДПЗПЧПТЙМЙУШ, РПЬФПНХ ОБДП РТПУФП, ЕУМЙ Ч AX Й BX ОХМЙ, ВТБФШ ЪОБЮЕОЙЕ ЙЪ CX Й DX.

оП Й ЬФПЗП ПЛБЪБМПУШ НБМП. чЕДШ ЧУЕ РЕТЕЮЙУМЕООЩЕ ЧЩЫЕ ЖХОЛГЙЙ ЙНЕАФ ПЗТБОЙЮЕОЙЕ ПВЯЈНБ РБНСФЙ Ч 4 зв, Л ФПНХ ЦЕ ОЕ ХЮЙФЩЧБАФ ФП, ЮФП РБНСФШ НПЦЕФ ВЩФШ ОЕ ОЕРТЕТЩЧОЩН ВМПЛПН. рПЬФПНХ Ч ОПЧЩИ BIOS РПСЧЙМБУШ ЕЭЈ ПДОБ ЖХОЛГЙС - РТЕТЩЧБОЙЕ 0x15, AX=0xE820. пОБ ЧПЪЧТБЭБЕФ ОЕ РТПУФП ЮЙУМП, Б ЛБТФХ РБНСФЙ. чИПДОЩЕ РБТБНЕФТЩ:

EAX=0xE820
EDX=0x534D4150 ("SMAP")
EBX - УНЕЭЕОЙЕ ПФ ОБЮБМБ ЛБТФЩ РБНСФЙ (ДМС ОБЮБМБ 0)
ECX - ТБЪНЕТ ВХЖЕТБ (ЛБЛ РТБЧЙМП 24 ВБКФБ - ТБЪНЕТ ПДОПЗП ЬМЕНЕОФБ)
ES:DI - БДТЕУ ВХЖЕТБ, ЛХДБ ОБДП ЪБРЙУБФШ ПЮЕТЕДОПК ЬМЕНЕОФ

чЩИПДОЩЕ РБТБНЕФТЩ:

EAX=0x534D4150 ("SMAP")
EBX - ОПЧПЕ УНЕЭЕОЙЕ ДМС УМЕДХАЭЕЗП ЧЩЪПЧБ ЖХОЛГЙЙ. еУМЙ 0, ФП ЧУС ЛБТФБ РБНСФЙ РТПЮЙФБОБ
ECX - ЛПМЙЮЕУФЧП ТЕБМШОП ЧПЪЧТБЭЈООЩИ ВБКФ (20 ЙМЙ 24 ВБКФБ)
ч ХЛБЪБООПН ВХЖЕТЕ УПДЕТЦЙФУС ПЮЕТЕДОПК ЬМЕНЕОФ ЛБТФЩ РБНСФЙ.

лБЦДЩК ЬМЕНЕОФ ЛБТФЩ РБНСФЙ ЙНЕЕФ УМЕДХАЭХА УФТХЛФХТХ (ОБРЙЫХ Ч УЙОФБЛУЙУЕ уЙ, РПФПНХ ЮФП ТБЪВПТ ДБООЩИ НЩ ВХДЕН ДЕМБФШ ХЦЕ Ч СДТЕ):

Struct { unsigned long long base; //вБЪПЧЩК ЖЙЪЙЮЕУЛЙК БДТЕУ ТЕЗЙПОБ unsigned long long length; //тБЪНЕТ ТЕЗЙПОБ Ч ВБКФБИ unsigned long type; // фЙР ТЕЗЙПОБ unsigned long acpi_attrs; //тБУЫЙТЕООЩЕ БФТЙВХФЩ ACPI };

рПУМЕДОЙК ЬМЕНЕОФ УФТХЛФХТЩ ОЕ ПВСЪБФЕМЕО. еЭЈ Ч ПДОПН ЙУФПЮОЙЛЕ ЧЙДЕМ, ЮФП РЕТЕД ЪБРТПУПН ЬМЕНЕОФБ УФПЙФ РПНЕУФЙФШ ФХДБ ЕДЙОЙЮЛХ. лПОЕЮОП, УЕКЮБУ НЩ ОЕ РПДДЕТЦЙЧБЕН ACPI, ОП МХЮЫЕ ЪБТБОЕЕ РПЪБВПФЙФУС П ФПН, ЮФПВЩ РПМХЮЙФШ ЛБЛ НПЦОП ВПМШЫЕ ДБООЩИ. ч ПФМЙЮЙЙ ПФ РБТБНЕФТПЧ РБНСФЙ, ЧУЈ ПУФБМШОПЕ НПЦОП МЕЗЛП ХЪОБФШ Й ЙЪ ЪБЭЙЭЈООПЗП ТЕЦЙНБ ОБРТСНХА, ВЕЪ BIOS.

тЕЗЙПОЩ РБНСФЙ, ПРЙУЩЧБЕНЩЕ ЛБТФПК, НПЗХФ ВЩФШ ОЕУЛПМШЛЙИ ФЙРПЧ:

1 - пВЩЮОБС РБНСФШ. нПЦЕФ ВЩФШ УЧПВПДОП ЙУРПМШЪПЧБОБ пу ДМС УЧПЙИ ГЕМЕК. рПЛБ НЩ ФПМШЛП Л ОЕК Й ВХДЕН ПВТБЭБФШУС, Б ЧУЈ ПУФБМШОПЕ РТПРХУЛБФШ.
2 - ъБТЕЪЕТЧЙТПЧБОП (ОБРТЙНЕТ, ЛПД BIOS). ьФБ РБНСФШ НПЦЕФ ВЩФШ ЛБЛ ЖЙЪЙЮЕУЛЙ ОЕДПУФХРОБ ДМС ЪБРЙУЙ, ФБЛ Й РТПУФП ЪБРЙУШ ФХДБ ОЕЦЕМБФЕМШОБ. фБЛХА РБНСФШ МХЮЫЕ ОЕ ФТПЗБФШ.
3 - дПУФХРОП РПУМЕ РТПЮФЕОЙС ФБВМЙГ ACPI. чЕТПСФОП, ЙНЕООП Ч ЬФЙИ ВМПЛБИ ЬФЙ ФБВМЙГЩ Й ИТБОСФУС. рПЛБ ДТБКЧЕТ ACPI ОЕ РТПЮЙФБЕФ ФБВМЙГЩ, ЬФХ РБНСФШ МХЮЫЕ ОЕ ФТПЗБФШ. рПФПН НПЦОП ЙУРПМШЪПЧБФШ ФБЛ ЦЕ, ЛБЛ Й РБНСФШ ФЙРБ 1.
4 - ьФХ РБНСФШ УМЕДХЕФ УПИТБОСФШ НЕЦДХ NVS УЕУУЙСНЙ. фБЛХА РБНСФШ НЩ ФТПЗБФШ ОЕ ВХДЕН, РПЛБ ОЕ ХЪОБЕН, ЮФП ФБЛПЕ NVS УЕУУЙЙ:-)

оЕ ЧУЕ BIOS НПЗХФ РПДДЕТЦЙЧБФШ ЬФХ ЖХОЛГЙА. еУМЙ ЛБЛБС-ФП ЖХОЛГЙС ОЕ РПДДЕТЦЙЧБЕФУС, ФП РТЙ ЧЩИПДЕ ЙЪ ОЕЈ ХУФБОПЧМЕО ЖМБЗ РЕТЕРПМОЕОЙС Й УМЕДХЕФ ПВТБЭБФШУС Л ВПМЕЕ УФБТПК. нЩ ВХДЕН ЙУРПМШЪПЧБФШ ЖПТНБФ ЛБТФЩ РБНСФЙ ЖХОЛГЙЙ 0xE820. еУМЙ УБНХ ЬФХ ЖХОЛГЙА ЧЩЪЧБФШ ОЕ РПМХЮЙМПУШ - РПМХЮБФШ ПВЯЈН РБНСФЙ ПВЩЮОЩНЙ УТЕДУФЧБНЙ Й УПЪДБЧБФШ УЧПА УПВУФЧЕООХА ЛБТФХ РБНСФЙ ЙЪ ПДОПЗП ЬМЕНЕОФБ. рПУЛПМШЛХ ПРТЕДЕМЕОЙЕ ПВЯЈНБ РБНСФЙ ЪБДБЮБ ОХЦОБС Й ДМС ЪБРХУЛБ 32-ВЙФОПЗП Й ДМС ЪБРХУЛБ 64-ВЙФОПЗП СДТБ, МХЮЫЕ ПЖПТНЙФШ ЕЈ Ч ЧЙДЕ РПДРТПЗТБННЩ. лБТФХ РБНСФЙ ТБЪНЕУФЙН РП БДТЕУХ 0x7000. оЕ ДХНБА, ЮФП ПОБ НПЦЕФ ВЩФШ ВПМШЫЕ РБТЩ ЛЙМПВБКФ. рПУМЕДОЙК ЬМЕНЕОФ ЧТХЮОХА УДЕМБЕН ФЙРБ 0 - ФБЛПЗП ФЙРБ ОЕ ЧПЪЧТБЭБЕФ BIOS Й ЬФП Й ВХДЕФ РТЙЪОБЛПН ЛПОГБ.

; рПМХЮЕОЙЕ ЛБТФЩ РБНСФЙ get_memory_map: mov di, 0x7000 xor ebx, ebx @: mov eax, 0xE820 mov edx, 0x534D4150 mov ecx, 24 mov dword, 1 int 0x15 jc @f add di, 24 test ebx, ebx jnz @b @: cmp di, 0x7000 ja .ok mov dword, 0x100000 mov dword, 0 mov dword, 0 mov dword, 1 mov dword, 0 mov ax, 0xE801 int 0x15 jnc @f mov ah, 0x88 int 0x15 jc .ok mov cx, ax xor dx, dx @: test cx, cx jz @f mov ax, cx mov bx, dx @: movzx eax, ax movzx ebx, bx mov ecx, 1024 mul ecx push eax mov eax, ebx mov ecx, 65536 mul ecx pop edx add eax, edx mov , eax add di, 24 jmp .ok .ok: xor ax, ax mov cx, 24 / 2 rep stosw ret

оХ ЧПФ Й ЗПФПЧ ОБЫ ОБЮБМШОЩК ЪБЗТХЪЮЙЛ ДМС 32-ВЙФОЩИ СДЕТ. ч ЪБЛМАЮЕОЙЕ РТЙЧПЦХ ЕЗП РПМОЩК ЛПД Й НЩ РЕТЕКДЈН Л СДТХ.

; оБЮБМШОЩК ЪБЗТХЪЮЙЛ СДТБ ДМС БТИЙФЕЛФХТЩ x86 format Binary as "bin" org 0x7C00 jmp boot ; ъБЗПМПЧПЛ ListFS align 4 fs_magic dd ? fs_version dd ? fs_flags dd ? fs_base dq ? fs_size dq ? fs_map_base dq ? fs_map_size dq ? fs_first_file dq ? fs_uid dq ? fs_block_size dd ? ; ъБЗПМПЧПЛ ЖБКМБ virtual at 0x800 f_info: f_name rb 256 f_next dq ? f_prev dq ? f_parent dq ? f_flags dq ? f_data dq ? f_size dq ? f_ctime dq ? f_mtime dq ? f_atime dq ? end virtual ; дБООЩЕ ОБЮБМШОПЗП ЪБЗТХЪЮЙЛБ label sector_per_track word at $$ label head_count byte at $$ + 2 label disk_id byte at $$ + 3 reboot_msg db "Press any key...",13,10,0 boot_file_name db "boot.bin",0 ; чЩЧПД УФТПЛЙ DS:SI ОБ ЬЛТБО write_str: push si mov ah, 0x0E @: lodsb test al, al jz @f int 0x10 jmp @b @: pop si ret ; лТЙФЙЮЕУЛБС ПЫЙВЛБ error: pop si call write_str ; рЕТЕЪБЗТХЪЛБ reboot: mov si, reboot_msg call write_str xor ah, ah int 0x16 jmp 0xFFFF:0 ; ъБЗТХЪЛБ УЕЛФПТБ DX:AX Ч ВХЖЕТ ES:DI load_sector: push dx add ax, word adc dx, word cmp byte, 0xFF je .use_EDD push bx cx si div mov cl, dl inc cl div mov dh, ah mov ch, al mov dl, mov bx, di mov al, 1 mov si, 3 @: mov ah, 2 int 0x13 jnc @f xor ah, ah int 0x13 dec si jnz @b .error: call error db "DISK ERROR",13,10,0 @: pop si cx bx dx ret .use_EDD: push si mov byte, 0x10 mov byte, 0 mov word, 1 mov , di push es pop word mov , ax mov , dx mov word, 0 mov word, 0 mov ah, 0x42 mov dl, mov si, 0x600 int 0x13 jc .error pop si dx ret ; рПЙУЛ ЖБКМБ У ЙНЕОЕН DS:SI Ч ЛБФБМПЗЕ DX:AX find_file: push cx dx di .find: cmp ax, -1 jne @f cmp dx, -1 jne @f .not_found: call error db "NOT FOUND",13,10,0 @: mov di, f_info call load_sector push di mov cx, 0xFFFF xor al, al repne scasb neg cx dec cx pop di push si repe cmpsb pop si je .found mov ax, word mov dx, word jmp .find .found: pop di dx cx ret ; ъБЗТХЪЛБ ФЕЛХЭЕЗП ЖБКМБ Ч РБНСФШ РП БДТЕУХ BX:0. лПМЙЮЕУФЧП ЪБЗТХЦЕООЩИ УЕЛФПТПЧ ЧПЪЧТБЭБЕФУС Ч AX load_file_data: push bx cx dx si di mov ax, word mov dx, word .load_list: cmp ax, -1 jne @f cmp dx, -1 jne @f .file_end: pop di si dx cx mov ax, bx pop bx sub ax, bx shr ax, 9 - 4 ret @: mov di, 0x8000 / 16 call load_sector mov si, di mov cx, 512 / 8 - 1 .load_sector: lodsw mov dx, add si, 6 cmp ax, -1 jne @f cmp dx, -1 je .file_end @: push es mov es, bx xor di, di call load_sector add bx, 0x200 / 16 pop es loop .load_sector lodsw mov dx, jmp .load_list ; фПЮЛБ ЧИПДБ Ч ОБЮБМШОЩК ЪБЗТХЪЮЙЛ boot: ; оБУФТПЙН УЕЗНЕОФОЩЕ ТЕЗЙУФТЩ jmp 0:@f @: mov ax, cs mov ds, ax mov es, ax ; оБУФТПЙН УФЕЛ mov ss, ax mov sp, $$ ; тБЪТЕЫЙН РТЕТЩЧБОЙС sti ; ъБРПНОЙН ОПНЕТ ЪБЗТХЪПЮОПЗП ДЙУЛБ mov , dl ; пРТЕДЕМЙН РБТБНЕФТЩ ЪБЗТХЪПЮОПЗП ДЙУЛБ mov ah, 0x41 mov bx, 0x55AA int 0x13 jc @f mov byte, 0xFF jmp .disk_detected @: mov ah, 0x08 xor di, di push es int 0x13 pop es jc load_sector.error inc dh mov , dh and cx, 111111b mov , cx .disk_detected: ; ъБЗТХЪЙН РТПДПМЦЕОЙЕ ОБЮБМШОПЗП ЪБЗТХЪЮЙЛБ mov si, boot_file_name mov ax, word mov dx, word call find_file mov bx, 0x7E00 / 16 call load_file_data ; рЕТЕИПДЙН ОБ РТПДПМЦЕОЙЕ jmp boot2 ; рХУФПЕ РТПУФТБОУФЧП Й УЙЗОБФХТБ rb 510 - ($ - $$) db 0x55,0xAA ; дПРПМОЙФЕМШОЩЕ ДБООЩЕ ЪБЗТХЪЮЙЛБ load_msg_preffix db "Loading "",0 load_msg_suffix db ""...",0 ok_msg db "OK",13,10,0 config_file_name db "boot.cfg",0 start16_msg db "Starting 16 bit kernel...",13,10,0 start32_msg db "Starting 32 bit kernel...",13,10,0 label module_list at 0x6000 label memory_map at 0x7000 ; тБЪВЙЕОЙЕ УФТПЛЙ DS:SI РП УЙНЧПМХ УМЕЫБ split_file_name: push si @: lodsb cmp al, "/" je @f test al, al jz @f jmp @b @: mov byte, 0 mov ax, si pop si ret ; ъБЗТХЪЛБ ЖБКМБ У ЙНЕОЕН DS:SI Ч ВХЖЕТ BX:0. тБЪНЕТ ЖБКМБ Ч УЕЛФПТБИ ЧПЪЧТБЭБЕФУС Ч AX load_file: push si mov si, load_msg_preffix call write_str pop si call write_str push si mov si, load_msg_suffix call write_str pop si push si bp mov dx, word mov ax, word @: push ax call split_file_name mov bp, ax pop ax call find_file test byte, 1 jz @f mov si, bp mov dx, word mov ax, word jmp @b @: call load_file_data mov si, ok_msg call write_str pop bp si ret ; рПМХЮЕОЙЕ ЛБТФЩ РБНСФЙ get_memory_map: mov di, memory_map xor ebx, ebx @: mov eax, 0xE820 mov edx, 0x534D4150 mov ecx, 24 mov dword, 1 int 0x15 jc @f add di, 24 test ebx, ebx jnz @b @: cmp di, 0x7000 ja .ok mov dword, 0x100000 mov dword, 0 mov dword, 0 mov dword, 1 mov dword, 0 mov ax, 0xE801 int 0x15 jnc @f mov ah, 0x88 int 0x15 jc .ok mov cx, ax xor dx, dx @: test cx, cx jz @f mov ax, cx mov bx, dx @: movzx eax, ax movzx ebx, bx mov ecx, 1024 mul ecx push eax mov eax, ebx mov ecx, 65536 mul ecx pop edx add eax, edx mov , eax add di, 24 jmp .ok .ok: xor ax, ax mov cx, 24 / 2 rep stosw ret ; рТПДПМЦЕОЙЕ ОБЮБМШОПЗП ЪБЗТХЪЮЙЛБ boot2: ; ъБЗТХЪЙН ЛПОЖЙЗХТБГЙПООЩК ЖБКМ ЪБЗТХЪЮЙЛБ mov si, config_file_name mov bx, 0x1000 / 16 call load_file ; чЩРПМОЙН ЪБЗТХЪПЮОЩК УЛТЙРФ mov bx, 0x9000 / 16 mov bp, module_list mov dx, 0x1000 .parse_line: mov si, dx .parse_char: lodsb test al, al jz .config_end cmp al, 10 je .run_command cmp al, 13 je .run_command jmp .parse_char .run_command: mov byte, 0 xchg dx, si cmp byte, 0 je .parse_line ; рХУФБС УФТПЛБ cmp byte, "#" je .parse_line ; лПННЕОФБТЙК cmp byte, "L" je .load_file ; ъБЗТХЪЛБ ЖБКМБ cmp byte, "S" je .start ; ъБРХУЛ СДТБ; оЕЙЪЧЕУФОБС ЛПНБОДБ mov al, mov [.cmd], al call error db "Unknown boot script command "" .cmd db ? db ""!",13,10,0 .config_end: ; рТЙ РТБЧЙМШОПН ЛПОЖЙЗХТБГЙПООПН ЖБКМЕ НЩ ОЕ ДПМЦОЩ УАДБ РПРБУФШ; ъБЧЕТЫЕОЙЕ jmp reboot ; ъБЗТХЪЛБ ЖБКМБ.load_file: push dx inc si call load_file push ax mov cx, 512 mul cx mov word, ax mov word, dx mov word, 0 mov word, 0 mov ax, bx mov cx, 16 mul cx mov word, ax mov word, dx mov word, 0 mov word, 0 pop ax shr ax, 9 - 4 add bx, ax add bp, 16 pop dx jmp .parse_line ; ъБРХУЛ СДТБ.start: ; рТПЧЕТЙН, ЮФП ЪБЗТХЦЕО ИПФС ВЩ ПДЙО ЖБКМ cmp bx, 0x9000 / 16 ja @f call error db "NO KERNEL LOADED",13,10,0 @: ; ъБРПМОСЕН РПУМЕДОЙК ЬМЕНЕОФ УРЙУЛБ ЖБКМПЧ xor ax, ax mov cx, 16 mov di, bp rep stosw ; рЕТЕИПДЙН Л РТПГЕДХТЕ ЙОЙГЙБМЙЪБГЙЙ СДТБ ДМС ОХЦОПК ТБЪТСДОПУФЙ inc si cmp word, "16" je .start16 cmp word, "32" je .start32 ;cmp word, "64" ;je .start64 ; оЕЙЪЧЕУФОБС ТСЪТСДОПУФШ СДТБ call error db "Invalid start command argument",13,10,0 ; ъБРХУЛ 16-ТБЪТСДОПЗП СДТБ.start16: mov si, start16_msg mov bx, module_list mov dl, jmp 0x9000 ; ъБРХУЛ 32-ТБЪТСДОПЗП СДТБ.start32: ; чЩЧПДЙН ХЧЕДПНМЕОЙЕ П ЪБРХУЛЕ 32-ВЙФОПЗП СДТБ mov si, start32_msg call write_str ; рТПЧЕТЙН, ЮФП РТПГЕУУПТ ОЕ ИХЦЕ i386 mov ax, 0x7202 push ax popf pushf pop bx cmp ax, bx je @f call error db "Required i386 or better",13,10,0 @: ; рПМХЮЙН ЛБТФХ РБНСФЙ call get_memory_map ; пЮЙУФЙН ФБВМЙГЩ УФТБОЙГ xor ax, ax mov cx, 3 * 4096 / 2 mov di, 0x1000 rep stosw ; ъБРПМОЙН ЛБФБМПЗ УФТБОЙГ mov word, 0x2000 + 111b mov word, 0x3000 + 111b ; ъБРПМОЙН РЕТЧХА ФБВМЙГХ УФТБОЙГ mov eax, 11b mov cx, 0x100000 / 4096 mov di, 0x2000 @: stosd add eax, 0x1000 loop @b ; ъБРПМОЙН РПУМЕДОАА ФБВМЙГХ УФТБОЙГ mov di, 0x3000 mov eax, dword or eax, 11b mov ecx, dword shr ecx, 12 @: stosd add eax, 0x1000 loop @b mov word, 0x4000 + 11b ; Kernel stack mov word, 0x3000 + 11b ; Kernel page table ; ъБЗТХЪЙН ЪОБЮЕОЙЕ Ч CR3 mov eax, 0x1000 mov cr3, eax ; ъБЗТХЪЙН ЪОБЮЕОЙЕ Ч GDTR lgdt ; ъБРТЕФЙН РТЕТЩЧБОЙС cli ; рЕТЕКДЈН Ч ЪБЭЙЭЈООЩК ТЕЦЙН mov eax, cr0 or eax, 0x80000001 mov cr0, eax ; рЕТЕКДЈН ОБ 32-ВЙФОЩК ЛПД jmp 8:start32 ; фБВМЙГБ ДЕУЛТЙРФПТПЧ УЕЗНЕОФПЧ ДМС 32-ВЙФОПЗП СДТБ align 16 gdt32: dq 0 ; NULL - 0 dq 0x00CF9A000000FFFF ; CODE - 8 dq 0x00CF92000000FFFF ; DATA - 16 gdtr32: dw $ - gdt32 - 1 dd gdt32 ; 32-ВЙФОЩК ЛПД use32 start32: ; оБУФТПЙН УЕЗНЕОФОЩЕ ТЕЗЙУФТЩ Й УФЕЛ mov eax, 16 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax mov esp, 0xFFFFDFFC ; рПНЕУФЙН Ч DL ОПНЕТ ЪБЗТХЪПЮОПЗП ДЙУЛБ mov dl, ; рПНЕУФЙН Ч EBX БДТЕУ УРЙУЛБ ЪБЗТХЦЕООЩИ ЖБКМПЧ mov ebx, module_list ; рПНЕУФЙН Ч ESI БДТЕУ ЛБТФЩ РБНСФЙ mov esi, memory_map ; рЕТЕИПДЙН ОБ СДТП jmp 0xFFC00000

рЕТЧПЕ СДТП

сДТП РПЛБ Х ОБУ ВХДЕФ УПУФПСФШ ЙЪ ДЧХИ ЖБКМПЧ - startup.asm Й main.c. startup.asm ОХЦЕО ДМС ФПЗП, ЮФПВЩ ВЩФШ ХЧЕТЕООЩНЙ, ЮФП ХРТБЧМЕОЙЕ РПРБДЈФ ОБ ЖХОЛГЙА kernel_main. чЕДШ ПОБ НПЦЕФ ВЩФШ ОЕ Ч ОБЮБМЕ ЖБКМБ, Б УПДЕТЦЙНПЕ startup.o НЩ РПМОПУФША ЛПОФТПМЙТХЕН Й ЕУМЙ ХЛБЦЕН ЕЗП РЕТЧЩН МЙОЛЕТХ, ФП ВХДЕН ХРТБЧМСФШ Й РЕТЧЩНЙ ВБКФБНЙ ДЧПЙЮОПЗП ЖБКМБ.

Format ELF public _start extrn kernel_main section ".text" executable _start: movzx edx, dl push edx push esi push ebx lgdt call kernel_main @: ;cli ;hlt jmp @b section ".data" writable gdt: dq 0 dq 0x00CF9A000000FFFF dq 0x00CF92000000FFFF gdtr: dw $ - gdt dd gdt

оХ ЧПФ Й РПУМЕДОЙК ОБЫ ЛПД ОБ ЮЙУФПН Assembler:-). пО ЧЩРПМОСЕФ РТПУФЕКЫХА ЪБДБЮХ - ХМПЦЙФШ Ч УФЕЛ ФТЙ БТЗХНЕОФБ ДМС ЖХОЛГЙЙ kernel_main Й РЕТЕДБФШ ОБ ОЕЈ ХРТБЧМЕОЙЕ. рПУМЕ ЧПЪЧТБФБ ЙЪ ОЕЈ СДТП ХИПДЙФ Ч ВЕУЛПОЕЮОЩК ГЙЛМ. рП УПЗМБЫЕОЙА ЧЩЪПЧБ ЖХОЛГЙК уЙ РБТБНЕФТЩ УМЕДХЕФ РЙИБФШ Ч УФЕЛ Ч ПВТБЪПН РПТСДЛЕ. фБЛЦЕ ЬФПФ ЛПД ЙОЙГЙБМЙЪБГЙЙ ЪБЗТХЦБЕФ ОПЧПЕ ЪОБЮЕОЙЕ Ч GDTR - ФЕРЕТШ ФБВМЙГБ ДЕУЛТЙРФПТПЧ УЕЗНЕОФПЧ ОБИПДЙФУС Ч РТПУФТБОУФЧЕ СДТБ Й ДБЦЕ ЕУМЙ НЩ ПФНПОФЙТХЕН РЕТЧЩК НЕЗБВБКФ ОЕ РТПЙЪПКДЈФ ОЙЛБЛЙИ ПЫЙВПЛ.

б ФЕРЕТШ УБНПЕ ЧЛХУОПЕ - РТПУФЕКЫЕЕ СДТП ОБ СЪЩЛЕ ЧЩУПЛПЗП ХТПЧОС:

Typedef struct { unsigned long long base; unsigned long long size; } BootModuleInfo; void kernel_main(char boot_disk_id, void *memory_map, BootModuleInfo *boot_module_list) { char *screen_buffer = (void*)0xB8000; char *msg = "Hello world!"; unsigned int i = 24 * 80; while (*msg) { screen_buffer = *msg; msg++; i++; } }

ьФП СДТП ОЕ ДЕМБЕФ ОЙЮЕЗП ПУПВЕООПЗП - РТПУФП ЧЩЧПДЙФ УФТПЛХ "Hello world!" ОБ РПУМЕДОАА УФТПЮЛХ ФЕЛУФПЧПЗП ЬЛТБОБ. уФТХЛФХТБ ПРЙУБООБС Ч ОБЮБМЕ ВХДЕФ ОХЦОБ ДМС ДПУФХРБ Л УРЙУЛХ ЪБЗТХЦЕООЩИ НПДХМЕК.

чБЦОП РПНОЙФШ, ЮФП ОЙЛБЛПК УФБОДБТФОПК ВЙВМЙПФЕЛЙ Х ОБУ ОЕФ - ОБН ДПУФХРОЩ ФПМШЛП ФЕ ЖХОЛГЙЙ, ЛПФПТЩЕ НЩ УДЕМБЕН УБНЙ. чУЕ printf, strcpy, memcpy Й Ф. Р. РТЙДЈФУС ТЕБМЙЪПЧЩЧБФШ УБНПУФПСФЕМШОП, ОЕ РЩФБКФЕУШ ПВТБФЙФШУС Л ОЙН. ч УМЕДХАЭЕН ЧЩРХУЛЕ НЩ ЪБКНЈНУС УПЪДБОЙЕН ОБЫЕЗП УПВУФЧЕООПЗП ЦХФЛП ХТЕЪБООПЗП БОБМПЗБ libc, ЮФПВЩ РТПЗТБННЙТПЧБФШ ВЩМП ХДПВОЕЕ. фХФ ОБЮЙОБЕФУС УБНБС ЙОФЕТЕУОБС ЮБУФШ, Б РТЙОСФЩЕ ТЕЫЕОЙС ЧП НОПЗПН РПЧМЙСАФ ОБ ЧУА УФТХЛФХТХ УЙУФЕНЩ.

уВПТЛБ СДТБ

йУРПМОСЕНЩЕ ЖБКМЩ УПВЙТБАФУС Ч ДЧБ ЬФБРБ - ЛПНРЙМСГЙС, Б РПФПН МЙОЛПЧЛБ. оБ РЕТЧПН ЬФБРЕ ЛПНРЙМСФПТ РТЕПВТБЪХЕФ ЙУИПДОЩК ЛПД Ч ЛПНБОДЩ РТПГЕУУПТБ Й УПИТБОСЕФ ЧУЈ ЬФП Ч ПВЯЕЛФОЩК ЖБКМ. лБЦДЩК НПДХМШ УЙУФЕНЩ УПИТБОСЕФУС Ч ПФДЕМШОПН ЖБКМЕ. ч ЬФПН ЖБКМЕ ФБЛ ЦЕ УПДЕТЦЙФУС ЙОЖПТНБГЙС П ЖХОЛГЙСИ, ПРЙУБООЩИ Ч НПДХМЙ, РПЬФПНХ ЙЪ ПДОПЗП ЖБКМБ НПЦОП УЧПВПДОП ЧЩЪЩЧБФШ ЖХОЛГЙА ЙЪ ДТХЗПЗП. чЕУШ ЛПД Ч ПВЯЕЛФОЩИ ЖБКМБИ ОЕ РТЙЧСЪБО Л ЛПОЛТЕФОЩН БДТЕУБН. оБ ЧФПТПН ЬФБРЕ МЙОЛЕТ УПВЙТБЕФ ЧУЕ ПВЯЕЛФОЩЕ ЖБКМЩ Ч ПДЙО ВЙОБТОЩК. рТЙ ЬФПН ЛПД РТЙЧСЪЩЧБЕФУС Л ЛПОЛТЕФОЩН БДТЕУБН (ЕУМЙ, ЛПОЕЮОП, НЩ ОЕ УПВЙТБЕН ДЙОБНЙЮЕУЛЙ ЪБЗТХЦБЕНХА ВЙВМЙПФЕЛХ), ЧНЕУФП УУЩМПЛ ОБ ЖХОЛГЙЙ РПДУФБЧМСАФУС ОХЦОЩЕ БДТЕУБ. оБН ОХЦОП РПМХЮЙФШ ОБ ЧЩИПДЕ ПУПВЩК ДЧПЙЮОЩК ЖБКМ. ьФП РТПУФП ЛПД Й ДБООЩЕ, ВЕЪ ЛБЛЙИ-МЙВП ЪБЗПМПЧЛПЧ (ФП ЕУФШ ЬФП ОЕ PE Й ОЕ ELF). ч ЛБЮЕУФЧЕ ВБЪПЧПЗП БДТЕУБ ЙУРПМШЪХЕФУС БДТЕУ 0xFFC00000. дМС ХРТПЭЕОЙС ЬФПЗП НЩ ПРЙЫЕН ЧУЈ, ЮФП ОБН ОХЦОП Ч УРЕГЙБМШОПН ЖПТНБФЕ УЛТЙРФБ ld:

OUTPUT_FORMAT("binary") ENTRY(_start) SECTIONS { .text 0xFFC00000: { *(.text) *(.code) *(.rodata*) } .data ALIGN(0x1000) : { *(.data) } .bss ALIGN(0x1000) : { *(.bss) } .empty ALIGN(0x1000) - 1: { BYTE(0) } }

ьФПФ УЛТЙРФ ЗПЧПТЙФ, ЮФП ОБЫ ЖБКМ ВХДЕФ МЕЦБФШ Ч РБНСФЙ ОЕРТЕТЩЧОЩН ВМПЛПН ОБЮЙОБС У БДТЕУБ 0xFFC00000. ч УБНПН ОБЮБМЕ ВХДЕФ ЙДФЙ УЕЛГЙС ЛПДБ, РПФПН УЕЛГЙС read-only ДБООЩИ, ЪБФЕН ПВЩЮОЩИ ДБООЩИ, РПФПН ОЕЙОЙГЙБМЙЪЙТПЧБООЩИ. чУЕ УЕЛГЙЙ ЧЩТПЧОЕОЩ ОБ ТБЪНЕТ УФТБОЙГЩ 4 лв (ЧДТХЗ НЩ РПФПН ЪБИПФЙН ЪБЭЙФЙФШ ОБ ХТПЧОЕ ФБВМЙГЩ УФТБОЙГ ЛПД ПФ ЪБРЙУЙ). рПУМЕДОЕЕ ПРЙУБОЙЕ УЕЛГЙЙ.empty ОЕПВИПДЙНП ДМС ФПЗП, ЮФПВЩ ДБЦЕ ОЕЙОЙГЙБЙМЙЪПТПЧБООЩЕ РЕТЕНЕООЩЕ ЪБОЙНБМЙ НЕУФП Ч ЖБКМЕ (ФБН ВХДХФ ОХМЙ). чЕДШ ОБЮБМШОЩК ЪБЗТХЪЮЙЛ ЧЩДЕМСЕФ РБНСФШ ДМС СДТБ ТХЛПЧПДУФЧХСУШ ТБЪНЕТПН ЖБКМБ.

уПВТБФШ ЧУЈ СДТП НПЦОП УМЕДХАЭЙНЙ ЛПНБОДБНЙ:

Fasm startup.asm startup.o gcc -c -m32 -ffreestanding -o main.o main.c ld --oformat=binary -melf_i386 -T script.ld -o kernel.bin startup.o main.o

рБТБНЕФТ GCC -ffreestanding ХЛБЪЩЧБЕФ ЕНХ ПФЛМАЮЙФШ ЧУЕ УФБОДБТФОЩЕ ВЙВМЙПФЕЛЙ. чЕДШ ПОЙ РТЙЧСЪБОЩ Л ЛПОЛТЕФОПК ПРЕТБГЙПООПК УЙУФЕНЕ, Б НЩ РЙЫЕН ОПЧХА.

уВПТЛБ ПВТБЪБ ДЙУЛБ

пВПКДХУШ ВЕЪ МЙЫОЙИ ЛПННЕОФБТЙЕЧ Й РТПУФП РТЙЧЕДХ МЙОХЛУПЧЩК УЛТЙРФ УВПТЛЙ ПВТБЪБ:

Dd if=bin/boot.bios.bin of=bin/boot_sector.bin bs=512 count=1 dd if=bin/boot.bios.bin of=disk/boot.bin bs=1 skip=512 cp bin/kernel.bin disk/kernel.bin bin/make_listfs of=disk.img bs=512 size=2880 boot=bin/boot_sector.bin src=./disk

пО РТЕДРПМБЗБЕФ, ЮФП ЧУЕ УЛПНРЙМЙТПЧБООЩЕ ЖБКМЩ МЕЦБФ Ч bin Ч ФЕЛХЭЕН ЛБФБМПЗЕ, Б ЕЭЈ ЙНЕЕФУС ЛБФБМПЗ disk, Ч ЛПФПТПН МЕЦЙФ boot.cfg УМЕДХАЭЕЗП УПДЕТЦБОЙС:

# Loading kernel Lkernel.bin # Boot 32 bit kernel S32

еУМЙ ЧЩ ЧУЈ УДЕМБМЙ РТБЧЙМШОП, РПМХЮЕООЩК ПВТБЪ НПЦОП ЪБРХУФЙФШ Ч ЬНХМСФПТЕ ЙМЙ ДБЦЕ ОБ ТЕБМШОПН ЦЕМЕЪЕ Й ЧЩ РПМХЮЙФЕ РПДПВОХА ЛБТФЙОХ:

ъБЗТХЪЮЙЛ УЮЙФЩЧБЕФ ЛПОЖЙЗХТБГЙПООЩК ЖБКМ, ЪБЗТХЦБЕФ СДТП, РЕТЕИПДЙФ Ч ЪБЭЙЭЈООЩК ТЕЦЙН Й РЕТЕДБЈФ ЕНХ ХРТБЧМЕОЙЕ. рПМХЮЙЧ ЕЗП, ОБЫЕ СДТП ЧЩЧПДЙФ РПУМЕДОАА УФТПЛХ ОБ ЬЛТБО. ьФП МЙЫШ ОБЮБМП ДПМЗПЗП РХФЙ, НЩ РЕТЕИПДЙН Л УБНПК ЙОФЕТЕУОПК ЮБУФЙ ТБЪТБВПФЛЙ. фЕРЕТШ ЧЩРХУЛЙ ВХДХФ ЗПТБЪДП ВПМЕЕ РТПУФЩН ДМС ЧПУРТЙСФЙС, ВМБЗПДБТС ЙУРПМШЪПЧБОЙА СЪЩЛБ ЧЩУПЛПЗП ХТПЧОС, ЛПФПТЩК ЛБЛ С ОБДЕАУШ ЧУЕ Й ФБЛ ЪОБАФ. еУМЙ ЧЩ ОЕ ИПФЙФЕ ТБЪВЙТБФШУС У Assembler, НПЦЕФЕ РТПУФП ЧЪСФШ НПК ЗПФПЧЩК ЪБЗТХЪЮЙЛ Й startup.asm Й ЙЪНЕОСФШ ХЦЕ ФПМШЛП УПДЕТЦЙНПЕ main.c, РПУЛПМШЛХ ЧЕУШ ЛПД ДП ЬФПЗП ОЕ ДЙЛФХЕФ ЦЈУФЛП ЛБЛЙЕ-МЙВП РБТБНЕФТЩ СДТБ (ЛТПНЕ жу У ЛПФПТПК НЩ ЪБЗТХЦБЕНУС) Й РПЪЧПМСЕФ РПУФТПЙФШ ОБ УЧПЕК ВБЪЕ ЮФП ХЗПДОП.

бЧФПНБФЙЪБГЙС УВПТЛЙ ЙМЙ Makefile

чЩ НПЗМЙ ЪБНЕФЙФШ, ЮФП ЧТХЮОХА ОБВЙЧБФШ УФПМШЛП ЛПНБОД ДПУФБФПЮОП ХФПНЙФЕМШОП. л ФПНХ ЦЕ ОЕ ЧУЕЗДБ ЕУФШ ОЕПВИПДЙНПУФШ РЕТЕЛПНРЙМЙТПЧБФШ ЧУЕ ЖБКМЩ. оБРТЙНЕТ, ЕУМЙ startup.asm ОЕ ВЩМ ЙЪНЕОЈО, НПЦОП ОЕ ЧЩЪЩЧБФШ fasm. уРЕГЙБМШОП ДМС ХРТПЭЕОЙС ЛПНРЙМСГЙЙ РТЙМПЦЕОЙК ВЩМБ РТЙДХНБОБ ХФЙМЙФБ make, ЛПФПТБС ЧИПДЙФ Ч УФБОДБТФОХА РПУФБЧЛХ GCC Й MinGW.

мАВПК Makefile УФПЙФ ЙЪ ОБВПТБ РТБЧЙМ У ФБЛПК УФТХЛФХТПК:

ЙНСгЕМЙйМЙжБКМБ: йНСрЕТЧПЗПйУИПДОПЗПжБКМБ йНСчФПТПЗПйУИПДОПЗПжБКМБ... лПНБОДЩлПНРЙМСГЙЙ

рЕТЧПЕ РТБЧЙМП, ЛПФПТПЕ ДПМЦОП ВЩФШ Ч МАВПН Makefile - ГЕМШ all. make УНПФТЙФ ОБ ЪБЧЙУЙНПУФЙ ГЕМЙ all Й ЛПНРЙМЙТХЕФ ЙИ, Б ЪБФЕН ЧЩРПМОСЕФ ЛПНБОДЩ Й ЬФПК ГЕМЙ. дМС ЛБЦДПК ДТХЗПК ГЕМЙ УОБЮБМБ УПВЙТБАФУС ЕЈ ЪБЧЙУЙНПУФЙ. рТЙ ЬФПН ЙНС ГЕМЙ Й ЙНС ЪБЧЙУЙНПУФЕК НПЗХФ УПЧРБДБФШ У ЙНЕОБНЙ ТЕБМШОЩИ ЖБКМПЧ. ч ФБЛПН УМХЮБЕ РЕТЕУВПТЛБ ГЕМЙ РТПЙЪПКДЈФ ФПМШЛП ЕУМЙ ЙУИПДОЙЛЙ ВЩМЙ ЙЪНЕОЕОЩ.

еЭЈ ПДОБ ГЕМШ, ЛПФПТБС ЮБУФП ЙУРПМШЪХЕФУС Ч Makefile - clean. еЈ ЪБДБЮБ ХДБМЙФШ ЧУЕ ВЙОБТОЩЕ ЖБКМЩ, ЮФПВЩ ОБЮБФШ УВПТЛХ "У ЮЙУФПЗП МЙУФБ". чПФ ФБЛ НПЦЕФ ЧЩЗМСДЕФШ Makefile ДМС СДТБ:

All: startup.o main.o script.ld ld --oformat=binary -melf_i386 -T script.ld -o kernel.bin startup.o main.o startup.o: startup.i386.asm fasm startup.i386.asm startup.o main.o: main.c gcc -c -m32 -ffreestanding -o main.o main.c clean: rm -v *.o kernel.bin

ьФПФ ФЕЛУФ ОЕПВИПДЙНП УПИТБОЙФШ Ч ЖБКМ У ЙНЕОЕН Makefile (ВЕЪ ТБУЫЙТЕОЙС) Ч ЛБФБМПЗ У ЙУИПДОЩНЙ ФЕЛУФБНЙ СДТБ. фЕРЕТШ ДПУФБФПЮОП ЧЩРПМОЙФШ ЛПНБОДХ make ВЕЪ РБТБНЕФТПЧ, ОБИПДСУШ Ч ЬФПН ЛБФБМПЗЕ Й НЩ РПМХЮЙН ЖБКМ kernel.bin (МЙВП УППВЭЕОЙС ПВ ПЫЙВЛБИ, ЕУМЙ ЮФП-ФП РПЫМП ОЕ ФБЛ).

б ЧПФ ФБЛ С УПВЙТБА ЪБЗТХЪЮЙЛ:

All: boot.bios.bin boot.bios.bin: boot.bios.asm fasm boot.bios.asm boot.bios.bin clean: rm -v boot.bios.bin

Й make_listfs:

All: compile compile: make_listfs.c gcc -o make_listfs make_listfs.c clean: rm -f make_listfs make_listfs.exe

оХ Й ОБЛПОЕГ ТБУУЛБЦХ РТП ЧЩЪПЧ ДТХЗЙИ Makefile ЙЪ ПДОПЗП. с ДПУФБФПЮОП МЕОЙЧ, ЮФПВЩ ДБЦЕ ЪБИПДЙФШ Ч ЛБФБМПЗЙ У ЛБЦДЩН ЛПНРПОЕОФПН УЙУФЕНЩ, РПЬФПНХ УПЪДБМ 1 Makefile, ЛПФПТЩК УПВЙТБЕФ УТБЪХ ЧУА УЙУФЕНХ. х НЕОС ЕУФШ РБРЛБ src, Ч ОЕК РПДЛБФБМПЗЙ: boot, kernel, make_listfs. ч УБНПК src ОБИПДЙФУС ЧПФ ФБЛПК Makefile:

All: make -C boot/ make -C kernel/ make -C make_listfs/ clean: make -C boot/ clean make -C kernel/ clean make -C make_listfs clean

фЕРЕТШ, ОБИПДСУШ Ч ЛБФБМПЗЕ src С РТПУФП РЙЫХ make Й РПМХЮБА РПМОПУФША УПВТБООХА УЙУФЕНХ, Б ЕУМЙ ОБРЙУБФШ make clean, ФП ЧУЕ ДЧПЙЮОЩЕ ЖБКМЩ ВХДХФ ХДБМЕОЩ Й ПУФБОХФУС ФПМШЛП ЙУИПДОЙЛЙ.

оХ Й Ч ДПЧЕТЫЕОЙЕ РПУМЕДОЙК УЛТЙРФ, ЛПФПТЩК ЧЩРПМОСЕФ РПМОХА ЛПНРЙМСГЙА Й УВПТЛХ ЧУЕИ ЛПНРПОЕОФПЧ Й ПВТБЪБ ДЙУЛБ. ч ПДОПН ЛБФБМПЗЕ У ОЙН ОБДП ТБЪНЕУФЙФШ src, РХУФПК ЛБФБМПЗ bin Й ЛБФБМПЗ disk У ЖБКМПН boot.cfg.

#!/bin/sh make -C src cp src/boot/boot.bios.bin bin/ cp src/kernel/kernel.bin bin/ cp src/make_listfs/make_listfs bin/ dd if=bin/boot.bios.bin of=bin/boot_sector.bin bs=512 count=1 dd if=bin/boot.bios.bin of=disk/boot.bin bs=1 skip=512 cp bin/kernel.bin disk/kernel.bin bin/make_listfs of=disk.img bs=512 size=2880 boot=bin/boot_sector.bin src=./disk read -p "Press Enter to continue..." dummy

у ФБЛЙН ОБВПТПН УЛТЙРФПЧ УВПТЛБ УЙУФЕНБ УФБОПЧЙФУС РТЕДЕМШОП РТПУФПК, ПУПВЕООП ЕУМЙ ХЮЕУФШ, ЮФП РПУМЕДОЙК УЛТЙРФ НПЦОП ЪБРХУЛБФШ ДЧПКОЩН ЛМЙЛПН ЙЪ ЖБКМПЧПЗП НЕОЕДЦЕТБ. тБЪМЙЮОЩЕ ЛПНБОДЩ ЧТПДЕ dd, cp, rm ОЕ УХЭЕУФЧХАФ РПД Windows, РПЬФПНХ ЕЈ РПМШЪПЧБФЕМСН РТЙЗПДЙФУС РБЛЕФ MSYS ЙМЙ Cygwin. пДОБЛП РТПУФБС УВПТЛБ ЧУЕИ ЛПНРПОЕОФПЧ ВХДЕФ ТБВПФБФШ ДБЦЕ ЕУМЙ Х ЧБУ ЕУФШ ФПМШЛП GCC Й fasm (make_listfs МЕЗЛП УЛПНРЙМЙТХЕФУС Й ЪБРХУФЙФУС Ч ЧЙДЕ Windows-РТЙМПЦЕОЙС).

рТЙНЕЮБОЙЕ ДМС РПМШЪПЧБФЕМЕК пу Windows

ld ДМС Windows ОЕ УПЧУЕН РПМОПГЕООЩК - ПО ОЕ РПДДЕТЦЙЧБЕФ ЧЩЧПД УТБЪХ Ч ВЙОБТОЩК ЖБКМ, ФПМШЛП Ч EXE. йУРТБЧЙФШ ЬФП НПЦОП УПЪДБЧ УОБЮБМБ EXE (ld ОЕ ПВТБФЙФ ЧОЙНБОЙЕ, ЮФП ВБЪПЧЩЕ БДТЕУБ УЕЛГЙК ОЕЧПЪНПЦОЩЕ ДМС ЧЕОДПЧЩИ ВЙОБТОЙЛПЧ), Б РПФПН ЧЩФБЭЙФШ ПФФХДБ ЮЙУФЩЕ ДБООЩЕ У РПНПЭША objcopy. еУМЙ ЧЩ УФПМЛОЈФЕУШ У ФЕН, ЮФП ld ПФЛБЪЩЧБЕФУС УПЪДБЧБФШ ЖБКМ kernel.bin, ЧПУРПМШЪХКФЕУШ ЧПФ ФБЛЙН Makefile ДМС СДТБ:

All: startup.o main.o script.ld ld -melf_i386 -T script.ld -o kernel.bin startup.o main.o objcopy kernel.bin -O binary startup.o: startup.i386.asm fasm startup.i386.asm startup.o main.o: main.c gcc -c -m32 -ffreestanding -o main.o main.c clean: rm -v *.o kernel.bin

ъБПДОП ХВЕТЙФЕ УФТПЛХ OUTPUT_FORMAT("binary") ЙЪ script.ld. фЕРЕТШ Й РПД Windows РПМХЮЙФУС УПВТБФШ СДТП УЙУФЕНЩ.

ъБЗТХЪЛБ УЙУФЕНЩ ОБ ТЕБМШОПК НБЫЙОЕ

рПУМЕ ФБЛЙИ ХУРЕИПЧ Х ОЕЛПФПТЩИ НПЦЕФ ЧПЪОЙЛОХФШ ЦЕМБОЙЕ ПРТПВПЧБФШ ОПЧХА пу ОБ ТЕБМШОПН ЦЕМЕЪЕ. ьФП ОЕ РТЕДУФБЧМСЕФ РТПВМЕН. у РПНПЭША HxD Ч Windows ПФЛТПКФЕ ДЙУЛЕФХ ЙМЙ ЖМЕЫЛХ, ЧЩВТБЧ ЧБТЙБОФ "пФЛТЩФШ ДЙУЛ". рТЙ ПФЛТЩФЙЙ ЖМЕЫЛЙ ЧБЦОП ПФЛТЩФШ ЙНЕООП УБНХ ЖМЕЫЛХ, Б ОЕ ЕЈ ТБЪДЕМ. ч ДТХЗПК ЧЛМБДЛЕ ПФЛТПКФЕ disk.img, ЧЩДЕМЙФЕ ЕЗП УПДЕТЦЙНПЕ РПМОПУФША Й УЛПРЙТХКФЕ ОБ ДЙУЛ У ЕЗП УБНПЗП ОБЮБМБ. рПУМЕ ЬФПЗП НПЦОП ОБЦБФШ "уПИТБОЙФШ" Й ДПЦДБФШУС ПЛПОЮБОЙС ЪБРЙУЙ. чУЕ ДБООЩЕ ОБ ЖМЕЫЛЕ ЙМЙ ДЙУЛЕФЕ РТЙ ЬФПН ВХДХФ ХОЙЮФПЦЕОЩ, Б ДМС ФПЗП, ЮФПВЩ ЕЈ ЙУРПМШЪПЧБФШ УОПЧБ РП ОБЪОБЮЕОЙА, ЕЈ РТЙДЈФУС ЪБОПЧП ПФЖПТНБФЙТПЧБФШ!

рПМШЪПЧБФЕМЙ Linux НПЗХФ РПУФХРЙФШ РТПЭЕ - ЧЩРПМОЙФШ УРЕГЙБМШОХА ЛПНБОДХ Ч ФЕТНЙОБМЕ. дМС ДЙУЛЕФЩ:

Dd if=disk.img of=/dev/fd0

дМС ЖМЕЫЛЙ:

Dd if=disk.img of=/dev/sdX

чНЕУФП sdX ОБДП РПДУФБЧЙФШ ОБУФПСЭЕЕ ЙНС ХУФТПКУФЧБ (sda, sdb, sdc, sdd Й Ф. Д.). зМБЧОПЕ РТЙ ЬФПН ОЕ РЕТЕРХФБФШ Й ОЕ ЪБРЙУБФШ ПВТБЪ ОБ УЙУФЕНОЩК ДЙУЛ, ХОЙЮФПЦЙЧ ЧУЕ ДБООЩЕ. тБЪХНЕЕФУС, ПВЕ ЛПНБОДЩ ДПМЦОЩ ЧЩРПМОСФШУС ПФ ЙНЕОЙ root ЙМЙ У РПНПЭША sudo.

рПУМЕ ЬФПЗП ОБДП ОБУФТПЙФШ Ч BIOS ЪБЗТХЪЛХ У ДЙУЛЕФЩ ЙМЙ ЖМЕЫЛЙ (УФБТЩЕ BIOS ОЕ РПДДЕТЦЙЧБАФ ЖМЕЫЛЙ) Й ОБУМБЦДБФШУС ЧЙДПН "Hello world".

ъБЛМАЮЕОЙЕ

оХ ЧПФ УПВУФЧЕООП Й ЧУЈ ОБ УЕЗПДОС. нЩ ОБЛПОЕГ-ФП ЪБЛПОЮЙМЙ РТПЗТБННЙТПЧБОЙЕ ОБ Assembler (ИПФС Ч у ЧУЈ ТБЧОП РТЙДЈФУС ЙОПЗДБ ДЕМБФШ БУУЕНВМЕТОЩЕ ЧУФБЧЛЙ ДМС ТБВПФЩ У ПВПТХДПЧБОЙЕН) Й РЕТЕЫМЙ ОБ СЪЩЛ ЧЩУПЛПЗП ХТПЧОС. еЭЈ ПЮЕОШ НОПЗП РТЕДУФПЙФ УДЕМБФШ. нЩ НПЦЕФЕ ХЦЕ РТПЧПДЙФШ ТБЪМЙЮОЩЕ ЬЛУРЕТЙНЕОФЩ, ЙЪНЕОСС НПК main.c, ФПМШЛП ХЮФЙФЕ, ЮФП МАВБС ПЫЙВЛБ (ДПУФХР Л ОЕУРТПЕГЙТПЧБООПК РБНСФЙ, ДЕМЕОЙЕ ОБ ОПМШ) РТЙЧЕДЈФ Л РЕТЕЪБЗТХЪЛЕ ЙМЙ ЪБЧЙУБОЙА УЙУФЕНЩ (НЩ РПЛБ ОЕ ПВТБВБФЩЧБЕН ЙУЛМАЮЕОЙС, РПЬФПНХ РТПГЕУУПТ ОЕ НПЦЕФ РТПДПМЦЙФШ ТБВПФХ РПУМЕ ПЫЙВЛЙ). дП ЧУФТЕЮЙ!

мАВЩЕ ЧПРТПУЩ ЧЩ НПЦЕФЕ ЪБДБФШ ОБ НПК БДТЕУ: [email protected] . й ДБ, УЕКЮБУ УБНПЕ ЧТЕНС ДМС ТБЪМЙЮОЩИ ЙДЕК РП ЛПОГЕРГЙЙ пу Й РТЕДМПЦЕОЙК.

Книга «Операционная система с 0 до 1» опубликована на GitHub и имеет более 2 000 звездочек и 100 форков. Как понятно из названия, прочитав её, вы сможете создать свою собственную операционную систему - и, пожалуй, мало что в мире программистов может быть круче.

Благодаря этой книге вы научитесь следующему:

  • Узнаете, как создать операционную систему на основе технической документации железа. В реальном мире это так и работает, вы не сможете использовать Google для быстрых ответов.
  • Поймёте, как компьютерные компоненты взаимодействуют друг с другом, от софта к железу.
  • Научитесь писать код самостоятельно. Слепое копирование кода не есть обучение, вы действительно научитесь решать проблемы. Кстати, слепое копирование еще и опасно.
  • Освоите всем привычные инструменты для низкоуровневой разработки.
  • Познакомитесь с языком ассемблера.
  • Выясните, из чего состоят программы и как операционная система запускает их. Небольшой обзор этой темы для любознательных мы давали в .
  • Разберётесь, как проводить отладку программы прямо на железе с GDB и QEMU.
  • Язык программирования C. Быстро освоить его можно, следуя .
  • Базовые знания Linux. Достаточно изучить на нашем сайте.
  • Базовые знания в физике: атомы, электроны, протоны, нейтроны, напряжение.

Аббревиатура "NT" маркетингом расшифровывается как "New Technologies", но в проектной документации, она означала совсем другое. Дело в том, что Windows NT разрабатывалась для нового, еще не выпущенного в 1988-м году, процессора Intel i860. Его кодовое название было "N10" (N T en).

Первая версия - Windows NT 3.1, вышла через 5 лет, в 1993 году. На этот момент в команде было уже 250 разработчиков.

Windows сегодня

  • 1 миллиард пользователей
  • 140 миллионов строк кода (включая тестовый код и инструментарий)
    Код Windows очень разный. Какие-то части написаны 20 лет назад, какие-то появились только в текущей версии. Например, код Web Services on Devices (WSD) в Windows Vista существует в своей первой версии, код GDI находится на завершающей стадии своего развития и почти не изменяется, код DirectX уже хорошо разработан, но активно изменяется и в настоящее время.
  • 8000 разработчиков
  • 36 языков локализации
  • 20 лет разработки

Разработка Windows

20-30 лет назад использовалась только одна методология программирования "Водопад". Она представляет собой последовательность:

Спецификации → Дизайн → Реализация → Тестирование → Поставка.

Но такая методология работает только для небольших проектов. Для такого продукта, как Windows сегодня, нужны другие методологии:

  • Product Cycle Model
  • Team Software Process
  • "Экстремальное программирование"

У всех этих методологий есть и преимущества и недостатки. В зависимости от размера команды и этапа развития компонента разные группы разработчиков Windows применяют разные методологии разработки.
Для Windows, как продукта в целом, используется Product Cycle Model:

  • Периоды по 3-4 месяца
  • Внутри периода - "водопад"

Самая главная проблема в разработке продукта такого масштаба состоит в том, что разработка требует времени. На начальном этапе решаются те проблемы, которые существуют в текущем времени и существующими средствами. Но единственная вещь, которая постоянна, это то, что все изменится. За годы разработки:

  • Требования изменятся
  • Возможности изменятся
  • График работ изменится
  • Проект изменится
  • Пользователи изменятся

Несмотря на то, что разные команды ведут разработку по-разному, существуют "универсальные" правила:

  • Выпуск промежуточных версий (milestones, beta, CTP) для широких масс тестеров
  • Выпуск внутренних сборок с короткими циклами (1 сутки)
  • Простота и надежность дизайна
  • Личные и командные вычитывания кода
  • Unit-тесты
  • Верификационные тесты (Build Verification Tests)
  • Любая промежуточная сборка должна быть качественной (то, что написано, должно работать)

От себя отмечу, что за месяц работы с Windows 7 build 6801 в качестве основной ОС на домашнем компьютере, у меня сформировалось положительное впечатление об этой сборки.

Весь процесс разработки Windows построен вокруг ежедневной сборки:

  • Это пульс продукта
  • Разработка никогда не прекращается
  • Ежедневное автоматическое тестирование
  • Интеграция на ранней стадии
  • Ответственность разработчиков
  • Очевидное состояние продукта

Когда-то раньше была только одна ветка исходного кода, и все разработчики вносили изменения прямо в неё. Сейчас команда разработчиков настолько большая, что это не работает. Поддерживается множество веток, среди которых есть основная - WinMain. У каждой лаборатории есть своя локальная ветка разработки, в которую интегрируются изменения. Проверенные изменения со временем интегрируются в WinMain.

Ежедневный цикл разработки:

  • 15:00 - Допущенные к интеграции изменения в систему контроля исходного кода
  • Сборка 6 версий (Free/Checked - x86, x64, IA64)
  • 18:00 - Новые версии доступны для тестирования
  • Новая версия устанавливается на несколько тысяч рабочих станций и серверов для тестирования
  • Автоматизированный стресс-тест
  • 05:00 - Протоколы тестов анализируются, сбои диагностируются
  • 09:00 - Сводные отчеты автоматически рассылаются командам
  • 09:30 - Сводное совещание руководителей команд для определения целей

Все участники проекта, включая самых высокопоставленных руководителей, используют промежуточные версии на своих рабочих (а обычно и домашних) компьютерах.

На чем пишется Windows?

  • C, C++, C#, Ассемблер (x86, x64, IA64)
    Ассемблеры применяются в довольно ограниченном объеме в тех ситуациях, когда без этого не обойтись
  • Visual Studio, Source Insight, build, nmake
  • Source Depot - система контроля исходных текстов
  • WinDbg, KD, NTSD - отладчики

Многие внутренние инструменты, такие как build, можно скачать с microsoft.com/whdc/devtools.

Изменения ядра Windows 7

Ядро Windows 7 претерпело следующие изменения:

  • Рефакторинг
    Почему в Windows нельзя удалить графическую подсистему?
    Ответ на этот вопрос с технической точки зрения состоит в том, что графическая подсистема в Windows не самостоятельна, это часть подсистемы Win32.
    В Windows 7 произошел рефакторинг многих низкоуровневых компонентов для того, чтобы разбить зависимости. Пользователям это не будет заметно, появятся только новые Dll, например kernel32.dll разделилась на kernel32.dll и kernelbase.dll.
    Это разбиение дало возможность выделить минимальное ядро, называемое MinWin (20 мегабайт на диске).
  • Поддержка EFI для x86 и x64 (как в Vista SP1)
    Многие производители пытаются избавиться от BIOS в пользу EFI.
  • Загрузка с VHD (виртуальный жесткий диск)
  • Параллельная инициализация устройств и старт сервисов
    При загрузке Windows довольно длительное время занимает построение дерева устройств. PNP-менеджер должен опрашивать драйверы шин (PCI, USB, FireWire и др.) на предмет того, какие устройства на них есть. И большую часть времени процессор ждет, пока устройства ответят (или нет). Ведь для того, чтобы определить устройства на шине нужно их опросить. Если они есть, то они ответят, а если нет, то приходится ждать, и процессор простаивает. Параллельное выполнение этих задач сокращает время загрузки.
  • Удаление Dispatcher lock из планировщика и PFN lock из менеджера памяти
    Последние несколько лет тактовые частоты процессоров не растут, и развитие идет в сторону увеличения кол-ва параллельно выполняющихся инструкций как на уровне одного ядра, так и на уровне системы (multicore). В связи с этим, была проведена большая работа по улучшению масштабирования.
    Два самых "горячих" лока, которые были в ядре, это Dispatcher lock и PFN lock были удалены.
    Dispatcher lock использовался планировщиком при изменении состояния потоков. Этот лок был удален, и состояние потока "ожидание" разделилось на несколько:
    • Ожидание: В процессе
    • Ожидание: Завершено
    • Ожидание: Отменено
    PFN lock использовался при изменении атрибутов физических страниц памяти. В мультипроцессорной системе каждый процессор запрашивал доступ к этому локу, что вело к большим затратам времени.
  • Поддержка 256 логических процессоров
    Раньше в Windows в качестве affinity mask использовалось машинное слово. Это было сделано из-за того, что так было легко находить свободные процессоры - каждый бит представляет собой процессор. Соответственно, в 32-битной системе поддерживалось 32 логических процессора, а в 64-битной - 64.
    В Windows 7 в результате перехода на сегментную модель affinity mask стала возможна поддержка 256 логических процессоров. Процессоры стали группироваться в группы/сегменты. В каждой группе могут находиться до 64-х процессоров. В результате получается обратная совместимость, старые программы "видят" только процессоры в одной группе, а новые программы, использующие новые интерфейсы, работают со всеми процессорами в системе.
  • Улучшенное энергосбережение: отключение процессорных сокетовСегодня стоит серьезная проблема энергосбережения не только перед владельцами ноутбуков, но и владельцами датацентров. В США 2% электроэнергии потребляются компьютерными датацентрами. Многие из них выключают часть своих серверов на время низкой активности пользователей (выходные дни).
    Было выяснено, что гораздо выгоднее отключать весь процессорный сокет, чем по одному ядру на нескольких, т.к. в этом случае можно отключить и всю инфраструктуру поддержки сокета (контроллер памяти).

Сопровождение Windows, обновления

Раньше обновления зачастую были кумулятивными(накапливаемыми). Это означало, что если ошибочный код содержался в раннем обновлении компонента, то и поздние версии будут содержать этот код. Но не всем пользователям нужны все обновления, у них разная конфигурация.

Теперь после выпуска (RTM) в Windows существует 2 версии исходного кода:

  • RTM GDR (General Distribution Release)
    Включает те немногие изменения, которые предназначены для всех. В основном исправления безопасности.
  • RTM LDR (Limited Distribution Release)
    Во время установки обновления клиент Windows Update выбирает нужную ему ветку и устанавливает код из нее.

Создание обновления безопасности

Работа по созданию обновления безопасности начинается с обнаружения уязвимости. Есть масса разных способов обнаружения - внутренние команды безопасности, партнеры безопасности, разработчики. Когда уязвимость обнаружена, начинается 2 параллельных процесса:

  • Разработка исправления для всех платформ
  • Поиск "вариантов"
    Масштабный поиск похожих вариантов уязвимостей на всех платформах. Поиск не идентичного кода, а похожего.

После разработки исправления, начинаются проверки его кода. Когда они завершатся, исправление интегрируется в сборку, и сборка отправляется на тестирование:

  • Ручное и автоматическое тестирование компонент
  • Автоматическое тестирование искажений форматов файлов, сетевых компонент и т.п. (больше миллиона вариантов)
  • Тестирование системы в целом, включая тестирование обратной совместимости

Только исправления, удовлетворяющие всем критериям качества, допускаются к выпуску на Windows Update и Download Center.

  • Вперёд >

WikiHow работает по принципу вики, а это значит, что многие наши статьи написаны несколькими авторами. При создании этой статьи над ее редактированием и улучшением работали, в том числе анонимно, 90 человек(а).

Операционные системы, составленные из сотен тысяч строк кода, позволяют пользователям взаимодействовать с компьютерной техникой. Они обычно пишутся на языках программирования C, C++ и ассемблер.

Шаги

    Для начала обучитесь программированию. Знание ассемблера необходимо; настоятельно рекомендуется также иметь понятие о других дополнительных языках программирования более низкого уровня, например, С.

    Определитесь, на какое устройство вы хотите загрузить операционную систему. Это может быть CD-диск, DVD-диск, устройство флэш-памяти, жесткий диск или другой компьютер.

    Решите, какой вы хотите видеть вашу операционную систему. Должна ли это быть полная версия ОС с графическим интерфейсом пользователя (GUI) или, может быть, что-нибудь более минималистичное? Вам необходимо знать, в каком направлении двигаться, еще перед началом процесса.

    Уточните, какую платформу процессора будет поддерживать ваша операционная система. AI-32 и x86_64 являются двумя наиболее распространенными версиями для персональных компьютеров, так что их можно считать наилучшим выбором.

    Определитесь, предпочитаете ли делать все самостоятельно с нуля, или же имеются ядра, на основе которых вы бы хотели надстроить систему. Linux с нуля – проект для тех, кто желает, к примеру, создать свой собственный дистрибутив Linux.

    Выберите, собираетесь вы использовать свой собственный загрузчик или предварительно созданный унифицированный системный загрузчик Grand Unified Bootloader (GRUB). Поскольку кодирование своей собственной программы загрузки требует обширных знаний в области компьютерного обеспечения и BIOS, это может отодвинуть график программирования действующего ядра.

    Примите решение по поводу языка программирования, который собираетесь использовать. Конечно, вполне возможно разработать ОС на таком языке, как Pascal или BASIC, но предпочтительнее писать на С или ассемблере. Ассемблер совершенно необходим, т. к. некоторые важные части операционной системы требуют знания именно этого языка. C++, с другой стороны, содержит ключевые слова, требуемые для запуска полной версии ОС.

    • Чтобы собрать ОС с помощью кодов C или C++, вы, конечно, будете использовать то один компилятор, то другой. Это означает, что вы должны прочесть руководство/инструкции/документацию для выбранного компилятора C/C++, что поставляется в комплекте с программным обеспечением или доступно на веб-сайте дистрибьютора. Вам придется узнать множество сложных вещей о компиляторе, кроме того, для совершенствования C++ предстоит изучить его схему и ABI. Вы, как ожидается, поймете различные форматы исполнительных задач (ELF, PE, COFF, обычные бинарные и т.д.) и заметите, что собственный формат Windows, PE (.exe) защищен авторским правом.
  1. Выберите интерфейс программирования приложений (API). Одной из подборок хорошего API является POSIX, так как она хорошо документирован. Все системы Unix имеют, по крайней мере, частичную поддержку POSIX, так что было бы тривиально пристраивать программы Unix на вашу операционную систему.

    Определитесь с дизайном. Существуют монолитные ядра и микроядра. Монолитные ядра выполняют все службы в ядре, в то время как микроядра имеют маленькое ядро в сочетании с пользовательской реализацией сервисов. В общем, монолитные ядра быстрее, но микроядра имеют лучшую изоляцию и защиту от возможных неисправностей.

    Рассмотрите вопрос о разработке и работе в команде. Таким образом, вам потребуется меньше времени на разрешение больших проблем, что позволит создать операционную систему лучшего качества за более короткие сроки.

    Не стирайте ваш жесткий диск полностью. Помните, форматирование диска необратимо очистит все ваши данные! Используйте GRUB или другой менеджер для дублированной загрузки вашего компьютера с другой ОС, пока ваша версия не будет полностью готова функционально.

    Начните с малого. Обратите внимание в первую очередь на мелочи, такие как отображение текста и прерывания, прежде чем переходить к сложным элементам, к примеру, к управлению памятью и многозадачности.

    Сохраняйте резервную копию последней рабочей версии. Это дает определенное спокойствие в случае, если что-то пойдет абсолютно неправильно в текущей версии вашей ОС или последующих дополнениях. В случае поломки вашего компьютера и невозможности загрузки, как вы и сами понимаете, отличной возможностью станет наличие второй копии для работы, так что вы сможете устранить имеющиеся неисправности.

    Протестируйте вашу новую операционную систему на виртуальной машине. Вместо перезагрузки компьютера каждый раз после внесения изменений или передачи файлов с рабочего компьютера тестовой машине вы можете использовать приложение для запуска ОС на виртуальной машине, в то время как ваша текущая ОС продолжает работать. Приложения VM включают в себя VMWare (которая также имеет сервер в свободном доступе), альтернативный открытый исходный код, Bochs, Microsoft Virtual PC (не совместим с Linux), а также XVM VirtualBox.

    Выпустите релиз-версию. Это позволит пользователям рассказать вам о возможных недостатках в вашей операционной системе.

  2. Операционная система также должна быть удобной для пользователя, так что не забудьте добавить полезные функции, которые станут неотъемлемой частью вашего дизайна.

    • Когда разработка будет закончена, подумайте, хотите ли вы представить код в свободном доступе либо же установить частные права на него.
    • Обязательно сделайте функции безопасности вашим основным приоритетом, если хотите, чтобы ваша система была жизнеспособной.
    • Не начинайте проект разработки операционной системы с целью обучения программированию. Если вы не знаете C, C++, Pascal или какие-нибудь другие подходящие языки и свойства, в том числе типы указателя, операции с битами низкого уровня, переключение битов, встроенный ассемблер и т.д., – значит, еще не готовы для создания ОС.
    • Просматривайте такие порталы, как OSDev и OSDever, которые помогут вам улучшить собственную операционную систему. Обратите особое внимание на то, что по большинству вопросов сообщество OSDev.org предпочитает, чтобы вы самостоятельно обращались к содержанию сайта, а не присоединялись к форуму. Если вы все же решили примкнуть к рядам форумчан, для этого должны быть определенные предпосылки. Вы обязаны досконально знать C или C++ и язык x86 ассамблер. Вы также должны понимать общие и комплексные понятия в программировании, такие как Linked Lists, Queues и т.д. Сообщество OSDev в своих правилах прямо говорит о том, что никто не собирается нянчить новых программистов. Если вы пытаетесь разработать ОС, само собой разумеется, что вы «бог» в области программирования. От вас также требуется прочесть руководство по работе с процессором касательно его архитектуры, выбранной вами; например, x86 (Intel), ARM, MIPS, PPC и т.д. Такой справочник по структуре процессора можно легко найти с помощью поиска в Google («Intel Manuals», «ARM manuals» и т.д.). Не регистрируйтесь на форуме OSDev.org, чтобы задавать очевидные вопросы. Это просто приведет к ответам вроде «Read the f*** ing Manual». Для начала вы должны попробовать почитать Википедию, пособия для различных инструментов, которые собираетесь использовать.
    • Проверьте наличие потенциальных мертвых точек и других ошибок. Недочеты, тупики и другие проблемы могут повлиять на проект вашей операционной системы.
    • Если вы хотите способ попроще, представьте дистрибутивы Linux - типа Fedora Revisor, Custom Nimble X, Puppy Remaster, PCLinuxOS mklivecd или SUSE Studio и SUSE KIWI. Тем не менее, создаваемая ОС принадлежит компании, которая первой представила этот сервис (хотя у вас есть права на ее свободное распространение, изменение и запуск, как вам нравится, под GPL).
    • Хорошим решением будет создание совершенно нового раздела для разрабатываемой операционной системы.

    Предупреждения

    • Небрежное переписывание ОС на жесткий диск может повредить его полностью. Будьте осторожны
    • У вас не получится полностью готовая система за две недели. Начните с загружаемой операционной системы, а затем переходите на более интересный материал.
    • Если вы сделаете что-то опрометчивое, как, например, напишите беспорядочные байты в произвольных портах I/O, то уничтожите вашу ОС и можете (в теории) спалить ваше оборудование.
    • Не ожидайте, что будет легко построить качественную операционную систему. Существует множество сложных взаимозависимостей. Например, для того, чтобы ОС была способна работать с несколькими процессорами, ваш диспетчер памяти должен иметь «блокирующие» механизмы для предотвращения доступа лишних процессоров в один и тот же ресурс одновременно. Используемые «блоки» предполагают наличие планировщика, чтобы убедиться, что только один процессор обращается к критическому ресурсу в любой момент времени, а все остальные находятся в режиме ожидания. Тем не менее, работа планировщика зависит от присутствия диспетчера памяти. Это пример зависимости от взаимоблокировки. Нет стандартного способа разрешить подобные проблемы; каждый создатель операционной системы, как ожидается, достаточно квалифицирован, чтобы найти свой собственный вариант их решения.

Руководство по созданию ядра для x86-системы. Часть 1. Просто ядро

Давайте напишем простое ядро, которое можно загрузить при помощи бутлоадера GRUB x86-системы. Это ядро будет отображать сообщение на экране и ждать.

Как загружается x86-система?

Прежде чем мы начнём писать ядро, давайте разберёмся, как система загружается и передаёт управление ядру.

В большей части регистров процессора при запуске уже находятся определённые значения. Регистр, указывающий на адрес инструкций (Instruction Pointer, EIP), хранит в себе адрес памяти, по которому лежит исполняемая процессором инструкция. EIP по умолчанию равен 0xFFFFFFF0 . Таким образом, x86-процессоры на аппаратном уровне начинают работу с адреса 0xFFFFFFF0. На самом деле это - последние 16 байт 32-битного адресного пространства. Этот адрес называется вектором перезагрузки (reset vector).

Теперь карта памяти чипсета гарантирует, что 0xFFFFFFF0 принадлежит определённой части BIOS, не RAM. В это время BIOS копирует себя в RAM для более быстрого доступа. Адрес 0xFFFFFFF0 будет содержать лишь инструкцию перехода на адрес в памяти, где хранится копия BIOS.

Так начинается исполнение кода BIOS. Сперва BIOS ищет устройство, с которого можно загрузиться, в предустановленном порядке. Ищется магическое число, определяющее, является ли устройство загрузочным (511-ый и 512-ый байты первого сектора должны равняться 0xAA55 ).

Когда BIOS находит загрузочное устройство, она копирует содержимое первого сектора устройства в RAM, начиная с физического адреса 0x7c00 ; затем переходит на адрес и исполняет загруженный код. Этот код называется бутлоадером .

Бутлоадер загружает ядро по физическому адресу 0x100000 . Этот адрес используется как стартовый во всех больших ядрах на x86-системах.

Все x86-процессоры начинают работу в простом 16-битном режиме, называющимся реальным режимом . Бутлоадер GRUB переключает режим в 32-битный защищённый режим , устанавливая нижний бит регистра CR0 в 1 . Таким образом, ядро загружается в 32-битном защищённом режиме.

Заметьте, что в случае с ядром Linux GRUB видит протоколы загрузки Linux и загружает ядро в реальном режиме. Ядро самостоятельно переключается в защищённый режим.

Что нам нужно?

  • x86-компьютер;
  • Linux;
  • ld (GNU Linker);

Задаём точку входа на ассемблере

Как бы не хотелось ограничиться одним Си, что-то придётся писать на ассемблере. Мы напишем на нём небольшой файл, который будет служить исходной точкой для нашего ядра. Всё, что он будет делать - вызывать внешнюю функцию, написанную на Си, и останавливать поток программы.

Как же нам сделать так, чтобы этот код обязательно был именно исходной точкой?

Мы будем использовать скрипт-линковщик, который соединяет объектные файлы для создания конечного исполняемого файла. В этом скрипте мы явно укажем, что хотим загрузить данные по адресу 0x100000.

Вот код на ассемблере:

;;kernel.asm bits 32 ;nasm directive - 32 bit section .text global start extern kmain ;kmain is defined in the c file start: cli ;block interrupts mov esp, stack_space ;set stack pointer call kmain hlt ;halt the CPU section .bss resb 8192 ;8KB for stack stack_space:

Первая инструкция, bits 32 , не является x86-ассемблерной инструкцией. Это директива ассемблеру NASM, задающая генерацию кода для процессора, работающего в 32-битном режиме. В нашем случае это не обязательно, но вообще полезно.

Со второй строки начинается секция с кодом.

global - это ещё одна директива NASM, делающая символы исходного кода глобальными. Таким образом, линковщик знает, где находится символ start - наша точка входа.

kmain - это функция, которая будет определена в файле kernel.c . extern значит, что функция объявлена где-то в другом месте.

Затем идёт функция start , вызывающая функцию kmain и останавливающая процессор инструкцией hlt . Именно поэтому мы заранее отключаем прерывания инструкцией cli .

В идеале нам нужно выделить немного памяти и указать на неё указателем стека (esp). Однако, похоже, что GRUB уже сделал это за нас. Тем не менее, вы всё равно выделим немного места в секции BSS и переместим на её начало указатель стека. Мы используем инструкцию resb , которая резервирует указанное число байт. Сразу перед вызовом kmain указатель стека (esp) устанавливается на нужное место инструкцией mov .

Ядро на Си

В kernel.asm мы совершили вызов функции kmain() . Таким образом, наш “сишный” код должен начать исполнение с kmain() :

/* * kernel.c */ void kmain(void) { const char *str = "my first kernel"; char *vidptr = (char*)0xb8000; //video mem begins here. unsigned int i = 0; unsigned int j = 0; /* this loops clears the screen * there are 25 lines each of 80 columns; each element takes 2 bytes */ while(j < 80 * 25 * 2) { /* blank character */ vidptr[j] = " "; /* attribute-byte - light grey on black screen */ vidptr = 0x07; j = j + 2; } j = 0; /* this loop writes the string to video memory */ while(str[j] != "\0") { /* the character"s ascii */ vidptr[i] = str[j]; /* attribute-byte: give character black bg and light grey fg */ vidptr = 0x07; ++j; i = i + 2; } return; }

Всё, что сделает наше ядро - очистит экран и выведет строку “my first kernel”.

Сперва мы создаём указатель vidptr , который указывает на адрес 0xb8000 . С этого адреса в защищённом режиме начинается “видеопамять”. Для вывода текста на экран мы резервируем 25 строк по 80 ASCII-символов, начиная с 0xb8000.

Каждый символ отображается не привычными 8 битами, а 16. В первом байте хранится сам символ, а во втором - attribute-byte . Он описывает форматирование символа, например, его цвет.

Для вывода символа s зелёного цвета на чёрном фоне мы запишем этот символ в первый байт и значение 0x02 во второй. 0 означает чёрный фон, 2 - зелёный цвет текста.

Вот таблица цветов:

0 - Black, 1 - Blue, 2 - Green, 3 - Cyan, 4 - Red, 5 - Magenta, 6 - Brown, 7 - Light Grey, 8 - Dark Grey, 9 - Light Blue, 10/a - Light Green, 11/b - Light Cyan, 12/c - Light Red, 13/d - Light Magenta, 14/e - Light Brown, 15/f – White.

В нашем ядре мы будем использовать светло-серый текст на чёрном фоне, поэтому наш байт-атрибут будет иметь значение 0x07.

В первом цикле программа выводит пустой символ по всей зоне 80×25. Это очистит экран. В следующем цикле в “видеопамять” записываются символы из нуль-терминированной строки “my first kernel” с байтом-атрибутом, равным 0x07. Это выведет строку на экран.

Связующая часть

Мы должны собрать kernel.asm в объектный файл, используя NASM; затем при помощи GCC скомпилировать kernel.c в ещё один объектный файл. Затем их нужно присоединить к исполняемому загрузочному ядру.

Для этого мы будем использовать связывающий скрипт, который передаётся ld в качестве аргумента.

/* * link.ld */ OUTPUT_FORMAT(elf32-i386) ENTRY(start) SECTIONS { . = 0x100000; .text: { *(.text) } .data: { *(.data) } .bss: { *(.bss) } }

Сперва мы зададим формат вывода как 32-битный Executable and Linkable Format (ELF). ELF - это стандарный формат бинарных файлов Unix-систем архитектуры x86. ENTRY принимает один аргумент, определяющий имя символа, являющегося точкой входа. SECTIONS - это самая важная часть. В ней определяется разметка нашего исполняемого файла. Мы определяем, как должны соединяться разные секции и где их разместить.

В скобках после SECTIONS точка (.) отображает счётчик положения, по умолчанию равный 0x0. Его можно изменить, что мы и делаем.

Смотрим на следующую строку: .text: { *(.text) } . Звёздочка (*) - это специальный символ, совпадающий с любым именем файла. Выражение *(.text) означает все секции.text из всех входных файлов.

Таким образом, линковщик соединяет все секции кода объектных файлов в одну секцию исполняемого файла по адресу в счётчике положения (0x100000). После этого значение счётчика станет равным 0x100000 + размер полученной секции.

Аналогично всё происходит и с другим секциями.

Grub и Multiboot

Теперь все файлы готовы к созданию ядра. Но остался ещё один шаг.

Существует стандарт загрузки x86-ядер с использованием бутлоадера, называющийся Multiboot specification . GRUB загрузит наше ядро, только если оно удовлетворяет этим спецификациям .

Следуя им, ядро должно содержать заголовок в своих первых 8 килобайтах. Кроме того, этот заголовок должен содержать 3 поля, являющихся 4 байтами:

  • магическое поле: содержит магическое число 0x1BADB002 для идентификации ядра.
  • поле flags : нам оно не нужно, установим в ноль.
  • поле checksum : если сложить его с предыдущими двумя, должен получиться ноль.

Наш kernel.asm станет таким:

;;kernel.asm ;nasm directive - 32 bit bits 32 section .text ;multiboot spec align 4 dd 0x1BADB002 ;magic dd 0x00 ;flags dd - (0x1BADB002 + 0x00) ;checksum. m+f+c should be zero global start extern kmain ;kmain is defined in the c file start: cli ;block interrupts mov esp, stack_space ;set stack pointer call kmain hlt ;halt the CPU section .bss resb 8192 ;8KB for stack stack_space:

Строим ядро

Теперь мы создадим объектные файлы из kernel.asm и kernel.c и свяжем их, используя наш скрипт.

Nasm -f elf32 kernel.asm -o kasm.o

Эта строка запустит ассемблер для создания объектного файла kasm.o в формате ELF-32.

Gcc -m32 -c kernel.c -o kc.o

Опция “-c” гарантирует, что после компиляции не произойдёт скрытого линкования.

Ld -m elf_i386 -T link.ld -o kernel kasm.o kc.o

Это запустит линковщик с нашим скриптом и создаст исполняемый файл, называющийся kernel .

Настраиваем grub и запускаем ядро

GRUB требует, чтобы имя ядра удовлетворяло шаблону kernel- . Поэтому переименуйте ядро. Своё я назвал kernel-701.

Теперь поместите его в директорию /boot . Для этого понадобятся права суперпользователя.

В конфигурационном файле GRUB grub.cfg добавьте следующее:

Title myKernel root (hd0,0) kernel /boot/kernel-701 ro

Не забудьте убрать директиву hiddenmenu , если она есть.

Перезагрузите компьютер, и вы увидите список ядер с вашим в том числе. Выберите его, и вы увидите:

Это ваше ядро! В добавим систему ввода / вывода.

P.S.

  • Для любых фокусов с ядром лучше использовать виртуальную машину.
  • Для запуска ядра в grub2 конфиг должен выглядеть так: menuentry "kernel 7001" { set root="hd0,msdos1" multiboot /boot/kernel-7001 ro }
  • если вы хотите использовать эмулятор qemu , используйте: qemu-system-i386 -kernel kernel