A1. Власний shell
Оболонка — найкоротший шлях побачити всі механізми модуля 6 одразу. Кожна її дія є системним викликом, і жодного з них не заміниш бібліотечною функцією.
Наприкінці ви матимете програму, яка запускає команди, будує конвеєри
й перенаправляє потоки. А заразом зрозумієте, чому cd не може бути
зовнішньою програмою й чому > file не потребує від програми жодної
підтримки.
Що має вийти
Section titled “Що має вийти”Виконуваний файл myshell, який читає команди зі стандартного входу
й виконує їх.
Чого робити не треба. Лапки, екранування, підстановка змінних і символи-шаблони до цієї роботи не входять: слова розділяються пробілами, і цього досить. Автоперевірка нічого з переліченого не використовує.
-
Запуск однієї команди.
Прочитати рядок, розбити на слова,
fork, у дитиніexecvp, у батькаwaitpid. Порожній рядок ігнорувати,Ctrl+D(кінець вводу) завершує оболонку.Перевірте
strace -fна власній програмі: ви маєте побачитиclone,execveіwait4— рівно те, що описано в модулі 6. -
Вбудовані команди:
cd,pwd,exit.cdмусить виконуватися в самій оболонці, а не в породженому процесі. Спробуйте зробити навпаки й подивіться, що станеться: робочий каталог зміниться в дитині, яка одразу помре.Це і є відповідь на питання, чому
cdнемає в/usr/bin. -
Перенаправлення
>,>>,<.Уся робота відбувається між
forkіexec: у дитині відкрити файл, підмінити дескриптор0або1черезdup2, закрити зайве — і аж тодіexecvp.Програма, яку ви запускаєте, про перенаправлення нічого не знає.
-
Конвеєри
a | b | c.На кожен стик —
pipe(). Дитина зліва пише вpipefd[1], дитина справа читає зpipefd[0]. Батько зобов’язаний закрити обидва кінці в себе, інакшеreadправоруч ніколи не побачить кінця потоку і конвеєр зависне.Кількість команд у конвеєрі — довільна.
-
Фонові задачі
&і прибирання зомбі.Команда з
&не чекається: оболонка одразу повертає запрошення. Але завершені діти лишаються зомбі, поки хтось не викличеwait— тому потрібен обробникSIGCHLDабоwaitpid(-1, ..., WNOHANG)у циклі перед кожним запрошенням.Перевірка: запустіть
sleep 0.2 &десять разів і подивітьсяps -eo stat | grep -c Z. -
Сигнали.
Ctrl+Cмає завершувати передній процес, а не саму оболонку. Оболонка ігноруєSIGINTдля себе й повертає типову дію дитині післяfork, доexec.
Автоперевірка
Section titled “Автоперевірка”Перевірка лежить в архіві з файлами курсу, і команди нижче виконуються з розпакованого каталогу.
cd labs/a1-shell./check.sh ./myshellСкрипт проганяє дванадцять сценаріїв — від одиночної команди до тримісного конвеєра з перенаправленням — і показує, що саме не збіглося з очікуваним.
Часті помилки
Section titled “Часті помилки”Конвеєр зависає. Батько не закрив свої копії кінців каналу. Правило
таке: кожен pipe() дає два дескриптори, після fork вони існують уже
в трьох процесах, і закрити треба всі, крім тих двох, що справді потрібні.
cd не працює. Ви виконали його в дитині. Робочий каталог є атрибутом
процесу, тож змінювати треба свій власний.
Зомбі накопичуються. Немає прибирання після фонових задач. Один зомбі ніхто й не помітить, а сотня вичерпає таблицю процесів (модуль 6).
Перенаправлення застосувалося до самої оболонки. dup2 виконано
до fork замість після. Наслідок помітний одразу: оболонка перестає
щось друкувати.
execvp повернувся, і програма продовжила виконуватись. Успішний
exec не повертається ніколи, тож його повернення означає помилку.
Дитина зобов’язана завершитися через _exit, інакше у вас буде дві
оболонки замість однієї.
Далі, якщо цікаво
Section titled “Далі, якщо цікаво”Керування задачами (jobs, fg, bg) через групи процесів
і tcsetpgrp; підстановка змінних; && і ||.