355 500 произведений, 25 200 авторов.

Электронная библиотека книг » Андрей Робачевский » Операционная система UNIX » Текст книги (страница 4)
Операционная система UNIX
  • Текст добавлен: 6 октября 2016, 02:43

Текст книги "Операционная система UNIX"


Автор книги: Андрей Робачевский


Жанр:

   

ОС и Сети


сообщить о нарушении

Текущая страница: 4 (всего у книги 39 страниц)

Дополнительные атрибуты файла

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

Дополнительные атрибуты также устанавливаются утилитой chmod(1), но вместо кодов 'r', 'w' или 'x' используются коды из табл. 1.3. Например, для установки атрибута SGID для файла file1 необходимо выполнить команду $ chmod g+s file1.

В табл. 1.3 приведены дополнительные атрибуты для файлов, и показано, как они интерпретируются операционной системой.

Таблица 1.3. Дополнительные атрибуты для обычных файлов


tSticky bitСохранить образ выполняемого файла в памяти после завершения выполнения
sSet UID, SUIDУстановить UID процесса при выполнении
sSet GID, SGIDУстановить GID процесса при выполнении
1БлокированиеУстановить обязательное блокирование файла

Установка атрибута Sticky bit (действительное название – save text mode) редко используется в современных версиях UNIX для файлов. В ранних версиях этот атрибут применялся с целью уменьшить время загрузки наиболее часто запускаемых программ (например, редактора или командного интерпретатора). После завершения выполнения задачи ее образ (т.е. код и данные) оставались в памяти, поэтому последующие запуски этой программы занимали значительно меньше времени.

Атрибуты (или флаги) SUID и SGID позволяют изменить права пользователя при запуске на выполнение файла, имеющего эти атрибуты. При этом привилегии будут изменены (обычно расширены) лишь на время выполнения и только в отношении этой программы[7]7
  Следует оговориться, что если программа в процессе выполнения запускает другие задачи, то они будут наследовать ее права доступа. Поэтому устанавливать флаги SUID и SGID следует с большой осторожностью и только для программ, которые не имеют возможности запуска произвольных задач.


[Закрыть]
.

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

В качестве примера использования этого свойства рассмотрим утилиту passwd(1), позволяющую пользователю изменить свой пароль. Очевидно, что изменение пароля должно привести к изменению содержимого определенных системных файлов (файла пароля /etc/passwd или /etc/shadow, или базы данных пользователей, если используется дополнительная защита системы). Понятно, что предоставление права на запись в эти файлы всем пользователям системы является отнюдь не лучшим решением. Установка SUID для программы passwd(1) (точнее, на файл /usr/bin/passwd – исполняемый файл утилиты passwd(1)) позволяет изящно разрешить это противоречие. Поскольку владельцем файла /usr/bin/passwd является суперпользователь (его имя в системе – root), то кто бы ни запустил утилиту passwd(1) на выполнение, во время работы данной программы он временно получает права суперпользователя, т. е. может производить запись в системные файлы, защищенные от остальных пользователей.

$ ls -lFa /usr/bin/passwd

-r-sr-sr-x 3 root sys 15688 Oct 25 1995 /usr/bin/passwd*

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

Блокирование файлов позволяет устранить возможность конфликта, когда две или более задачи одновременно работают с одним и тем же файлом. К этому вопросу мы вернемся в главе 4.

Однако вернемся к обсуждению дополнительных атрибутов для каталогов (табл. 1.4).

Таблица 1.4. Дополнительные атрибуты для каталогов


tSticky bitПозволяет пользователю удалять только файлы, которыми он владеет или имеет права на запись
sSet GID, SGIDПозволяет изменить правило установки владельца– группы создаваемых файлов, аналогично реализованному в BSD UNIX

При обсуждении прав доступа отмечалось, что предоставление права на запись в каталог дает достаточно большие полномочия. Имея такое право, пользователь может удалить из каталога любой файл, даже тот, владельцем которого он не является и в отношении которого не имеет никаких прав. Установка атрибута Sticky bit для каталога позволяет установить дополнительную защиту файлов, находящихся в каталоге. Из такого каталога пользователь может удалить только файлы, которыми он владеет, или на которые он имеет явное право доступа на запись, даже при наличии права на запись в каталог. Примером может служить каталог /tmp, который является открытым на запись для всех пользователей, но в котором может оказаться нежелательной возможность удаления пользователем чужих временных файлов.

