Unix

Операционная система UNIX

Записи по книге А.Робачевского, С.Немнюгина, О.Стесик

Операционная система UNIX

0. Введение

В центре архитектуры unix находится ядро, оно взаимодействует с аппаратной частью компьютера, изолируя прикладные программы от особенностей архитектуры
Ядро предоставляет прикладным программам услуги с помощью системных вызовов. Например работа с файлами, создание процессов и управление ими, межпроцессное взаимодействие итд

Вокруг ядра, используя его возможности, работают приложения


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

Вот тут видно как открывается файл, читается и закрывается:

root@mars:/var/tmp/test/test# cat test.c 
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
main() {
  int fd;
  char buf[80];
  fd = open("file", O_RDONLY);
  read(fd, buf, sizeof(buf));
  close(fd);
}

root@mars:/var/tmp/test/test# strace ./a.out |& tail -5
openat(AT_FDCWD, "file", O_RDONLY)      = 3
read(3, "Mar 19 17:05:24 mars sshd[29845]"..., 80) = 80
close(3)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++
root@mars:/var/tmp/test/test# 

Ядро состоит из трех основных систем:

Файловая подсистема дает унифицированный доступ к данным и переферийным устройствам. Также она контролирует права, выполняет размещение/удаление/запись/чтение
Большая часть прикладных функций выполняется через интерфейс ФС (в том числе доступ к периферийным устройствам), поэтому права на файлы определяют привилегии пользователя в системе

Подсистема управления процессами. Запущенные программы порождают процессы, эта подсистема контролирует и управляет ими, а также распределяет ресурсы между ними

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

Подсистема ввода-вывода выполняет запросы двух предыдущих подсистем к периферийным устройствам. Она обеспечивает буферизацию и взаимодействует с драйверами
Драйверы это модули ядра которые обслуживают внешние устройства

Операционная система UNIX

1. Работа в операционной системе UNIX

Имя файла является атрибутом файловой системы, а не набора некоторых данных на диске, который не имеет имени как такового

Каждый файл имеет связанные с ним метаданные, которые хранятся в индексных дескрипторах - inode, и содержат в себе все характеристики файла, которые позволяют ОС работать с ним
В частности метаданные содержат указатели на дисковые блоки хранения данных файла

Имя файла является указателем на метаданные
В то время как метаданные никак не указывают на имя (односторонняя связь)
Отсюда становится понятно как работают хардлинки

Существует 6 типов файлов (от типа зависят возможные действия, назначение итд):

Обычный файл для операционной системы это просто набор байтов, вся их обработка выполняется уровнем выше (прикладной программой)

Каталог это файл содержащий имена находящихся в нем файлов а так же указатели на матаднные этих файлов
Каталоги определяют положение файла в дереве ФС
Прочесть каталог могут многие, но записать в него может только ядро

Специальный файл устройства обеспечивает доступ к физическому устройству
Бывают символьные и блочные, Символьные используются для небуферизованного обмена, а блочные позволяют обмениваться пакетами фиксированной длины - блоками

FIFO позволяет передвать данные между процессами

Связь. Записи в каталогах связывают имя файла и метаданные (имя -> inode), метаданные связываются с блоками данных (inode -> blocks)
Соответственно на одну и ту же айноду может указывать несколько имен и один файл может иметь несколько имен

Файлу безразлично какие имена он имеет, но не безразлично их количество
Если у файла не осталось имен в файловой системе, то он должен быть удален

Это называется жесткой связью (hardlink)

root@mars:/var/tmp/test/test# touch file1
root@mars:/var/tmp/test/test# ln file1 file2
root@mars:/var/tmp/test/test# ls -li
total 0
4856290 -rw-r--r-- 2 root root 0 Mar 19 20:33 file1
4856290 -rw-r--r-- 2 root root 0 Mar 19 20:33 file2
root@mars:/var/tmp/test/test# touch otherfile
root@mars:/var/tmp/test/test# ls -li
total 0
4856290 -rw-r--r-- 2 root root 0 Mar 19 20:33 file1
4856290 -rw-r--r-- 2 root root 0 Mar 19 20:33 file2
4856297 -rw-r--r-- 1 root root 0 Mar 19 20:33 otherfile
root@mars:/var/tmp/test/test# echo 'test' > file1
root@mars:/var/tmp/test/test# cat file2
test
root@mars:/var/tmp/test/test# 

