Процесcы в операционной системе Linux (основные понятия)

Основными активными сущностями в системе Linux являются процессы. Каждый процесс выполняет одну программу и изначально получает один поток управления. Иначе говоря, у процесса есть один счетчик команд, который отслеживает следующую исполняемую команду. Linux позволяет процессу создавать дополнительные потоки (после того, как он начинает выполнение).

Linux представляет собой многозадачную систему, так что несколько независимых процессов могут работать одновременно. Более того, у каждого пользователя может быть одновременно несколько активных процессов, так что в большой системе могут одновременно работать cотни и даже тысячи процессов. Фактически на большинстве однопользовательских рабочих станций (даже когда пользователь куда-либо отлучился) работают десятки фоновых процессов, называемых демонами (daemons). Они запускаются при загрузке системы из сценария оболочки.

Типичным демоном является cron. Он просыпается раз в минуту, проверяя, не нужно ли ему что-то сделать. Если у него есть работа, то он ее выполняет, а затем отправляется спать дальше (до следующей проверки).

Этот демон позволяет планировать в системе Linux активность на минуты, часы, дни и даже месяцы вперед. Например, представьте, что пользователю назначено явиться во военкомат в 3 часа дня в следующий вторник. Он может создать запись в базе данных демона cron, чтобы тот просигналил ему, скажем, в 14:30. Когда наступает назначенный день и время, демон cron видит, что у него есть работа, и запускает в назначенное время программу звукового сигнала (в виде нового процесса).

Демон cron также используется для периодического запуска задач, например ежедневного резервного копирования диска в 4 часа ночи или напоминания забывчивым пользователям каждый год за неделю до 31 декабря купить подарки для празднования нового года. Другие демоны управляют входящей и исходящей электронной почтой, очередями принтера, проверяют, достаточно ли еще осталось свободных страниц памяти и т.д. Демоны реализуются в системе Linux довольно просто, так как каждый из них представляет собой отдельный процесс, независимый от всех остальных процессов.

Процессы создаются в операционной системе Linux очень просто. Системный вызов fork создает точную копию исходного процесса, называемого родительским процессом (parent process). Новый процесс называется дочерним процессом (child process). У родительского и у дочернего процессов есть свои собственные (приватные) образы памяти. Если родительский процесс впоследствии изменяет какие-либо свои переменные, то эти изменения остаются невидимыми для дочернего процесса (и наоборот).

Открытые файлы используются родительским и дочерним процессами совместно. Это значит, что если какой-либо файл был открыт в родительском процессе до выполнения системного вызова fork, то он останется открытым в обоих процессах и в дальнейшем. Изменения, произведенные с этим файлом любым из процессов, будут видны другому. Такое поведение является единственно разумным, так как эти изменения будут видны также и любому другому процессу, который тоже откроет этот файл.

Тот факт, что образы памяти, переменные, регистры и все остальное и у родительского процесса, и у дочернего идентичны, приводит к небольшому затруднению: как процессам узнать, который из них должен исполнять родительский код, а который дочерний? Секрет в том, что системный вызов fork возвращает дочернему процессу число 0, а родительскому — отличный от нуля PID (Process IDentifier — идентификатор процесса) дочернего процесса. Оба процесса обычно проверяют возвращаемое значение и действуют соответственно:

pid = fork( ); /* если fork завершился успешно, pid > 0 в родительском процессе */
if (pid < 0) {
handle_error(); /* fork потерпел неудачу (например, память или какая-
либо таблица переполнена) */
} else if (pid > 0) {
/* здесь располагается родительский код */
} else {
/* здесь располагается дочерний код */
}


Если дочерний процесс желает узнать свой PID, то он может воспользоваться системным вызовом getpid. Идентификаторы процессов используются различным образом. Например, когда дочерний процесс завершается, его родитель получает PID только что завершившегося дочернего процесса. Это может быть важно, так как у родительского процесса может быть много дочерних процессов. Поскольку у дочерних процессов также могут быть дочерние процессы, то исходный процесс может создать целое дерево детей, внуков, правнуков и более дальних потомков.

В системе Linux процессы могут общаться друг с другом с помощью некой формы передачи сообщений. Можно создать канал между двумя процессами, в который один процесс сможет писать поток байтов, а другой процесс сможет его читать. Эти каналы иногда называют трубами (pipes). Синхронизация процессов достигается путем блокирования процесса при попытке прочитать данные из пустого канала. Когда данные появляются в канале, процесс разблокируется.

