| 
         | 
       
      
         
        Основни характеристики на UNIX. Представяне на
        типично UNIX ядро. 
        Част І. Дизайн и имплементация на 4.4BSD 
        Тома Борисов | 
       
     
    Въведение 
    Поради голямата си популярност UNIX е може би
    една от най-интересните теми за обсъждане. Много
    от UNIX SO са free или open source, а това ги прави крайно
    удобни за развитие от страна на голям кръг
    разработчици. Честото клониране при тях е
    обичайно явление. 
    Мислех си каква да е първата тема в тази рубрика.
    Имах много възможности; на първо място можеше да
    се пише за Linux. Това до някъде изглежда оправдано.
    Linux е определено най-бързо развиващата се OS сред
    UNIX клонингите. За нея е написано много, така че
    нямаше да е трудно да намеря необходимите
    материали, освен това опитът ми с нея е много
    по-голям отколкото с другите UNIX системи. От друга
    страна именно тази популярност както и факта, че
    Linux е UNIX-like а не 100% UNIX ме възпря да започна с него.
    Трябваше да намеря истински UNIX за когото да пиша.
    Това логично трябваше да бъде или Sun Solaris или BSD.
    Заради скромния ми опит със Solaris и липсата на
    инсталирана версия под ръка факта че се
    разпространява под по-стриктен лиценз, реших да
    не се занимавам с него за сега. Остана разбира се
    BSD. Тя има Open Source варианти, които дават възможност
    да се реализират редица мрежови проекти при това
    на най-високо ниво. Така, че инсталирах FreeBSD 4.2 и
    започнах да е занимавам с него. BSD е един от
    най-разпространените UNIX. На базата на него са
    изградени много OS като FreeBSD, OpenBSD, BSDi, NetBSD, а на
    последно време и не толкова типични UNIX OS като BeOS и
    MacOS X. 
    Първата тема ще е за ядрото на BSD. Тук ще се
    занимаваме с някои системни особености и ще се
    даде описание на основните му характеристики.
    Поради обемистия материал този въпрос ще се
    разгледа в няколко статии - четири по мои
    преценки. На някои това може да се стори скучно,
    но целта на материала е да се събере информация
    за същността на операционната система. Имам идея
    да направя подобно проучване и за Linux, като се
    надявам това да послужи за добра база за
    сравнение между UNIX системите. 
    Нямам намерение да давам описание относно
    ползването на UNIX като цяло, а ще се концентрирам
    върху оптимизирането им и някои въпроси за
    сигурността. Разбира се ще оценя препоръки към
    темата и дори бих окуражил изпращането на
    материали от ваша страна, които да бъдат
    публикувани в тази рубрика. 
    История на UNIX 
    Началото на операционната система UNIX се
    поставя от CTSS, обширна система с поделяне по
    време, разработена от Ф. Корбато в Масачузетския
    технологичен институт в началото на 60-те.
    Разбирайки предимствата на
    многопотребителските многозадачни системи GENERAL
    ELECTRIC, AT&T BELL LABS и MIT започват проект въз основата
    на операционната система GE635 наречен MULTICS. Поради
    закъсняване на проекта AT&T изтегля част от
    персонала си към други проекти. През късната
    пролет и ранното лято на 1969 Руд Канадей, Дъг
    Макилрой, Денис Ритчи и Кен Томпсън започват
    обсъждане по въпроса какво може да се спаси от
    идеите на MULTICS. През август Томсън написва
    операционна система, шел, редактор и асемблер,
    отделяйки за всяко по една седмица. Питър Нюман
    нарича новата операционна система UNICS. Не е ясно
    как се е стигнало до UNIX. 
    От 1970 до 1972 системата се подобрява и се добавят
    много нов възможности. UNIX остава ограничена само
    до лабораториите на AT&T, докато не я инсталират
    в телефонния отдел в Манхатън. През следващата
    година все нови и нови членове на компютърната
    общност чуват за UNIX и питат за нея. В AT&T се чудят
    какво да правят. Те немогат да включат в бизнеса
    нещо различно от телефония и телеграфия, така че
    решават да дадат UNIX на нормална цена на
    университетските изследователски центрове при
    следните условия: без реклами, без поддръжка, без
    коригиране на грешките и с плащане в аванс. В
    резултат на това общността на потребителите на
    UNIX се увеличава. Използването й расте въпреки
    липсата на поддръжка от страна на AT&T. 
    Тъй като системата постоянно се променя версиите
    се именуват според ръководствата за програмисти,
    издавани от Ричи и Томпсън - Първа редакция 1971,
    Втора редакция 1972... 
    През октомври 1973 се изготвя Четвърта редакция на
    UNIX. Тъй като системата е леснодостъпна и AT&T не я
    поддържат, потребителите на системата се
    обединяват и започват сами да си помагат, а някои
    от тях се заемат сами да добавят нови функции.
    Университета Бъркли в Калифорния е мястото с
    най-голяма активност в тези разработки. 
    В началото на 1978 има големи заявки за
    разработките на Бъркли и те започват да се
    дистрибутират. Първата лента съдържа система
    Паскал за UNIX и текстов редактор ex. Преди края
    на 1978 е пусната втората версия - 2BSD. В това време Interactive
    Systems пуска първата комерсиална версия на UNIX, а
    Whitesmiths - първия клонинг Idris. 
    Седма версия (1979) на UNIX е сред най-важните. Тя е
    първата преносима операционна система и съдържа awk,
    make, uucp. Но производителността е по-слаба в
    сравнение с предходната версия. Потребителският
    вариант се появява през 1982 под името 2.8.1BSD.
    Последната версия е 4.4BSD от 1993. BSD вече се поддържа
    от BSDI (след съдебен процес). 
    AT&T се опитват да създадат унификация на UNIX
    системите: тяхната System V и BSD версията на Бъркли. В
    резултат на това се получава System V Release 4, която
    комбинира и двете системи. Тя става основа за
    разработената от Sun SunOS, 4.x BSD версиите на която е
    преименувана на Solaris. Освен System V Release 4
    производителите създават още един стандарт
    наречен POSIX, характеризиращ основните черти
    които трябва да носи една UNIX система. Той е
    отворен стандарт и дори WinNT отговаря на някои от
    изискванията. Най-интересният от всички UNIX
    клонове е Linux. Той е създаден от Линус Торвалдс
    през 1991. За историята на Linux сте се спрем подробно
    в други статии. 
    Ядрото 
    Ядрото е час от системата, което се изпълнява в
    защитен режим и осигурява връзката на
    потребителските програми с прилежащия хардуер
    както и с другите софтуерни съставляващи
    (файлова система, мрежови протоколи). Ядрото
    предоставя също и основните функционалности;
    създава и управлява процесите, предоставя
    функции за достъп до файловата система и
    средствата за комуникация. Тези функции се
    наричат system calls и са достъпни на потребителските
    процеси под формата на библиотечни процедури и
    функции. Тези system calls са единствения интерфейс
    към функционалностите на ядрото. Kernel в
    традиционната терминология на операционните
    системи е малко ядро от софтуер, който предоставя
    само функционалност, необходимата за
    имплементацията на допълнителното софтуерно
    обкръжение. Така например достъп до файлова
    система и мрежовите функции се изпълняват като
    допълнителни услуги. 
    4.4BSD ядрото не е разделено на различни процеси,
    както е било в ранните UNIX версии. Ранните kernels са
    малки и при включването на услуги като мрежова
    поддръжка се увелича ват размерите им.
    Съвременната тенденция при OS е да запази малкия
    размер на ядрото като тези услуги са изпълнени
    като отделни приложения. За тази версия обаче е
    бил избран монолитен kernel за производителност и
    лекота при проектиране. 
    Организация на ядрото 
    Най-голяма част от ядрото е имплементация на
    системните функционалности до които
    приложенията имат достъп през system calls. В 4.4BSD
    кодът е разделен условно на следните части: 
      - Основна функционалност на ядрото: таймер и
        управление на системния часовник, управление на
        дескриптора и системни процеси;
 
      - Управление на паметта: paging и swapping;
 
      - Основни системни интерфейси: I/O, управление;
 
      - Файлова система: файлове, директории,
        заключване на файл, определяне на пътищата до
        файл, управление на I/O буфера;
 
      - Поддръжка на терминали: драйвери за терминални
        устройства;
 
      - Поддръжка на мрежова комуникация: протоколи и
        основни мрежови функционалности като routing.
 
     
    По-голямата част (80.4% или 162 617 реда код) то ядрото
    е машинно независим код и е приложим при различни
    платформи. 
    Машинно зависимият код е изолиран от главния
    сорс на ядрото, тоест никоя част от машинно
    независимия код няма зависимост от какъвто и да е
    машинно зависим код. Частите, които са машинно
    зависими са съответно: 
      - Стартов процес на ниско ниво;
 
      - Управление на прихващанията и грешките;
 
      - Управление на ниско ниво на изпълнимото
        съдържание на процес;
 
      - Конфигуриране и инициализиране на хардуера;
 
      - Изпълнима поддръжка за I/O.
 
     
    Може да се отбележи, че кодът писан на асемблер
    е под 2% от целия, останалата част е писана на C. 
    Само малка част от ядрото е предназначена за
    инициализация на системата. Този код е отговорен
    за първоначалното зареждане и за настройките на
    хардуерната и софтуерната сред на ядрото. Някои
    операционни системи (тези с ограничени ресурси)
    игнорират или свалят от паметта кода изпълняващ
    тези функции след като се изпълнят. 4.4BSD не взема
    паметта на тези програми, защото те
    представляват едва 0.5% от общото системно
    натоварване на нормална машина. Освен това
    стартовият код не се намира само на едно място в
    ядрото, а е разпръснат. 
    Услугите в ядрото 
    Границата между ядрото и потребителските
    програми се налага от защитения режим на
    прилежащия хардуер. Ядрото оперира в отделно
    адресно поле, недостъпно за потребителските
    процеси. Привилегировани операции като
    започването на I/O или изключването на процесора
    са достъпни само за ядрото. Процесите изискват
    услуги от ядрото чрез system calls. Тези заявки са
    механизмът чрез който потребителските процеси
    изискват от ядрото извършване на сложни операции
    като запис върху носител или прости операции
    като връщането на текущото време. Всички заявки
    изглеждат синхронни за приложението. То не се
    изпълнява докато действието свързано с system call
    се изпълнява. Ядрото може да свърши с
    изпълнението на някои операции, свързани с
    заявката и след като се е възстановил от system call.
    Например при заявка за запис ядрото копира
    нужната информация в буфера си, но се
    възстановява преди да е приключил записът от
    буфера на носителя. 
    System call обикновено се изпълнява като
    хардуерно прихващане, които променя режима на
    изпълнение на процесора и адресирането на
    паметта. Параметрите, предавани от програмите
    чрез system calls се проверяват от ядрото преди да
    се изпълнят. Такава проверка осигурява цялостта
    на системата. Всички параметри се копират в
    адресното пространство на ядрото, за да се
    гарантира че валидираните параметри не се
    променят в резултат на странични ефекти,
    породени от изпълнението на system call.
    Резултатите от заявките се връщат от кернела във
    вид на хардуерни регистри или чрез копиране на
    техните стойности в оказан от потребителския
    процес адрес. Също както параметрите, кака и
    върнатите адреси и резултати се валидират за да
    се установи тяхната принадлежност към адресното
    пространство на процеса. Ако се установи грешка
    по време на изпълнение на system call, ядрото
    връща код за грешка на потребителя. 
    Потребителските приложения и ядрото оперират
    независимо едни от други. 4.4BSD не съхранява I/O
    контролни блокове или друга информация, свързана
    с OS, в потребителското адресно пространство.
    Всеки потребителски процес има собствено
    адресно пространство в което се изпълнява. 
    Управление на процесите 
    4.4BSD поддържа многозадачна среда. Всяка задача
    или тред от изпълнения се нарича процес.
    Контекста на процес се състои от потребителско
    ниво, включващ съдържанието на неговото адресно
    поле и средата за изпълнение, както и от
    кернелско ниво, което включва параметри на
    планирането, контроли на ресурсите и
    идентификационна информация. Контекста на
    процес включва всичко използвано от ядрото при
    доставяне на необходимите му услуги.
    Потребителят може да изпълнява процес, да
    контролира неговото състояние по време на
    изпълнение и да получава информация за статуса
    по време на изпълнението. На всеки процес се
    присвоява уникална стойност, наречена
    идентификатор на процеса (PID). Тази стойност се
    използва от ядрото за да идентифицира процес
    когато докладва неговото състояние на
    потребителя или от потребителя, когато той
    изпраща system call чрез ядрото към процеса. 
    Ядрото създава процес като дупликира контекста
    от друг процес. Новият процес се нарича child process
    на оригинала, който от своя страна се нарича parent
    process. Контекста дуплициран при създаването на
    процес включва както потребителското състояние
    на изпълнение, така и системното положение на
    процеса управляван от ядрото. 
      
    На горната фигура е показан живота на един
    процес. Процес създава свой child процес чрез fork
    system call. Той връща две стойности - съответно в
    родителският процес връща PID на child процеса,
    а на child процеса връща 0. Отношенията между
    child и parent процес са строго йерархични. Новият
    процес поделя всички ресурси на създалият го
    процес като файлови дескриптори, статус на
    сигналите и разположение в паметта. 
    Въпреки това има случаи, когато новият процес не
    е просто копие на родителят, а се зарежда и
    изпълнява друг код. Процес може да препокрие
    своето адресно поле с адресно поле на друга
    програма, като подава на новосъздаденото копие
    набор от параметри, като се използва system call execve.
    Един от параметрите е името на файл във формат,
    разбираем за системата - бинарен изпълним файл
    или файл, който предизвиква изпълнението на
    специфична програма, която интерпретира
    неговото съдържание (скрипт). 
    Процес може да бъде терминиран чрез изпълнението
    на exit system call, като при това се изпраща 8-битов
    изходен статус на родителя. Ако процес иска да
    предаде повече от един байт информация с
    родителя си, то той трябва или да премине в режим
    на междупроцесна комуникация или да използва
    файл посредник. 
    Процес може да преустанови временно своето
    изпълнение докато някой от неговите child
    процеси не се терминира и не върне своя PID и
    изходен статус, като се използва wait system call.
    Родителски процес може да се настрои да бъде
    информиран, когато някой от неговите подпроцеси
    е терминиран ненормално или излезе. Това се
    постига чрез wait4 system call като по този начин
    родителския процес получава информация за
    статуса с който излиза подпроцеса. Процесите се
    подреждат за изпълнение в зависимост от техните
    параметри на приоритета. Тези приоритети се
    управляват от кернелски алгоритми за планиране.
    Потребителите могат да влияят на приоритетите
    като подават параметри nice които изменят
    глобалните приоритети, но все пак трябва да се
    съобразяват със наличните процесорни ресурси в
    зависимост от политиката на планиране на ниво
    ядро. 
    Сигнали 
    Системата определя набор от сигнали, които може
    да се подават на процес. Сигналите в 4.4BSD се
    моделират след хардуерно прекъсване.. Когато
    сигнал се генерира той се блокира от понататъшно
    появяване докато не бъде обработен (caught).
    Стандартното действие на сигнал е да прекрати
    процеса, като в някои случаи това е съпроводено с
    създаването на core files, които съдържат копие
    на адресното пространство на процеса.
    Обработката на сигнала може да не е задължителна.
    Той може да се игнорира. 
    Някои сигнали неможе да се игнорират. Това са SIGKILL
    който прекратява текущия процес и SIGSTOP който
    регулира работата на процеса. 
    Всички сигнали са с еднакъв приоритет. Когато те
    възникват едновременно обработката им зависи от
    конкретната имплементация. Има специални
    механизми които предпазват критични части от
    кода от получаването на някои сигнали. Групи от
    процеси и сесии. За да се осигури контрол над
    много зависими процеси се създават групи от
    процеси - process groups. Групирането на процеси води до
    улеснено изпращане на сигнали до много процеси,
    както и до контролира достъпа до терминали. Има
    методи чрез които да се промени текущата група.
    Създаването на нова група е лесно; стойността на
    новата група е обикновено идентификатора на
    създавания процес. 
    
      #ps acux
      
        USER 
        root 
        root 
        root 
        root 
        root 
        root 
        root 
        root 
        root 
        root 
        root 
        root 
        root 
        root 
        root 
        root 
        root 
        root 
        usera 
        root | 
        PID 
        306 
        1 
        2 
        3 
        4 
        5 
        29 
        108 
        129 
        132 
        136 
        189 
        192 
        193 
        194 
        195 
        196 
        268 
        270 
        0 | 
        %CPU 
        0.0 
        0.0 
        0.0 
        0.0 
        0.0 
        0.0 
        0.0 
        0.0 
        0.0 
        0.0 
        0.0 
        0.0 
        0.0 
        0.0 
        0.0 
        0.0 
        0.0 
        0.0 
        0.0 
        0.0 | 
        %MEM 
        0.1 
        0.0 
        0.0 
        0.0 
        0.0 
        0.0 
        0.0 
        0.2 
        0.3 
        0.7 
        0.6 
        0.3 
        0.2 
        0.2 
        0.2 
        0.2 
        0.2 
        0.4 
        0.3 
        0.0 | 
        VSZ 
        08 
        528 
        0 
        0 
        0 
        0 
        208 
        916 
        952 
        2496 
        2404 
        1336 
        924 
        924 
        924 
        924 
        924 
        1336 
        1012 
        0 | 
        RSS 
        240 
        296 
        0 
        0 
        0 
        0 
        92 
        616 
        704 
        1904 
        1436 
        896 
        616 
        616 
        616 
        616 
        616 
        940 
        860 
        0 | 
        TT 
        v1 
        ?? 
        ?? 
        ?? 
        ?? 
        ?? 
        ?? 
        ?? 
        ?? 
        ?? 
        ?? 
        v0 
        v3 
        v4 
        v5 
        v6 
        v7 
        v1 
        v2 
        ?? | 
        STAT 
        R+ 
        ILs 
        DL 
        DL 
        DL 
        DL 
        Is 
        Is 
        Is 
        Is 
        Is+ 
        Is+ 
        Is+ 
        Is+ 
        Is+ 
        Is+ 
        Is+ 
        Ss 
        Is+ 
        DLs | 
        STARTED 
        4:32PM 
        5:50PM 
        5:50PM 
        5:50PM 
        5:50PM 
        5:50PM 
        5:50PM 
        3:50PM 
        3:50PM 
        3:50PM 
        3:50PM 
        3:50PM 
        3:50PM 
        3:50PM 
        3:50PM 
        3:50PM 
        3:50PM 
        4:13PM 
        4:14PM 
        5:50PM | 
        TIME 
        0:00.00 
        0:00.00 
        0:00.00 
        0:00.00 
        0:00.00 
        0:00.04 
        0:00.00 
        0:00.05 
        0:00.01 
        0:00.02 
        0:00.28 
        0:00.06 
        0:00.01 
        0:00.01 
        0:00.01 
        0:00.01 
        0:00.01 
        0:00.08 
        0:00.03 
        0:00.00 | 
        COMMAND 
        ps 
        init 
        pagedaemon 
        vmdaedom 
        bufdaemon 
        syncer 
        adjkerntz 
        syslogd 
        cron 
        sendmail 
        sshd 
        csh 
        getty 
        getty 
        getty 
        getty 
        getty 
        csh 
        bash 
        swapper | 
       
     
    изход от изпълнение на ps
    с форматиращи параметри  
    Групите от процеси понякога се определят
    като работа job и се поддържа от програми от
    високо ниво като шела. Терминалите имат
    присвоени идентификатори на групи процеси. Той
    обикновено е еднакъв с групата процеси
    асоциирани с терминала. Шел, контролиращ
    задачите може да създаде групи процеси
    асоциирани с определен терминал, тогава
    терминала става контролиращия терминал на всеки
    процес в групите. Процес може да чете от
    дескриптора на терминал само ако
    идентификаторът му съвпада с този на
    идентификатора на терминала, ако не на процеса се
    забранява да чете от терминала. Като се променя
    идентификатора на групата, терминала може да
    общува с различни задачи. 
    Също както процесите се групират в групи от
    процеси, така и групите от процеси се групират в
    сесии (session). Главното приложение на сесиите
    е да се създаде изолирана среда за демонни
    процеси и техните наследници и да събира логин
    шела на потребителя и задачите, които той
    създава.  
    Заключение 
    С това приключваме за тази част от
    разглеждането на ядрото. До сега се занимавахме
    само с процесите и въпроси, свързани с
    организацията на ядрото. В следващите статии ще
    разгледаме въпроса за връзките на процесите с
    другите ресурси и между самите тях.   |