Кроме одинаковых inode, выше виден еще и третий столбец (числа 2/1), он как раз и указывает на количество хардлинков

Символьная связь ведет себя иначе
Содержимое символьной ссылки это имя целевого файла
На примере видна логика работы

root@mars:/var/tmp/test/test/test# echo 'niga1' > file
root@mars:/var/tmp/test/test/test# mkdir test
root@mars:/var/tmp/test/test/test# cd test
root@mars:/var/tmp/test/test/test/test# echo 'niga2' > file
root@mars:/var/tmp/test/test/test/test# cd ..
root@mars:/var/tmp/test/test/test# tree
.
|-- file
`-- test
    `-- file

1 directory, 2 files

# Подготовили площадку, имеем два файла с одинаковым именем но с разным содержимым

root@mars:/var/tmp/test/test/test# ln -s file link

# Делаем ссылку с относительным именем цели и тестим

root@mars:/var/tmp/test/test/test# cat link 
niga1
root@mars:/var/tmp/test/test/test# mv link test/.
root@mars:/var/tmp/test/test/test# cat test/link 
niga2

Символьная ссылка это особый тип файла
ОС ведет себя особым образом при работе с ним

Хардлинк имеет ограничение, он может существовать только в рамках одной ФС, тогда как символические ссылки могут быть cross file system

Сокеты предназначены для межпроцессного взаимодействия, в том числе и для процессов на разных компьютерах
Поэтому сокеты часто используются для доступа к сети


lost+found - как ранее говорилось, у файла должен быть минимум один хардлинк, иначе файл должен быть удален
Но "должен" не равно "будет"
Поэтому при ошибках остановки ОС или аппаратных сбоях могут возникать ситуации когда на диске есть правильная структура файла но для него отсутсвует имя в каком-либо каталоге
Системы проверки и восстановления ФС кладут такие файлы в lost+found


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


При создании файла ему назначается владелец-пользователь и владелец-группа
Владелец-группа не обязательно должен быть первичной группой пользователя-создателя
Например в freebsd владелец-группа для файла наследуется от вышестоящего каталога

На самом деле файл создает не пользователь а процесс который пользователь породил
У этого процесса есть разные аттрибуты
Такие как UID, EUID, GID и EGID
От них как раз и зависит очень многое


Для бинарей чтобы запуститься достаточно иметь права на исполнение
А для скритов также требуется право на чтение
upd: не совсем

root@two:~# cat scr
#!/bin/bash
echo test

root@two:~# ls -l
total 4
-rw-r--r-- 1 root root 22 May  6 06:08 scr

root@two:~# . scr
test

root@two:~# chmod 100 scr

root@two:~# ls -l
total 4
---x------ 1 root root 22 May  6 06:08 scr

root@two:~# ./scr
test

Символьные ссылки вообще не имеют своих прав, они наследуют права от целевого файла

У каталогов права используются немного иначе
Право чтения каталога позволяет прочитать его, те получить список имен файлов внутри (только имена и только на один уровень вглубь) = ls
Право исполнения на каталоге позволит пройти внутрь и обратиться к метаданным файлов внутри каталога = ls -l

Чтобы зайти в папку не обязательно иметь права на ее чтение

macbook:a vandud$ pwd
/tmp/a

macbook:a vandud$ ls b
ls: b: Permission denied

macbook:a vandud$ tree
.
└── b [error opening dir]
1 directory, 0 files

macbook:a vandud$ cd b/c

macbook:c vandud$ ls -l
total 0
-rwx------  1 vandud  wheel  0 Mar 26 19:36 file

Как видно в примере выше можно перескочить папку
Достаточно лишь иметь право зайти в нее и не обязательно ее читать

Например когда ты заранее знаешь путь по которому тебе нужно оказаться

Биты x и r независимы
С помощью различных их комбинаций можно добиваться инетересных эффектов

