Для получения и установки значений конкретных переменных окружения используются две функции: getenv(3C) и putenv(3C):
#include <stdlib.h>char *getenv(const char *name);возвращает значение переменной окружения
nameint putenv(const char *string);помещает переменную и ее значение (
var_name=var_valueВ качестве примера приведем программу, похожую по своей функциональности на предыдущую, которая выборочно выводит значения переменных и устанавливает новые значения по желанию пользователя.
#include <stddef.h>#include <stdlib.h>#include <stdio.h>main(int argc, char *argv[]) { char *term; char buf[200], var[200]; /* Проверим, определена ли переменная TERM */ if ((term = getenv("TERM")) == NULL) /* Если переменная не определена, получим от пользователя ее значение и поместим переменную в окружение программы */ { printf("переменная TERM не определена, введите значение: "); putenv(var); } else /* Если переменная TERM определена, предоставим пользователю возможность изменить ее значение, после чего поместим ее в окружение процесса */ { printf("TERM=%s. Change? [N]", getenv("TERM")); gets(buf); if (buf[0] == 'Y' || buf[0] == 'y') { printf("TERM="); gets{buf); sprintf(var, "TERM=%s", buf); putenv(var); printf("new %s\n", var); } }}Сначала программа проверяет, определена ли переменная
TERMTERMTERMЗапуск этой программы приведет к следующим результатам:
$ <b>а.out</b>TERM=ansi. Change? [N]<b>y</b>TERM=<b>vt100</b>new TERM=vt100$К сожалению, введенное значение переменной будет действительно только для данного процесса и порожденных им процессов: если после завершения программы a.out вывести значение
TERM$ <b>echo $TERM</b>ansi$Наследование окружения программы мы обсудим в разделе "Создание и управление процессами" далее в этой главе.
Переменные окружения, как и параметры, позволяют передавать программе некоторую информацию. Однако если программа является интерактивной, основную информацию она, скорее всего, будет получать непосредственно от пользователя. В связи с этим встает вопрос: каким образом программа узнает, где находится пользователь, чтобы правильно считывать и выводить информацию? Другими словами, программе необходимо знать, с каким терминальным устройством работает пользователь, запустивший ее.
Обычно при запуске программы на выполнение из командной строки shell автоматически устанавливает для нее три стандартных потока ввода/вывода: для ввода данных, для вывода информации и для вывода сообщений об ошибках. Начальную ассоциацию этих потоков (их файловых дескрипторов) с конкретными устройствами производит терминальный сервер (в большинстве систем это процесс getty(1M)), который открывает специальный файл устройства, связанный с терминалом пользователя, и получает соответствующие дескрипторы. Эти потоки наследует командный интерпретатор shell и передает их запускаемой программе. При этом shell может изменить стандартные направления (по умолчанию все три потока связаны с терминалом пользователя), если пользователь указал на это с помощью специальных директив перенаправления потока (>, <, >>, <<) см. главу 1, раздел "Пользовательская среда UNIX"). Раздел "Группы и сеансы" внесет окончательную ясность в этот вопрос при описании управляющего терминала.
Такой механизм позволяет программисту не задумываться о местонахождении пользователя, и в то же время обеспечить получение и передачу данных именно запустившему данную программу пользователю.
Завершая разговор о запуске программ, заметим, что при компиляции программы редактор связей устанавливает точку входа в программу, указывающую на библиотечную функцию _start(). Эта функция инициализирует процесс, создавая кадр стека, устанавливая значения переменных и, в конечном итоге, вызывая функцию main().
Завершение C-программы
Существует несколько способов завершения программы. Основными являются возврат из функции main() [17] и вызов функций exit(2), оба приводят к завершению выполнения задачи. Заметим, что процесс может завершиться по не зависящим от него обстоятельствам, например, при получении сигнала, действие по умолчанию для большинства из которых приводит к завершению выполнения процесса [18] (см. раздел "Сигналы" далее в этой главе). В этом случае функция exit(2) будет вызвана ядром от имени процесса.
Системный вызов exit(2) выглядит следующим образом:
#include <unistd.h>void exit(int status);Аргумент
status