При помощи каналов организуются конвейеры оболочки. Когда оболочка видит строку вроде
sort <f | head
то она создает два процесса, sort и head, а также устанавливает между ними канал таким образом, что стандартный поток вывода программы sort соединяется со стандартным потоком ввода программы head. При этом все данные, которые пишет sort, попадают напрямую к head, для чего не требуется временного файла. Если канал переполняется, то система приостанавливает работу sort до тех пор, пока head не удалит из него хоть сколько-нибудь данных.

Процессы также могут общаться и другим способом — при помощи программных прерываний. Один процесс может послать другому так называемый сигнал (signal). Процессы могут сообщить системе, какие действия следует предпринимать, когда придет сигнал. Варианты такие: проигнорировать сигнал, перехватить его, позволить сигналу убить процесс (действие по умолчанию для большинства сигналов). Если процесс выбрал перехват посылаемых ему сигналов, он должен указать процедуру обработки сигналов. Когда сигнал прибывает, управление сразу же передается обработчику. Когда процедура обработки сигнала завершает свою работу, то управление снова передается в то место, в котором оно находилось, когда пришел сигнал (это аналогично обработке аппаратных прерываний ввода-вывода). Процесс может посылать сигналы только членам своей группы процессов (process group), состоящей из его прямого родителя (и других предков), братьев и сестер, а также детей (и прочих потомков). Процесс может также послать сигнал сразу всей своей группе за один системный вызов.

Сигналы используются и для других целей. Например, если процесс выполняет вычисления с плавающей точкой и непреднамеренно делит на 0, то он получает сигнал SIGFPE (Floating-Point Exception SIGnal — сигнал исключения при выполнении операции с плавающей точкой).
Поделиться публикацией