macbook:test vandud$ mkdir a
macbook:test vandud$ chmod a-r+x a
macbook:test vandud$ touch a/file
macbook:test vandud$ ls -l a
ls: a: Permission denied
macbook:test vandud$ echo 'text' > a/file
macbook:test vandud$ cat a/file # <tab> тут не срабатывает
text

Чтобы удалить файл достаточно иметь право на запись в папку в которой он находится (не нужно иметь прав на сам файл)
Право на запись в каталог дает много полномочий
Такая проблема решается Sticky bit'ом на каталог


Даже если пользователь 'user' входит в группу 'group', он не сможет прочитать 'file', зато все остальные смогут

----rw-r-- 2 user group 0 Mar 19 20:33 file

Выше было про основные аттрибуты файлов
Но существуют также и дополнительные
Они выставляются так же командой chmod, но используются другие буквы

Code Name Description
t Sticky bit Сохранить образ выполняемого файла в памяти после завершения, чтобы в следующий раз он запустился быстрее
s Set UID, SUID Установить UID процесса при выполнении, позволяет изменить права пользователя на время выполнения файла
s Set GID, SGID Установить GID процесса при выполнении
l Блокирование Установить обязательное блокирование файла

SUID и SGID следует высталять осторожно, потому что в процессе выполнения программа может запускать другие программы и права будут наследоваться

SUID и SGID позволяют запускать файл с правами владельца-пользователя или владельца-группы

i.dudin_pro@mon-test-vm-01y:/usr/bin$ ls -l passwd
-rwsr-xr-x 1 root root 54192 May 17  2017 passwd

passwd как раз работает за счет SUID. Потому что пароли хранятся в системном файле /etc/shadow и пользователям как-то нужно иметь возможно его изменять (чтобы изменить свой пароль в нем)

Блокирование позволяет устранить возможность конфликта когда две+ задачи работают с одним файлом


Sticky bit для каталога позволяет пользователю удалять только те файлы которыми он владеет или имеет права на запись (при наличие права на запись в каталог)
Так работает раздел /tmp, в него могут писать все, но удалить можно только свое

i.dudin_pro@mon-test-vm-01y:~/test$ ls -la
total 12
drwxr-xr-x 3 i.dudin_pro linux_users 4096 Mar 26 16:18 .
drwxr-xr-x 5 i.dudin_pro linux_users 4096 Mar 26 13:56 ..
drwxr-xr-x 2 i.dudin_pro linux_users 4096 Mar 26 16:18 dir
-rw-r--r-- 1 i.dudin_pro linux_users    0 Mar 26 16:18 file
i.dudin_pro@mon-test-vm-01y:~/test$ chmod +t dir/
i.dudin_pro@mon-test-vm-01y:~/test$ chmod +t file ### можно использовать букву 'a' или 'o' или никакую как в этой строке
i.dudin_pro@mon-test-vm-01y:~/test$ ls -l
total 4
drwxr-xr-t 2 i.dudin_pro linux_users 4096 Mar 26 16:18 dir
-rw-r--r-T 1 i.dudin_pro linux_users    0 Mar 26 16:18 file
i.dudin_pro@mon-test-vm-01y:~/test$ touch file2
i.dudin_pro@mon-test-vm-01y:~/test$ mkdir dir2
i.dudin_pro@mon-test-vm-01y:~/test$ ls -l
total 8
drwxr-xr-t 2 i.dudin_pro linux_users 4096 Mar 26 16:18 dir
drwxr-xr-x 2 i.dudin_pro linux_users 4096 Mar 26 16:19 dir2
-rw-r--r-T 1 i.dudin_pro linux_users    0 Mar 26 16:18 file
-rw-r--r-- 1 i.dudin_pro linux_users    0 Mar 26 16:19 file2
i.dudin_pro@mon-test-vm-01y:~/test$ chmod a+t file2
i.dudin_pro@mon-test-vm-01y:~/test$ chmod a+t dir2
i.dudin_pro@mon-test-vm-01y:~/test$ ls -l
total 8
drwxr-xr-t 2 i.dudin_pro linux_users 4096 Mar 26 16:18 dir
drwxr-xr-t 2 i.dudin_pro linux_users 4096 Mar 26 16:19 dir2
-rw-r--r-T 1 i.dudin_pro linux_users    0 Mar 26 16:18 file
-rw-r--r-T 1 i.dudin_pro linux_users    0 Mar 26 16:19 file2

