Операционная система UNIX
Операционная система UNIX читать книгу онлайн
Книга посвящена семейству операционных систем UNIX и содержит информацию о принципах организации, идеологии и архитектуре, объединяющих различные версии этой операционной системы.
В книге рассматриваются: архитектура ядра UNIX (подсистемы ввода/вывода, управления памятью и процессами, а также файловая подсистема), программный интерфейс UNIX (системные вызовы и основные библиотечные функции), пользовательская среда (командный интерпретатор shell, основные команды и утилиты) и сетевая поддержка в UNIX (протоколов семейства TCP/IP, архитектура сетевой подсистемы, программные интерфейсы сокетов и TLI).
Для широкого круга пользователей.
Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних чтение данного контента СТРОГО ЗАПРЕЩЕНО! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту [email protected] для удаления материала
Для получения и установки значений конкретных переменных окружения используются две функции: getenv(3C) и putenv(3C):
#include <stdlib.h>
char *getenv(const char *name);
возвращает значение переменной окружения
name
int 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 %sn", var);
}
}
}
Сначала программа проверяет, определена ли переменная
TERM
TERM
TERM
Запуск этой программы приведет к следующим результатам:
$ <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