Атрибут SGID также имеет иное значение для каталогов. При установке этого атрибута для каталога вновь созданные файлы этого каталога будут наследовать владельца-группу по владельцу-группе каталога. Таким образом для UNIX версии System V удается имитировать поведение систем версии BSD, для которых такое правило наследования действует по умолчанию.

Посмотреть наличие дополнительных атрибутов можно с помощью подробного списка файлов:

$ ls -l

...

drwxrwxrwt 5 sys  sys    367 Dec 19 20:29 /tmp

-r-sr-sr-x 3 root root 15688 Oct 25 1995  /usr/bin/passwd

...

Таблица 1.5. Операции изменения атрибутов файла


Изменение прав доступа chmod(1) владелец
Изменение дополнительного атрибута Sticky bit chmod(1) суперпользователь
Изменение дополнительного атрибута SGID chmod(1) владелец, причем его GID также должен совпадать с идентификатором группы файла

Процессы

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

Программы и процессы

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

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

В то же время не следует отождествлять процесс с программой хотя бы потому, что программа может породить более одного процесса. Простейшие программы, например, команда who(1) или cat(1), при выполнении представлены только одним процессом. Сложные задачи, например системные серверы (печати, FTP, Telnet), порождают в системе несколько одновременно выполняющихся процессов.

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

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

В то же время процессы имеют возможность обмениваться друг с другом данными с помощью предоставляемой UNIX системой межпроцессного взаимодействия. В UNIX существует набор средств взаимодействия между процессами, таких как сигналы (signals), каналы (pipes), разделяемая память (shared memory), семафоры (semaphores), сообщения (messages) и файлы, но в остальном процессы изолированы друг от друга.

Типы процессовСистемные процессы

Системные процессы являются частью ядра и всегда расположены в оперативной памяти. Системные процессы не имеют соответствующих им программ в виде исполняемых файлов и запускаются особым образом при инициализации ядра системы. Выполняемые инструкции и данные этих процессов находятся в ядре системы, таким образом они могут вызывать функции и обращаться к данным, недоступным для остальных процессов. Системными процессами являются: shed (диспетчер свопинга), vhand (диспетчер страничного замещения), bdfflush (диспетчер буферного кэша) и kmadaemon (диспетчер памяти ядра). К системным процессам следует отнести ink, являющийся прародителем всех остальных процессов в UNIX. Хотя init не является частью ядра, и его запуск происходит из исполняемого файла (/etc/init), его работа жизненно важна для функционирования всей системы в целом.

Демоны

Демоны – это неинтерактивные процессы, которые запускаются обычным образом – путем загрузки в память соответствующих им программ (исполняемых файлов), и выполняются в фоновом режиме. Обычно демоны запускаются при инициализации системы (но после инициализации ядра, подробнее см. главу 3) и обеспечивают работу различных подсистем UNIX: системы терминального доступа, системы печати, системы сетевого доступа и сетевых услуг и т.п. Демоны не связаны ни с одним пользовательским сеансом работы и не могут непосредственно управляться пользователем. Большую часть времени демоны ожидают пока тот или иной процесс запросит определенную услугу, например, доступ к файловому архиву или печать документа.

Прикладные процессы

К прикладным процессам относятся все остальные процессы, выполняющиеся в системе. Как правило, это процессы, порожденные в рамках пользовательского сеанса работы. С такими процессами вы будете сталкиваться чаще всего. Например, запуск команды ls(1) породит соответствующий процесс этого типа. Важнейшим пользовательским процессом является основной командный интерпретатор (login shell), который обеспечивает вашу работу в UNIX. Он запускается сразу же после вашей регистрации в системе, а завершение работы login shell приводит к отключению от системы.

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

Интерактивные процессы монопольно владеют терминалом, и пока такой процесс не завершит свое выполнение, пользователь не сможет работать с другими приложениями.[8]8
  Вы сможете работать с другими приложениями, если в функции интерактивного процесса входит запуск на выполнение других программ. Примером такой задачи является командный интерпретатор shell, который считывает пользовательский ввод и запускает соответствующие задачи. Более типичным в данном контексте является процесс, порожденный командой ps(1). Пока ps(1) не завершит работу, вы не сможете вводить команды shell.


[Закрыть]

Атрибуты процесса

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

Идентификатор процесса Process ID (PID)

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

Идентификатор родительского процесса Parent Process ID (PPID)

Идентификатор процесса, породившего данный процесс.

Приоритет процесса (Nice Number)

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

Терминальная линия (TTY)