Размер буквы указывает на включенность аттрибута x

-rw-r--r-T 1 i.dudin_pro linux_users    0 Mar 26 16:21 file3
-rw-r--r-t 1 i.dudin_pro linux_users    0 Mar 26 16:22 file4

При установке SGID на каталог, новые файлы в каталоге будут наследовать владельца-группу по владельцу-группе каталога

i.dudin_pro@mon-test-vm-01y:~/test/a$ ls -la
total 8
drwxr-sr-x 2 i.dudin_pro www-data    4096 Mar 26 13:53 . # видим что текущая директория имеет sgid
drwxr-xr-x 3 i.dudin_pro linux_users 4096 Mar 26 13:53 ..

i.dudin_pro@mon-test-vm-01y:~/test/a$ touch file # создаем файл

i.dudin_pro@mon-test-vm-01y:~/test/a$ ls -la
total 8
drwxr-sr-x 2 i.dudin_pro www-data    4096 Mar 26 14:12 .
drwxr-xr-x 3 i.dudin_pro linux_users 4096 Mar 26 13:53 ..
-rw-r--r-- 1 i.dudin_pro www-data       0 Mar 26 14:12 file # файл унаследовал группу от папки (пользователь-создатель имеет другую группу

SUID на каталог не ставится


Кроме обычных и дополнительных аттрибутов, файлы могут наделяться скрытыми аттрибутами, обусловленными конкретной реализацией файловой системы

В случае конфликта прав между между обычными и специфическими аттрибутами, решают специфические

i.dudin_pro@mon-test-vm-01y:~/test$ sudo chattr +i file
i.dudin_pro@mon-test-vm-01y:~/test$ lsattr file
----i--------e-- file
i.dudin_pro@mon-test-vm-01y:~/test$ sudo rm file 
rm: cannot remove 'file': Operation not permitted # даже от рута не удаляется
i.dudin_pro@mon-test-vm-01y:~/test$ sudo chattr +a file
i.dudin_pro@mon-test-vm-01y:~/test$ lsattr file
-----a-------e-- file
i.dudin_pro@mon-test-vm-01y:~/test$ echo "test" > file # нельзя перезаписать
-bash: file: Operation not permitted
i.dudin_pro@mon-test-vm-01y:~/test$ echo "test" >> file # но можно дозаписать
i.dudin_pro@mon-test-vm-01y:~/test$ cat file
test

При настройке прав с помощью числовой комбинации можно использовать эти числа в качестве четвертого (самого левого) префикса


Для того чтобы программа могла быть запущена на выполнение, ОС сначала должна создать соответствующее окружение (среду выполнения) куда относятся ресурсы памяти, возможность доступа к устройствам и другие различные ресурсы
Это окружение называется процесс

Не следует отождествлять процесс и программу, потому что одна программа может породить множество процессов

Выполнение процесса заключается в точном следовании набору инструкций, который никогда не передает управление набору инструкций другого процесса
Процесс взаимодействует с разделом данных и стеком, но ему недоступны разделы данных и стеки других процессов

Чтобы процессы могли взаимодействовать между собой существует система межпроцессного взаимодействия
В нее входят сигналы, каналы, разделяемая память, семафоры, сообщения и файлы, но в остальном процессы изолированы друг от друга


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

Бывают просто демоны которые стртуют либо от рук пользователя либо автоматически после инициализации ядра (это разные web, ftp, ssh демоны)

Бывают прикладные процессы, они как правило интерактивные
Таким процессом является оболочка


Процессы имеют некоторые аттрибуты благодаря которым ОС может эффективно управлять ими

PID (Process ID) - уникальный в системе номер процесса позволяющий различать процессы. Новому процессу присваивается следующий свободный номер (присвоение происходит по возрастающей, т.е. чем новее процесс тем больше его pid)
Когда номера заканчиваются то новому процессу выдается минимальный из свободных (ведь при завершении процесса его pid освобождается) и все идет по второму кругу