Комментарии 37

    +23
    > Системный вызов fork создает точную копию исходного процесса

    Хотелось бы уточнить, что Linux ядро не создает точную копию процесса, т.к. это накладно и глупо.
    Вместо этого используется механизм copy-on-write. Т.е. создается просто дубликат таблиц адресного пространства
    родителя, копирование происходит, только если процесс начинает менять данные. Но тут есть еще одна хитрость.
    Сразу после fork() потомок получает более высокий приоритет, чем родитель и соответственно новая программа
    успевает начать выполняться раньше :)
      0
      Не знал таких тонкостей, спасибо.
        0
        Временами это успевает попортить кровь при написании скриптов.
          +2
          Ну так нужно намертво усвоить, что никакой гарантии порядка выполнения многопоточных приложений нет, если специально их не синхронизировать.
            +1
            Знаете, когда пишешь какую-нибудь дребедень для разового использования не всегда приходит в голову, что ты пишешь «многопоточное приложение без специальной синхронизации». Чаще всего думаешь «ща по-быстрому костыль приделаю». Первые пару раз я сильно удивлялся, что некоторые команды в скрипте отрабатывают не последовательно, потом стал осмотрительнее.
          +5
          > Сразу после fork() потомок получает более высокий приоритет, чем родитель и соответственно новая программа успевает начать выполняться раньше

          Вообщето это не совсем так. Кто запускается после fork — parent or child завист от параметра заданного в

          /proc/sys/kernel/sched_child_runs_first

          Когда-то давным давно первым запускался child, затем в 2.2 поменяли на parent, затем в 2.6 поменяли на child но добавили опцию в /proc. В 2.6.32 по-умолчанию поменяли обратно на parent-first :)

          Почему же parent-first лучше? Дело в том что child обычно сразу же вызывает exec что убивает кеши процессора. А если parent продолжает выполнение, то используются текущие кеши и производительность повышается.

          А еще в linux есть vfork — оптимизированная версия fork которая не копирует таблицы адресов. Хотя она не posix и не рекомендуется к использованию.
            +3
            Забыл добавить что эту и другую увлекательную информацию вы можете почерпнуть из великолепной книги Керриска «The Linux Programming Interface»
          +10
          Статья из книжки линугз для школоты.
            +26
            Совсем охренели. копипаст же: www.nrjetix.com/fileadmin/doc/publications/Lectures/Lecture13.pdf

            >01 августа 2011 в 13:18 по приглашению НЛО

            прекрасно.
              –10
              Данная статья написана по лекции, прослушенной в университете. Сам преподаватель расхваливал книгу Эндрю Таненбаумана «Современные операционные системы». Возможно лекции сплошняком брались оттуда, не проверял. Но если это так, тогда www.nrjetix.com/fileadmin/doc/publications/Lectures/Lecture13.pdf тоже копипаст.
                +9
                >Данная статья написана по лекции, прослушенной в университете

                перешлите инвайт преподу.
                  +13
                  Лучше Танненбауму. Он оценит
                  +4
                  А вы еще одногрупникам предложите написать каждому по статье на хабр «что я смог понять и запомнить из последней лекции в универе».
                  Чего уж там мелочиться.
                    –1
                    90% не смогут ничего написать. А что Вы кстати написали содержательного за последнее время?
                      +3
                      «Можешь не писать хуйни, в которой не разбираешься — не пиши».
                      Мне пока вполне удается следдовать этому принципу.
                  0
                  А где выкопать все лекции??? Это бесценно
                  0
                  На мой взгляд, даже если копипаст, всё равно спасибо автору, ибо это тот вопрос, который был не ясен мне.
                    0
                    Всегда пожалуйста. Только не ожидал я такой реации на то, что если я взял что-то за основу и честно в этом признался, то человека обязательно надо зачмырить. Я и не знал, что большинство людей здесь родились со знаниями в IT.
                      0
                      Есть странный парадокс, люди которые что-то знают, начинают считать что другие это знают тоже, и когда кто-то решает для других объяснить это толковым языком, начинают за это чмырить.
                        +1
                        Более странный парадокс, что Вы одобряете копипасту. В данном случае Танненбаум -> Лектор -> Студент -> Хабр.
                        По правилам Хабра все же публиковаться должен авторский контент, а не реферат.

                        > 90% не смогут ничего написать. А что Вы кстати написали содержательного за последнее время?

                        Вы тоже ничего не написали сами, только пересказали лекцию, и даже не имеете представления, что первоисточник Таненбаум. От себя или из других источников абсолютно ничего не добавлено.

                        Еще более печально, что на группу студентов (по Вашим оценкам) только один-два могут вразумительно рассказать, что было на лекции. И опять же, судя по Вашим оценкам, — это потолок способностей 100%.
                          0
                          Отвечаю на то, что адресовано лично мне.

                          Копипасту не одобряю. Но это именно то, что было нужно мне. И хотелось бы найти весь курс лекций (кто подскажет где?)
                            0
                            Книга Эндрю Таненбаум «Современные операционные системы»
                            0
                            Я бы попросил не путать реферат и копипаст.
                              0
                              То, что реферат производное произведение, не значит, что оно не авторское. Другое дело, что ссылки на источники должны быть.
                                0
                                У современных студентов в большинстве случаев оно не авторское. И речь вовсе не об этом.
                                  0
                                  Запрещён правилами полный копипаст. Тут явно не он, а скорее грубый рерайт, что правилами, вроде бы, не запрещено, а народ топик поддерживает, намекая, что содержание интересно, но карму опустил, намекая что топик-ссылка был бы уместнее. Саморегуляция :)
                              +3
                              а вам, простите, религия не позволяет читать что-то, написанное толковым языком, пока его нет на главной хабра?
                                +2
                                Вот что полезного в этой статье?
                                Лично для вас, обнаружившего в ней толковый язык?
                                Вот какие можно сделать из статьи для себя выводы поо пунктам:
                                Что такое такое «фоновый процесс aka daemon»?
                                Откуда они берутся в системе?
                                Как помянутый cron может запускать другие процессы?
                                Насколько точна фраза «Процесс может посылать сигналы только членам своей группы процессов»? И как это согласуется с действительностью, данной нам в ощущениях?

                                0
                                Все претензии — за «Карузо в исполнении Рабиновича»
                              +1
                              плохо лежавшее не является спизженным
                              +4
                              Очень здорово написать про fork и забыть про exec, без которого механизм порождения дочернего процесса бессмысленен чуть менее, чем полностью.
                                0
                                +1
                                К тому же вызов sys_clone поудачнее будет, т.к. позволят родителю иметь общую память с ребёнком…
                                  0
                                  Если уж на то пошло — fork() как таковой и не используется — только clone() (это если я еще что-то помню после универа)
                                    0
                                    Для тредов — clone. Для создания процесса из нового бинарника — vfork(). fork() — только в случаях, когда нужен fork().
                                      +1
                                      clone универсален (поведение задается флагами) и, например, в glibc эмулирует fork: glibc-20090518/nptl/sysdeps/unix/sysv/linux/i386/fork.c

                                      #define ARCH_FORK() \
                                      INLINE_SYSCALL (clone, 5, \
                                      CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD, 0, \
                                      NULL, NULL, &THREAD_SELF->tid)

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                              Самое читаемое