Терминал или псевдотерминал, ассоциированный с процессом, если такой существует. Процессы-демоны не имеют ассоциированного терминала.

Реальный (RID) и эффективный (EUID) идентификаторы пользователя

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

Реальный (RGID) и эффективный (EGID) идентификаторы группы

Реальный идентификатор группы равен идентификатору первичной или текущей группы пользователя, запустившего процесс. Эффективный идентификатор служит для определения прав доступа к системным ресурсам по классу доступа группы. Так же как и для эффективного идентификатора пользователя, возможна его установка равным идентификатору группы владельца исполняемого файла (флаг SGID).

Команда ps(1) (process status) позволяет вывести список процессов, выполняющихся в системе, и их атрибуты:

$ ps -ef | head -20

UID  PID  PPID С STIME  TTY   TIME CMD

root 0    0    0 Dec 17 ?     0:00 sched

root 1    0    0 Dec 17 ?     0:01 /etc/init -

root 2    0    0 Dec 17 ?     0:00 pageout

root 3    0    0 Dec 17 ?     7:00 fsflush

root 164  1    0 Dec 17 ?     0:01 /usr/lib/sendmail -bd -q1h

fed  627  311  0 Dec 17 pts/3 0:27 emiclock

fed  314  304  0 Dec 17 pts/4 0:00 /usr/local/bin/bash

fed  3521 512  0              0:01

Более подробное описание полей вывода команды ps(1) приведено далее в разделе «Основные утилиты UNIX».

Жизненный путь процесса

Процесс в UNIX создается системным вызовом fork(2). Процесс, сделавший вызов fork(2) называется родительским, а вновь созданный процесс – дочерним. Новый процесс является точной копией породившего его процесса. Как это ни удивительно, но новый процесс имеет те же инструкции и данные, что и его родитель. Более того, выполнение родительского и дочернего процесса начнется с одной и той же инструкции, следующей за вызовом fork(2). Единственно, чем они различаются – это идентификатором процесса PID. Каждый процесс имеет одного родителя, но может иметь несколько дочерних процессов.

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

В UNIX запуск на выполнение новой программы часто связан с порождением нового процесса, таким образом сначала процесс выполняет вызов fork(2), порождая дочерний процесс, который затем выполняет exec(2), полностью замещаясь новой программой.

Рассмотрим эту схему на примере.

Допустим, пользователь, работая в командном режиме (в командном интерпретаторе shell) запускает команду ls(1). Текущий процесс (shell) делает вызов fork(2), порождая вторую копию shell. В свою очередь, порожденный shell вызывает exec(2), указывая в качестве параметра имя исполняемого файла, образ которого необходимо загрузить в память вместо кода shell. Код ls(1) замещает код порожденного shell, и утилита ls(1) начинает выполняться. По завершении работы ls(1) созданный процесс «умирает». Пользователь вновь возвращается в командный режим. Описанный процесс представлен на рис. 1.5. Мы также проиллюстрируем работу командного интерпретатора в примере, приведенном в главе 2.

Рис. 1.5. Создание процесса и запуск программы

Если сделать "отпечаток" выполняемых процессов, например командой ps(1), между указанными стадиями, результат был бы следующим:

Пользователь работает в командном режиме:

UID   PID PPID С  STIME    TTY   TIME CMD

user1 745 1    10 10:11:34 ttyp4 0:01 sh

Пользователь запустил команду ls(1), и shell произвел вызов fork(2):

UID   PID PPID С  STIME    TTY   TIME CMD

user1 745 1    10 10:11:34 ttyp4 0:01 sh

user1 802 745  14 11:00:00 ttyp4 0:00 sh

Порожденный shell произвел вызов exec(2):

UID   PID PPID С  STIME    TTY   TIME CMD

user1 745 1    10 10:11:34 ttyp4 0:01 sh

user1 802 745  12 11:00:00 ttyp4 0:00 ls

Процесс ls(1) завершил работу:

UID   PID PPID С  STIME    TTY   TIME CMD

user1 745 1    10 10:11:34 ttyp4 0:01 sh

Описанная процедура запуска новой программы называется fork-and-exec.

Однако бывают ситуации, когда достаточно одного вызова fork(2) без последующего exec(2). В этом случае исполняемый код родительского процесса должен содержать логическое ветвление для родительского и дочернего процессов[9]9
  Такое ветвление можно организовать на основании значения, возвращаемого системным вызовом fork(2). Для родительского процесса fork возвращает идентификатор созданного дочернего процесса, а дочерний процесс получает значение, равное 0. Подробнее эти вопросы будут рассмотрены в главе 2.