PPID (Parent Process ID) - Pid родительского процесса
Nice Number - Относительный приоритет процесса учитываемый планировщиком при определнии очередности запуска (процессорные ресурсы распределяются исходя из приоритета выполнения который вычисляется динамически ядром и зависит в том числе от относительного приоритета)

TTY (Терминальная линия) - Терминал или псевдотерминал ассоциированный с процессом (демоны такого не имеют)

RID,EUID (Реальный и эффективный ID) - Реальный это id пользователя который запустил программу, а эффективный это id пользователя чьими правами наделен процесс. Обычно они эквивалентны, но euid можно изменить с помощью suid

RGID,EGID - То же самое что и выше но про группу


Новый процесс порождается с помощью системного вызова fork
Дочерний процесс является точной копией родительского

Дочерний отличается от родительского только pid'ом (ну и ppid'ом), все остальное (данные, инструкции, права и прочее) наследуется
Даже выполнение начнется с одного и того же момента

Далее выполняется вызов exec для загрузки кода новой программы в новый процесс и выполнения


Сигналы нужны для уведомления процессов о разных событиях
Например если процесс пытается поделить на ноль то ядро пошлет ему сигнал SIGFPE
А при нажатии Ctrl+C процесс получит сигнал SIGINT

Для отправления сигнала используется команда kill

root может слать сигналы кому угодно, а пользователь должен слать сигналы тем процессам владельцем которых он является
Должны совпадать RID/EUID процесса отправителя и процесса получателя

При получении сигнала процесс имеет три пути:

Некоторые сигналы нельзя перехватить (SIGKILL, SIGSTOP)

Но иногда и после неперехватываемых сигналов процесс может не завершиться:

Сигналы могут иметь специфичные для приложения значения
Например SIGHUP для named заставит его перечитать базу с диска


UNIX предоставляет единый интерфейс различных устройств в виде специальных файлов
Каждый файл соответствует физическому устройству или псевдоустройству
Такой файл связывает приложение с драйвером устройства

Существует два типа специальных файлов устройств

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

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


Пользователь тоже имеет аттрибуты
Их можно увидеть в файле /etc/passwd

[19:39:16] vandud@macbook: vacuum-on-db-mon [0]$ grep -vE '^#' /etc/passwd | head
nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false
root:*:0:0:System Administrator:/var/root:/bin/sh
daemon:*:1:1:System Services:/var/root:/usr/bin/false
...
  1. name - логин пользователя
  2. passwd - закодированный пароль, обычно на месте этого аттрибута стоит 'x', а сам пароль хранится в /etc/shadow, в случае если в этом месте стоит '*' то пользователь не сможет зайти в систему, обычно такой пароль имеют псевдоюзеры
  3. UID - user id
  4. GID - id первичной группы пользователя
  5. comment - обычно это реальное имя пользователя
  6. homedir - хомяк
  7. shell - шелл

root имеет uid=0
root - это только имя, неограниченные полномочия имеет пользователь с uid=0 (то есть можно сменить root на что-то свое)

Аналогично с группой root или wheel которая имеет GID=0

Сегодня нормой считается создание персональной группы для каждого пользователя
Таким образом исключается случайное превышение полномочий за счет групповых прав


При включении терминала активируется программа getty, она является сервером терминального доступа и запускает программу login, которая после успешной аутентификации запускает программу из последнего столбца /etc/passwd (оболочку)

Начальное окружение устанавливается программой login
А именно:


Экспорт переменных

[21:28:33] vandud@macbook: ~ [0]$ echo $TEST

[21:28:43] vandud@macbook: ~ [0]$ TEST=test
[21:28:48] vandud@macbook: ~ [0]$ echo $TEST
test
[21:28:51] vandud@macbook: ~ [0]$ bash -c 'echo $TEST'

[21:28:59] vandud@macbook: ~ [0]$ export TEST=test
[21:29:08] vandud@macbook: ~ [0]$ bash -c 'echo $TEST'
test
[21:29:11] vandud@macbook: ~ [0]$

Просто определнные переменные являются внутренними текущего экземпляра оболочки и не наследуются порожденными процессами
Чтобы они начали наследоваться нужно обозначить их как экспортируемые


Любая команда в оболочке относится к одной из трех категорий:

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


