Linux программирование в примерах
if ((niceval = nice(inc)) < 0 && errno != 0) {fprintf(stderr, "nice(%d) failed: %s\n", inc, strerror(errno));/* другое восстановление */}Этот пример может завершиться неудачей, если в
отрицательное значение, а процесс не запущен какinc.root9.1.3.1. POSIX против действительности
Диапазон значений относительного приоритета от -20 до 19, которые использует Linux, имеет исторические корни; он ведет начало по крайней мерее V7. POSIX выражает состояние менее прямым языком, что дает возможность большей гибкости, сохраняя в то же время историческую совместимость. Это также затрудняет чтение и понимание стандарта, вот почему вы и читаете эту книгу. Итак, вот как описывает это POSIX
Во-первых, значение относительного приоритета процесса, поддерживаемое системой, колеблется от 0 до '
'. Константа(2 * NZERO) - 1определена вNZEROи должна равняться по крайней мере 20. Это дает диапазон 0–39.<limits.h>Во-вторых, как мы описывали, сумма текущего значения относительного приоритета и приращение
загоняются в этот диапазон.incrВ заключение, возвращаемое
значение является значением относительного приоритета процесса минусnice(). При значенииNZERO20 это дает первоначальный диапазон от -20 до 19, который мы описали вначале.NZEROРезультатом является то, что возвращаемое nice() значение в действительности изменяется от '
' до '-NZERO', и лучше всего писать свой код в терминах этой именованной константы. Однако, на практике трудно найти систему, в которойNZERO-1не было бы равно 20.NZERO9.1.4. Запуск новой программы: семейство
exec()После запуска нового процесса (посредством
) следующим шагом является запуск в процессе другой программы. Имеется несколько функций, которые служат различным целям:fork()#include <unistd.h> /* POSIX */int execve(const char *filename, /* Системный вызов */char *const argv[], char *const envp[]);int execl(const char *path, const char *arg, ...); /* Оболочки */int execlp(const char *file, const char *arg, ...);int execle(const char *path, const char *arg, ..., char *const envp[]);int execv(const char *path, char *const argv[]);int execvp(const char *file, char *const argv[]);Мы ссылаемся на эти функции как на «семейство
». Функции с именемexec()нет; вместо этого мы используем это имя для обозначения любой из перечисленных выше функций. Как и в случае сexec(), «fork()» используется на языке Unix и в качестве глагола, означающего исполнение (запуск) программы, и в качестве существительного.exec9.1.4.1. Системный вызов
execve()Простейшей для объяснения функцией является
. Она является также лежащим в основе системным вызовом. Другие являются функциями-оболочками, как вскоре будет объяснено.execve()int execve(const char *filename, char *const argv[],char* const envp[]);является именем программы для исполнения. Это может быть именем полного или относительного пути. Файл должен иметь формат исполняемого файла, который понимает ядро. Современные системы используют формат исполняемого файла ELF (Extensible Linking Format — открытый формат компоновки). GNU/Linux распознает ELF и несколько других форматов. С помощьюfilenameможно исполнять интерпретируемые сценарии, если они используют особую первую строку с именем интерпретатора, начинающуюся с 'execve()'. (Сценарии, которые не начинаются с '#!', потерпят неудачу.) В разделе 1.1.3 «Исполняемые файлы» представлен пример использования '#!'. argv является стандартным списком аргументов С — массив символьных указателей на строки аргументов, включая значение для использования с#![90], завершающийся указателемargv[0].NULLявляется окружением для использования новым процессом, с таким же форматом, как глобальная переменнаяenvp(см. раздел 2.4 «Переменные окружения»). В новой программе это окружение становится начальным значениемenviron.environПрограмма не должна возвращаться из вызова
. Если она возвращается, возникла проблема. Чаще всего либо не существует затребованная программа, либо она существует, но не является исполняемой (значения дляexec()иerrno ENOENTсоответственно). Может быть множество других ошибок; см. справочную страницу execve(2).EACCESSВ предположении, что вызов был успешным, текущее содержимое адресного пространства процесса сбрасывается. (Ядро сначала сохраняет в безопасном месте данные
иargv.) Ядро загружает для новой программы исполняемый код вместе со всеми глобальными и статическими переменными. Затем ядро инициализирует переменные окружения переданнымиenvpданными, а далее вызывает процедуруexecve()новой программы с переданным функцииmain()массивомexecve(). Подсчитывается число аргументов и это значение передаетсяargvвmain().argcК этому моменту новая программа запущена. Она не знает (и не может определить), какая программа была в процессе до нее. Обратите внимание, что ID процесса не меняется. Многие другие атрибуты при вызове
сохраняются; вскоре мы рассмотрим это более подробно.execдля процесса можно сравнить с ролями, которые играют в жизни люди. В различное время в течение дня один человек может быть родителем, супругом, другом, студентом или рабочим, покупателем в магазине и т.д. Это одна и та же личность, исполняющая различные роли. Также и процесс — его PID, открытые файлы, текущий каталог и т.п. — не изменяются, тогда как выполняемая работа - запущенная с помощьюexec()программа — может измениться.exec()