[Закрыть]
.

Все процессы в UNIX создаются посредством вызова fork(2). Запуск на выполнение новых задач осуществляется либо по схеме fork-and-exec, либо с помощью exec(2). «Прародителем» всех процессов является процесс init(1М), называемый также распределителем процессов. Если построить граф «родственных отношений» между процессами, то получится дерево, корнем которого является init(1M). Показанные на рис. 1.6 процессы sched и vhand являются системными и формально не входят в иерархию (они будут рассматриваться в следующих главах).

Рис. 1.6. Типичное «дерево» процессов в UNIX

Сигналы

Сигналы являются способом передачи от одного процесса другому или от ядра операционной системы какому-либо процессу уведомления о возникновении определенного события. Сигналы можно рассматривать как простейшую форму межпроцессного взаимодействия. В то же время сигналы больше напоминают программные прерывания, – средство, с помощью которого нормальное выполнение процесса может быть прервано. Например, если процесс производит деление на 0, ядро посылает ему сигнал SIGFPE, а при нажатии клавиш прерывания, обычно <Del> или <Ctrl>+<C>, текущему процессу посылается сигнал SIGINT.

Для отправления сигнала служит команда kill(1):

kill sig_no pid

где sig_nо – номер или символическое название сигнала, a pid – идентификатор процесса, которому посылается сигнал. Администратор системы может посылать сигналы любым процессам, обычный же пользователь может посылать сигналы только процессам, владельцем которых он является (реальный и эффективный идентификаторы процесса должны совпадать с идентификатором пользователя[10]10
  Точнее, с реальным и эффективным идентификаторами процесса, посылающего сигнал. Если вы посылаете сигнал командой kill(1), работая в shell, то речь идет о командном интерпретаторе.


[Закрыть]
). Например, чтобы послать процессу, который вы только что запустили в фоновом режиме, сигнал завершения выполнения SIGTERM, можно воспользоваться командой:

$ long_program &

Запустим программу в фоновом режиме

$ kill $!

По умолчанию команда kill(1) посылает сигнал SIGTERM; переменная $! содержит PID последнего процесса, запущенного в фоновом режиме

При получении сигнала процесс имеет три варианта действий для выбора:

1. Он может игнорировать сигнал. Не следует игнорировать сигналы, вызванные аппаратной частью, например, при делении на 0 или ссылке на недопустимые области памяти, так как дальнейшие результаты в отношении данного процесса непредсказуемы.

2. Процесс может потребовать действия по умолчанию. Как ни печально, обычно это сводится к завершению выполнения процесса.

3. Наконец, процесс может перехватить сигнал и самостоятельно обработать его. Например, перехват сигнала SIGINT позволит процессу удалить созданные им временные файлы, короче, достойно подготовиться к «смерти». Следует иметь в виду, что сигналы SIGKILL и SIGSTOP нельзя ни перехватить, ни игнорировать.

По умолчанию команда kill(1) посылает сигнал с номером 15 – SIGTERM[11]11
  Соответствие между символьными именами и номерами сигналов может отличаться различных версиях UNIX. Команда kill -l выводит номера сигналов и их имена.


[Закрыть]
, действие по умолчанию для которого – завершение выполнения процесса, получившего сигнал.

Иногда процесс продолжает существовать и после отправления сигнала SIGTERM. В этом случае можно применить более жесткое средство – послать процессу сигнал SIGKILL с номером (9), – поскольку этот сигнал нельзя ни перехватить, ни игнорировать:

$ kill -9 pid

Однако возможны ситуации, когда процесс не исчезает и в этом случае. Это может произойти для следующих процессов:

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

□ Процессы, ожидающие недоступные ресурсы NFS (Network File System), например, записывающие данные в файл файловой системы удаленного компьютера, отключившегося от сети. Эту ситуацию можно преодолеть, послав процессу сигнал SIGINT или SIGQUIT.

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

Сигналы могут не только использоваться для завершения выполнения но и иметь специфическое для приложения (обычно для системных демонов) значение (естественно, это не относится к сигналам SIGKILL и SIGSTOP). Например, отправление сигнала SIGHUP серверу имен DNS named(1M) вызовет считывание базы данных с диска. Для других приложений могут быть определены другие сигналы и соответствующие им значения.

Более подробно сигналы мы рассмотрим в главах 2 и 3.


    Ваша оценка произведения:

Популярные книги за неделю