В bash есть функция exec
Она делает то что описано выше про системеный вызов exec

[14:22:09] vandud@macbook: ~ [0]$ ssh mon-test-vm-01y.g01.i-free.ru
i.dudin_pro@mon-test-vm-01y:~$ exec ps ### Вызываю ps через exec
  PID TTY          TIME CMD
 2268 pts/0    00:00:00 ps ### Видим только ps
Connection to mon-test-vm-01y.g01.i-free.ru closed. ### И вылетаем (потому что ps завершился, а bash'a у нас нет)

[14:22:23] vandud@macbook: ~ [0]$ ssh mon-test-vm-01y.g01.i-free.ru
i.dudin_pro@mon-test-vm-01y:~$ ps ### То же самое но без exec
  PID TTY          TIME CMD
 2295 pts/0    00:00:00 bash ### bash есть
 2310 pts/0    00:00:00 ps
i.dudin_pro@mon-test-vm-01y:~$ ### И мы не вылетаем

У bash есть функция для определения действий которые будут выполнены при получении bash'ем сигналов

i.dudin_pro@mon-test-vm-01y:~$ trap 'echo signal recieved' 14
i.dudin_pro@mon-test-vm-01y:~$ ps
  PID TTY          TIME CMD
 3687 pts/1    00:00:00 bash
 3705 pts/1    00:00:00 ps
i.dudin_pro@mon-test-vm-01y:~$ kill -14 3687
signal recieved
i.dudin_pro@mon-test-vm-01y:~$

Функции в bash не особо отличаются от скриптов, в них так же предаются аргументы и внутри перечисляются команды
Но функции располагаются внутри памяти и поэтому работают быстрее

Операционная система UNIX

2. Среда программирования UNIX

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

Системные вызовы описываются в man (2)

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

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

Список кодов ошибок основных сисколлов стандартизован (хотя появляются новые вызовы и их толкования могут различаться)

Полный список можно найти тут

root@two:/usr/include/asm-generic# tail errno.h

/* for robust mutexes */
#define	EOWNERDEAD	130	/* Owner died */
#define	ENOTRECOVERABLE	131	/* State not recoverable */

#define ERFKILL		132	/* Operation not possible due to RF-kill */

#define EHWPOISON	133	/* Memory page has hardware error */

#endif

Или в /usr/include/errno.h


На схеме ниже описана процедура создания приложения

IMG_1603.jpg

  1. Компиляция: файлы с текстами программы обрабатываются компилятором (gcc), параметры компиляции определяются в Makefile. Компилятор создает набор промежуточных объектых файлов .o
  2. Все эти файлы связываются друг с другом и с библиотеками линковщиком (ld) в статическом или динамическом режиме

Виртуальная память процесса состоит из нескольких сегментов или областей памяти
Размер, содержимое и расположение сегментов определяется как самой программой так и ее форматом
Обычно используется ELF (executable and linking format)

IMG_1604.jpg


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

Это позволяет программе не заботиться о поиске устройства пользователя который ее запустил

Основные способы завершения программы это возврат из функции main() и вызов фукции exit()
Процесс может завершиться по независящим от него обстаятельствам (например получение сигнала функция exit будет вызвана ядром от имени процесса)


Для работы с файлами программа может воспользоваться системными вызовами ядра или библиотечными функциями
Понятно что библиотечные функции это надстройки над системными вызовами

При открытии файла, процессу возвращается файловый дескриптор. Дальнейшее взаимодействие с этим файлом будет происходить по этому дескриптору
Значение этого дескритора определяется минимальным свободным слотом в таблице дескрипторов процесса

У функции open есть флаги, много разных, один из интересных - O_NONBLOCK
Он позволяет сделать операции с файлом - неблокирующими
То есть при чтении/записи возврат будет происходить мгновенно

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

При записи данных в файл, функции write передается дескритор (куда писать) и указатель на буфер в котором хранятся данные для записи

Функция pipe позволяет создать однонаправленный (симплексный) канал для обмена данными между двумя родственными процессами
Только родственные процессы могут получить доступ к одному и тому же каналу
Функция вернет два файловых дескриптора, первый для чтения, второй для записи