Ծրագրավորողը և օպերացիոն համակարգը։ Մաս II

Առաջին մասում մենք սկսեցինք մեր էքսկուրսիան դեպի օպերացիոն համակարգի խորքեր՝ տեսնելու համար, թե ինչ է կատարվում այնտեղ, երբ մենք ցանկանում ենք ֆայլ բացել և ինչ-որ բան գրել այնտեղ։

Ֆայլ բացելու հարցումը, փոխանցվելով համակարգային կանչերի կոմպոնենտից ֆայլային համակարգի կոմպոնենտներին, ի վերջո հասավ արտաքին հիշասարքի դրայվերին։ Այս պահին կարիք կա հիշասարքից կարդալ ֆայլային համակարգի սուպերբլոքը, որպեսզի կարողանանք կենդանացնել այն։ Հարցումը պարունակում է բլոքի համարը և կարդալու հրամանի կոդը։

Արտաքին սարքերը հիմնականում ունենում են երկու մակարդակ՝ տրամաբանական և մեխանիկական։ Տրամաբանական կոմպոնենտին անվանում են կառավարիչ (controller)։ Նրա ֆունկցիան է կառավարել մեխանիկական մասը և տրամադրել համեմատաբար հարմար ինտերֆեյս ծրագրային ապահովման համար։ Օրինակ՝ մեր այս դեպքում մենք օգտվելու ենք հենց այդ ինտերֆեյսից։ Այն իրենից ներկայացնում է մի քանի ռեգիստրներ, որոնք տրամադրում են երեք հիմնական ֆունկցիոնալություն՝ կառավարում, տվյալների փոխանակում և տեղեկություն վիճակի վերաբերյալ։ Մեր այս օրինակում դրայվերը պետք է կառավարման ռեգիստրում գրի կարդալու հրամանի կոդը, իսկ տվյալների ռեգիստրում, ամենայն հավանականությամբ, պետք է փոխանցվի բլոքի համարը և օպերատիվ հիշողության այն հատվածի հասցեն, որտեղ կուզենք տեսնել այս հրամանի արդյունքը՝ սուպերբլոքի պարունակությունը։ Հրամանը կառավարիչ կոմպոնենտին հասցնելու համար պետք է օգտվել կենտրոնական պրոցեսորի տրամադրած հրամաններից՝ in, out` եթե ունենք port-mapped I/O, կամ հիշողության համապատասխան բայթերի մեջ գրել-կարդալու հրամաններից, եթե գործ ունենք memory-mapped I/O դեպքի հետ։

Արտաքին սարքի կառավարիչ կոմպոնենտը, ստանալով կարդալու հրամանը, այն ձևափոխում է ֆիզիկական ազդակների՝ էլեկտրական լարումների հաջորդականության, որով և կառավարում է սարքի մեխանիկական մասը՝ տարատեսակ շարժիչներին, ռելեներին և էլի ուրիշ սարքերի։ Արդյունքում կոշտ սկավառակի կարդացող գլխիկը տեղափոխվում է համապատասխան դիրք և սկավառակն սկսում է պտտվել։ Տեղի է ունենում ընթերցման գործողությունը և ընթերցված բայթերը կառավարիչ կոմպոնենտն սկսում է արտագրել նշված հասցեով տիրույթ։

Այժմ պետք է կենտրոնական պրոցեսորին տեղեկացնել հրամանի բարեհաջող ավարտի մասին։ Այդ նպատակով կոշտ սկավառակի կառավարիչն ուղարկում է ընդհատում։ Այն իրենից ներկայացնում է էլեկտրական ազդակ՝ ուղարկված ընդհատումների կառավարիչին, որն էլ այն փոխանցում է կենտրոնական պրոցեսորին՝ բառացիորեն ընդհատելով նրա աշխատանքը։ Այս պարագայում պրոցեսորը պետք է գտնի ընհատման մշակող ֆունկցիան և կանչի այն։ Ինչպես հիշում ենք՝ այդ ֆունկցիաների հասցեները բեռնվում են պրոցեսորի համար հայտնի և հասանելի հիշողության հատված՝ օպերացիոն համակարգի բեռնման առաջին քայլերում։ Այժմ, ելնելով ընդհատման համարից, պրոցեսորը գտնում է մշակող ֆունկցիան և սկսում է կատարել նրա մեջ եղած հրամանները։ Կարևոր է նշել, որ այդ ֆունկցիան հանդիսանում է կոշտ սկավառակի դրայվերի մի մասը։ Այսինքն ղեկավարումը վերադարձվեց այնտեղ, ուր մենք կանգ էինք առել՝ սպասելով, մինչև արտաքին սարքը կավարտի իրեն տրված հրամանի կատարումը։

Դրայվերը, ով ուղարկել էր կարդալու հրամանը, տեսնում է, որ այն բարեհաջող կատարվել է և տվյալներն արդեն հասանելի են օպերատիվ հիշողության մեջ։ Հետևաբար պետք է այդ մասին տեղյակ պահել ֆայլալին համակարգին, ով դիմել էր կարդալու համար։ Սպասումն ավարտվում է և ֆայլային համակարգն արդեն կարող է օգտագործել սուպերբլոքի պարունակությունը։ Այն անհրաժեշտ էր ֆայլային համակարգի կառուցվածքը պարզելու համար՝ թե ո՛ր կոմպոնենտը ո՛ր բլոքներում է տեղավորված։ Մասնավորապես մեզ անհրաժեշտ է գտնել i-node-երի աղյուսակը և համակարգի մուտքի կետը՝ արմատ հանդիսացող պանակի i-node-ը։ Այս վերջինը պարունակում է ֆայլային համակարգի ամենավերին մակարդակում գտնվող ֆայլերի և պանակների ցանկը և նրանց համապատասխանող i-node-երի համարները։ Մեր օրինակում՝ այն փաստացի պարունակում է /home պանակի պարունակությունը։ Այն կարդալու համար պետք է բազում հարցումներ ուղարկել արտաքին հիշասարքի դրայվերին և սպասել դրանց պատասխաններին։ Մասնավորապես պետք է կարդալ հենց i-node-ը։ Ինչպես նաև կարող է կարիք լինի կարդալ նրանում եղած հղումների ցույց տված բլոքները։ Ամեն դեպքում՝ մի քանի քայլից մենք կունենանք /home/me պանակի պարունակությունը, որտեղից էլ կտեսնենք, որ այն իրոք պարունակում է file1 անունով ֆայլ։

Ֆայլի i-node-ը պարունակում է տեղեկություն այն մասին, թե ով ինչ իրավունքներ ունի այդ ֆայլի նկատմամբ։ Այս դեպքում մեզ անհրաժեշտ է ստուգել և համոզվել, որ դիմող պրոցեսը, ավելի ճիշտ այն օգտագործողը, ում անունից այն աշխատում է, ու այդ ֆայլը կարդալու իրավունք։ i-node-ի համապատասխան բայթերում այդ փաստը ստուգելուց հետո, ֆայլային համակարգի կոնկրետ իրականացման կոմպոնենտը ղեկավարումը վերադարձնում է վիրտուալ ֆայլային համակարգին՝ հաղորդելով, որ ֆայլը բացելու հետ կապված խնդիրներ չեն հայտնաբերվել։ Վիրտուալ ֆայլային համակարգն անցում է հաջորդ քայլին՝ ստեղծում է ֆայլի նկարագրիչ (file descriptor), որը պարունակում է հտևյալ տեղեկությունը․

1․ Որ ֆայլն է բացված, այսինքն նրա i-node-ի համարը։

2․ Ինչ նպատակով է այն բացված։ Այս դեպքում՝ միայն կարդալու համար։

3․ Ուր ենք հասել կարդալով։ Այս դեպքում՝ ֆայլի սկզբում ենք։

Ֆայլային նկարագրիչը տեղավորվում է հատուկ աղյուսակում՝ ամենափոքր ազատ համարի տակ, և հենց այդ համարը վերադարձվում է կանչող պրոցեսին։ Այսպիսով, open() ֆունկցիայի բարեհաջող կանչի արդյունքում, կանչող պրոցեսը ստանում է ստեղծված նկարագրիչի համարը։ Այն հետագայում օգտագործվելու է որպես պարամետր բոլոր այն ֆունկցիաների համար, ովքեր այդ ֆայլի հետ պետք է կատարեն ինչ-որ գործողություններ։

Այսպիսով՝ դիտարկվող ծրագրային հատվածի առաջին տողն ավարտվեց։ Նրա կատարման ընթացքում ակտիվացվեցին օպերացիոն համակարգի մի շարք կոմպոնենտներ, ստեղծվեցին մի շարք օբյեկտներ, վերադարձվեցին արժեքներ, մշակվեցին ընդհատումներ, կատարվեցին հարցումներ արտաքին սարքերին, տեղի ուեցավ հիշողության հատկացում և այլն։ Եվ սա դեռ ընդամենը առաջին տողն էր։ Իսկ ինչեր են մեզ սպասվում երկրորդ տողում․․․

Շարունակելի․․․