Linux программирование в примерах - Страница 7
Ядро распознает, что исполняемый файл содержит двоичный объектный код, проверяя первые несколько байтов файла на предмет совпадения со специальными магическими числами. Это последовательности двух или четырех байтов, которые ядро распознает в качестве специальных. Для обратной совместимости современные Unix-системы распознают несколько форматов. Файлы ELF начинаются с четырех символов «
177ELF
Помимо двоичных исполняемых файлов, ядро поддерживает также исполняемые сценарии (скрипты). Такой файл также начинается с магического числа: в этом случае, это два обычных символа
# !
#!
#! /bin/awk -f
BEGIN {print "hello, world"}
Предположим, указанное содержимое располагается в файле
hello.awk
hello.awk
/bin/awk -f hello.awk
awk
hello, world
Механизм с использованием
#!
hello.awk
hello
hello
hello
1.1.4. Устройства
Одним из самых замечательных новшеств Unix было объединение файлового ввода- вывода и ввода-вывода от устройств.[14] Устройства выглядят в файловой системе как файлы, для доступа к ним используются обычные права доступа, а для их открытия, чтения, записи и закрытия используются те же самые системные вызовы ввода-вывода. Вся «магия», заставляющая устройства выглядеть подобно файлам, скрыта в ядре. Это просто другой аспект движущего принципа простоты в действии, мы можем выразить это как никаких частных случаев для кода пользователя.
В повседневной практике, в частности, на уровне оболочки, часто появляются два устройства:
/dev/null
/dev/tty
/dev/null
/dev/null
/dev/tty
Системы GNU/Linux и многие современные системы Unix предоставляют устройства
/dev/stdin
/dev/stdout
/dev/stderr
Другие устройства представляют реальное оборудование, такое, как ленточные и дисковые приводы, приводы CD-ROM и последовательные порты. Имеются также программные устройства, такие, как псевдотерминалы, которые используются для сетевых входов в систему и систем управления окнами,
/dev/console
/dev/console
К сожалению, соглашения по именованию устройств не стандартизированы, и каждая операционная система использует для лент, дисков и т.п. собственные имена. (К счастью, это не представляет проблемы для того, что мы рассматриваем в данной книге.) Устройства имеют в выводе '
ls -l
b
с
$ ls -l /dev/tty /dev/hda
brw-rw-rw- 1 root disk 3, 0 Aug 31 02:31 /dev/hda
crw-rw-rw- 1 root root 5, 0 Feb 26 08:44 /dev/tty
Начальная '
b
c
1.2. Модель процессов Linux/Unix
Процесс является работающей программой.[15] Процесс имеет следующие атрибуты:
уникальный идентификатор процесса (PID);
• родительский процесс (с соответствующим идентификатором, PPID);
• идентификаторы прав доступа (UID, GID, набор групп и т.д.);
• отдельное от всех других процессов адресное пространство;
• программа, работающая в этом адресном пространстве;
• текущий рабочий каталог ('
.
• текущий корневой каталог (
/
• набор открытых файлов, каталогов, или и того, и другого;
• маска запретов доступа, использующаяся при создании новых файлов;
• набор строк, представляющих окружение[16];
• приоритеты распределения времени процессора (продвинутая тема);
• установки для размещения сигналов (signal disposition) (продвинутая тема); управляющий терминал (тоже продвинутая тема).
Когда функция
main()
Новые процессы всегда создаются существующими процессами. Существующий процесс называется родительским, а новый процесс — порожденным. При загрузке ядро вручную создает первый, изначальный процесс, который запускает программу
/sbin/init
init
init
Отношение порожденный-родительский является отношением один к одному; у каждого процесса есть только один родитель, поэтому легко выяснить PID родителя. Отношение родительский-порожденный является отношением один ко многим; каждый данный процесс может создать потенциально неограниченное число порожденных. Поэтому для процесса нет простого способа выяснить все PID своих потомков. (Во всяком случае, на практике это не требуется.) Родительский процесс можно настроить так, чтобы он получал уведомление при завершении порожденного процесса, он может также явным образом ожидать наступления такого события.