Читать книгу 📗 "Операционная система UNIX - Робачевский Андрей Михайлович"
При запуске программы командный интерпретатор порождает процесс, который наследует все четыре идентификатора и, следовательно, имеет те же права, что и shell. Поскольку в конкретном сеансе работы пользователя в системе прародителем всех процессов является login shell, то и их пользовательские идентификаторы будут идентичны.
Казалось бы, эту стройную систему могут "испортить" утилиты с установленными флагами SUID и SGID. Но не стоит волноваться — как правило, такие программы не позволяют порождать другие процессы, в противном случае, эти утилиты необходимо немедленно уничтожить!
На рис. 2.10. показан процесс наследования пользовательских идентификаторов в рамках одного сеанса работы.

Рис. 2.10. Наследование пользовательских идентификаторов
Для получения значений идентификаторов процесса используются следующие системные вызовы:
#include <sys/types.h>#include <unistd.h>uid_t getuid(void);uid_t geteuid(void);gid_t getgid(void);gid_t getegid(void);Эти функции возвращают для сделавшего вызов процесса соответственно реальный и эффективный идентификаторы пользователя и реальный и эффективный идентификаторы группы.
Процесс также может изменить значения этих идентификаторов с помощью системных вызовов:
#include <sys/types.h>#include <unistd.h>int setuid(uid_t uid);int setegid(gid_t egid);int seteuid(uid_t euid);int setgid(gid_t gid);Системные вызовы setuid(2) и setgid(2) устанавливают сразу реальный и эффективный идентификаторы, а системные вызовы seteuid(2) и setegid(2) — только эффективные.
Ниже приведен фрагмент программы login(1), изменяющей идентификаторы процесса на значения, полученные из записи файла паролей. В стандартной библиотеке имеется ряд функций работы с записями файла паролей, каждая из которых описывается структурой
passwdТаблица 2.17. Поля структуры passwd
| Поле | Значение |
|---|---|
char *pw_name | Имя пользователя |
char *pw_passwd | Строка, содержащая пароль в зашифрованном виде; из соображения безопасности в большинстве систем пароль хранится в файле /etc/shadow, а это поле не используется |
uid_t pw_uid | Идентификатор пользователя |
gid_t pw_gid | Идентификатор группы |
char *pw_gecos | Комментарий (поле GECOS), обычно реальное имя пользователя и дополнительная информация |
char *pw_dir | Домашний каталог пользователя |
char *pw_shell | Командный интерпретатор |
Функция, которая потребуется для нашего примера, позволяет получить запись файла паролей по имени пользователя. Она имеет следующий вид:
#include <pwd.h>struct passwd *getpwnam(const char *name);Итак, перейдем к фрагменту программы:
...struct passwd *pw;char logname[MAXNAME];/* Массив аргументов при запуске командного интерпретатора */char *arg[MAXARG];/* Окружение командного интерпретатора */char *envir[MAXENV];.../* Проведем поиск записи пользователя с именем logname, которое было введено на приглашение "login:" */pw = getpwnam(logname);/* Если пользователь с таким именем не найден, повторить приглашение */if (pw == 0) retry(); /* В противном случае установим идентификаторы процесса равными значениям, полученным из файла паролей и запустим командный интерпретатор */else { setuid(pw->pw_uid); setgid(pw->pw_gid); execve(pw->pw_shell, arg, envir);}...Вызов execve(2) запускает на выполнение программу, указанную в первом аргументе. Мы рассмотрим эту функцию в разделе "Создание и управление процессами" далее в этой главе.
Выделение памяти
При обсуждении формата исполняемых файлов и образа программы в памяти мы отметили, что сегменты данных и стека могут изменять свои размеры. Если для стека операцию выделения памяти операционная система производит автоматически, то приложение имеет возможность управлять ростом сегмента данных, выделяя дополнительную память из хипа (heap — куча). Рассмотрим этот программный интерфейс.
Память, которая используется сегментами данных и стека, может быть выделена несколькими различными способами как во время создания процесса, так и динамически во время его выполнения. Существует четыре способа выделения памяти:
1. Переменная объявлена как глобальная, и ей присвоено начальное значение в исходном тексте программы, например:
char ptype = "Unknown file type";Строка
ptype2. Значение глобальной переменной неизвестно на этапе компиляции, например:
char ptype[32];В этом случае место в исполняемом файле для
ptype