
- •Системное программирование в unix средствами Free Pascal
- •Глава 1. Основные понятия и терминология 13
- •Глава 2. Файл 17
- •Глава 3. Работа с файлами 43
- •Глава 4. Каталоги, файловые системы и специальные файлы 62
- •Глава 5. Процесс 86
- •Глава 6. Сигналы и их обработка 117
- •Глава 7. Межпроцессное взаимодействие при помощи программных каналов 139
- •Глава 8. Дополнительные методы межпроцессного взаимодействия 163
- •Глава 9. Терминал 196
- •Глава 10.Сокеты 224
- •Глава 11. Стандартная библиотека ввода/вывода 239
- •Глава 12. Разные дополнительные системные вызовы и библиотечные процедуры 267
- •Глава 13. Задачи с решениями 287
- •Предисловие о книге
- •Назначение этой книги
- •Спецификация х/Open
- •Структура книги
- •Что вы должны знать
- •Соглашения
- •Глава 1. Основные понятия и терминология
- •1.1. Файл
- •1.1.1. Каталоги и пути
- •1.1.2. Владелец файла и права доступа
- •1.1.3. Обобщение концепции файла
- •1.2. Процесс
- •1.2.1. Межпроцессное взаимодействие
- •1.3. Системные вызовы и библиотечные подпрограммы
- •Глава 2. Файл
- •2.1. Примитивы доступа к файлам в системе unix
- •2.1.1. Введение
- •2.1.2. Системный вызовfdopen
- •Описание
- •Предостережение
- •2.1.3. Создание файла при помощи вызоваfdopen
- •Описание
- •2.1.4. Системный вызов fdcreat
- •Описание
- •2.1.5. Системный вызовfdclose
- •Описание
- •2.1.6. Системный вызовfdread
- •Описание
- •Указатель чтения-записи
- •2.1.7. Системный вызовfdwrite
- •Описание
- •2.1.8. Пример copyfile
- •2.1.9. Эффективность вызововfdread иfdwrite
- •Описание
- •2.1.10. Вызов fdseek и произвольный доступ
- •Описание
- •2.1.11. Пример: гостиница
- •2.1.12. Дописывание данных в конец файла
- •2.1.13. Удаление файла
- •Описание
- •2.1.14. Системный вызов fcntl
- •Описание
- •2.2. Стандартный ввод, стандартный вывод и стандартный вывод диагностики
- •2.2.1. Основные понятия
- •2.2.2. Программа io
- •2.2.3. Использование стандартного вывода диагностики
- •2.3. Стандартная библиотека ввода/вывода: взгляд в будущее
- •Описание
- •Вывод сообщений об ошибках при помощи функции writeln
- •2.4. Системные вызовы и переменнаяlinuxerror
- •2.4.7. Подпрограмма perror
- •Глава 3. Работа с файлами
- •3.1. Файлы в многопользовательской среде
- •3.1.1. Пользователи и права доступа
- •Действующие идентификаторы пользователей и групп
- •3.1.2. Права доступа и режимы файлов
- •Описание
- •3.1.3. Дополнительные права доступа для исполняемых файлов
- •3.1.4. Маска создания файла и системный вызов umask
- •Описание
- •3.1.5. Вызовfdopen и права доступа к файлу
- •3.1.6. Определение доступности файла при помощи вызова access
- •Описание
- •3.1.7. Изменение прав доступа при помощи вызова chmod Описание
- •3.1.8. Изменение владельца при помощи вызова chown
- •Описание
- •3.2. Файлы с несколькими именами
- •3.2.1. Системный вызов link Описание
- •3.2.2. Системный вызов unlink
- •3.2.3. Системный вызов frename
- •Описание
- •3.2.4. Символьные ссылки
- •Описание
- •Описание
- •3.2.5. Еще об именах файлов
- •Описание
- •3.3. Получение информации о файле: вызов fstat
- •Описание
- •Описание
- •3.3.1. Подробнее о вызове chmod
- •Глава 4. Каталоги, файловые системы и специальные файлы
- •4.1. Введение
- •4.2. Каталоги с точки зрения пользователя
- •Текущий рабочий каталог
- •4.3. Реализация каталогов
- •4.3.1. Снова о системных вызовах link и unlink
- •4.3.2. Точка и двойная точка
- •4.3.3. Права доступа к каталогам
- •4.4. Использование каталогов при программировании
- •4.4.1. Создание и удаление каталогов
- •Описание
- •Описание
- •4.4.2. Открытие и закрытие каталогов
- •Описание
- •Описание
- •4.4.3. Чтение каталогов: вызовы readdir и rewinddir
- •Описание
- •Описание
- •Второй пример: процедура find_entry
- •4.4.4. Текущий рабочий каталог
- •4.4.5. Смена рабочего каталога при помощи вызова chdir Описание
- •4.4.6. Определение имени текущего рабочего каталога
- •Описание
- •Описание
- •4.4.7. Обход дерева каталогов
- •Описание
- •Описание
- •4.5. Файловые системы unix
- •4.5.1. Кэширование: вызовы sync и fsync
- •Описание
- •4.6. Имена устройств unix
- •4.6.1. Файлы блочных и символьных устройств
- •4.6.2. Структураtstat
- •4.6.3. Информация о файловой системе
- •Описание
- •4.6.4. Ограничения файловой системы: процедуры pathconf и fpathconf
- •Описание
- •Глава 5. Процесс
- •5.1. Понятие процесса
- •5.2. Создание процессов
- •5.2.1. Системный вызов fork
- •Описание
- •Идентификатор процесса
- •5.3. Запуск новых программ при помощи вызова ехес
- •5.3.1. Семейство вызовов ехес
- •Описание
- •Вызовы execv, execlpи execvp
- •5.3.2. Доступ к аргументам, передаваемым при вызове exec
- •5.4. Совместное использование вызовов ехес и fork
- •Пример docommand
- •5.5. Наследование данных и дескрипторы файлов
- •5.5.1. Вызов fork,файлы и данные
- •5.5.2. Вызов ехес и открытые файлы
- •5.6. Завершение процессов при помощи системного вызова halt Описание
- •5.7. Синхронизация процессов
- •5.7.1. Системный вызов wait Описание
- •5.7.2. Ожидание завершения определенного потомка: вызов waitpid
- •Описание
- •5.8. Зомби-процессы и преждевременное завершение программы
- •5.9. Командный интерпретатор smallsh
- •5.10. Атрибуты процесса
- •5.10.1. Идентификатор процесса
- •5.10.2. Группы процессов и идентификаторы группы процессов
- •Описание
- •5.10.3. Изменение группы процесса
- •Описание
- •5.10.4. Сеансы и идентификатор сеанса
- •Описание
- •Описание
- •5.10.5. Переменные программного окружения
- •Описание
- •5.10.6. Текущий рабочий каталог
- •5.10.7. Текущий корневой каталог
- •Описание
- •5.10.8. Идентификаторы пользователя и группы
- •5.10.9. Ограничения на размер файла: вызов ulimit
- •Описание
- •5.10.10. Приоритеты процессов
- •Описание
- •Глава 6. Сигналы и их обработка
- •6.1. Введение
- •6.1.1. Имена сигналов
- •6.1.2. Нормальное и аварийное завершение
- •6.2. Обработка сигналов
- •6.2.1. Наборы сигналов
- •Описание
- •6.2.2. Задание обработчика сигналов: вызов sigaction
- •Описание
- •Пример 1: перехват сигнала sigint
- •Пример 2: игнорирование сигнала sigint
- •Пример 3: восстановление прежнего действия
- •Пример 4: аккуратный выход
- •6.2.3. Сигналы и системные вызовы
- •6.2.4. Процедуры sigsetjmpи siglongjmp
- •Описание
- •6.3. Блокирование сигналов
- •Описание
- •6.4. Посылка сигналов
- •6.4.1. Посылка сигналов другим процессам: вызов kill
- •Описание
- •6.4.2. Посылка сигналов самому процессу: вызовы sigraiseи alarm
- •Описание
- •Описание
- •6.4.3. Системный вызов pause
- •Описание
- •6.4.4. Системные вызовы sigpending и sigsuspend
- •Описание
- •Глава 7. Межпроцессное взаимодействие при помощи программных каналов
- •7.1. Каналы
- •7.1.1. Каналы на уровне команд
- •7.1.2. Использование каналов в программе
- •Описание
- •7.1.3. Размер канала
- •7.1.4. Закрытие каналов
- •7.1.5. Запись и чтение без блокирования
- •7.1.6. Использование системного вызова select для работы с несколькими каналами
- •Описание
- •Описание
- •Описание
- •7.1.7. Каналы и системный вызов ехес
- •7.2. Именованные каналы, или fifo
- •7.2.1. Программирование при помощи каналов fifo
- •Описание
- •Глава 8. Дополнительные методы межпроцессного взаимодействия
- •8.1. Введение
- •8.2. Блокировка записей
- •8.2.1. Мотивация
- •8.2.2. Блокировка записей при помощи вызова fcntl
- •Описание
- •Установка блокировки при помощи вызова fcntl
- •Снятие блокировки при помощи вызова fcntl
- •Задача об авиакомпании acme Airlines
- •Проверка блокировки
- •8.3. Дополнительные средства межпроцессного взаимодействия
- •8.3.1. Введение и основные понятия
- •Ключи средств межпроцессного взаимодействия
- •Описание
- •Операция get
- •Другие операции
- •Структуры данных статуса
- •8.3.2. Очереди сообщений
- •Описание
- •Работа с очередью сообщений: примитивы msgsndи msgrcv
- •Описание
- •Пример передачи сообщений: очередь с приоритетами
- •Программа etest
- •Программа stest
- •Системный вызов msgctl
- •Описание
- •8.3.3. Семафоры Семафор как теоретическая конструкция
- •Системный вызов semget Описание
- •Системный вызов semctl Описание
- •Операции над семафорами: вызов semop
- •Описание
- •Флаг sem_undo
- •Пример работы с семафорами
- •8.3.4. Разделяемая память
- •Системный вызов shmget
- •Описание
- •Операции с разделяемой памятью: вызовы shmat и shmdt
- •Описание
- •Системный вызов shmctl Описание
- •Пример работы с разделяемой памятью: программа shmcopy
- •8.3.5. Команды ipcsи ipcrm
- •Глава 9. Терминал
- •9.1. Введение
- •9.2. Терминал unix
- •9.2.1. Управляющий терминал
- •9.2.2. Передача данных
- •9.2.3. Эхо-отображение вводимых символов и опережающий ввод с клавиатуры
- •9.2.4. Канонический режим, редактирование строки и специальные символы
- •9.3. Взгляд с точки зрения программы
- •9.3.1. Системный вызовfdopen
- •9.3.2. Системный вызов fdread
- •9.3.3. Системный вызов fdwrite
- •9.3.4. Функции ttynameи isatty
- •Описание
- •9.3.5. Изменение свойств терминала: структура termios
- •Описание
- •Описание
- •Определение структуры termios
- •Массив с_сс
- •Поле c_cflag
- •Описание
- •Поле c_iflag
- •Поле c_oflag
- •Поле с_lflag
- •Описание
- •9.3.6. Параметры min и time
- •9.3.7. Другие системные вызовы для работы с терминалом
- •Описание
- •9.3.8. Сигнал разрыва соединения
- •9.4. Псевдотерминалы
- •9.5. Пример управления терминалом: программа tscript
- •Глава 10.Сокеты
- •10.1. Введение
- •10.2. Типы соединения
- •10.3. Адресация
- •10.3.1. Адресация Internet
- •Описание
- •10.3.2. Порты
- •10.4. Интерфейс сокетов
- •10.4.1. Создание сокета
- •Описание
- •10.5. Программирование в режиме tcp-соединения
- •10.5.1. Связывание
- •Описание
- •10.5.2. Включение приема tcp-соединений
- •Описание
- •10.5.3. Прием запроса на установку tcp-соединения
- •Описание
- •10.5.4. Подключение клиента
- •Описание
- •10.5.5. Пересылка данных
- •Описание
- •10.5.6. Закрытие tcp-соединения
- •10.6. Программирование в режиме пересылок udp-дейтаграмм
- •10.6.1. Прием и передача udp-сообщений
- •Описание
- •10.7. Различия между двумя моделями
- •Глава 11. Стандартная библиотека ввода/вывода
- •11.1. Введение
- •11.2. Структура tfile
- •11.3. Открытие и закрытие потоков: процедуры fopenи fclose Описание
- •Описание
- •11.4. Посимвольный ввод/вывод: процедуры getc и putc Описание
- •11.5. Возврат символов в поток: процедура ungetc Описание
- •11.6. Стандартный ввод, стандартный вывод и стандартный вывод диагностики
- •11.7. Стандартные процедуры опроса состояния
- •Описание
- •11.8. Построчный ввод и вывод
- •Описание
- •Описание
- •11.9. Ввод и вывод бинарных данных: процедуры freadи fwrite Описание
- •11.10. Произвольный доступ к файлу: процедуры fseek, rewindи ftell
- •Описание
- •11.11. Форматированный вывод: семейство процедур printf Описание
- •Задание ширины поля и точности
- •Комплексный пример
- •Специальные символы
- •Процедура sprintf
- •11.12. Форматированный ввод: семейство процедур scanf Описание
- •11.13. Запуск программ при помощи библиотек стандартного ввода/вывода
- •Описание
- •Описание
- •11.14. Вспомогательные процедуры
- •11.14.1. Процедуры freopen и fdopen Описание
- •11.14.2. Управление буфером: процедуры setbufи setvbuf Описание
- •Глава 12. Разные дополнительные системные вызовы и библиотечные процедуры
- •12.1. Введение
- •12.2. Управление динамическим распределением памяти
- •Описание
- •Описание
- •Описание
- •Пример использования функции malloc:связные списки
- •Вызовы brk и sbrk
- •12.3. Ввод/вывод с отображением в память и работа с памятью
- •Описание
- •Системные вызовы ттар и munmap
- •Описание
- •Описание
- •12.4. Время
- •Описание
- •Описание
- •12.5. Работа со строками и символами
- •12.5.1. Семейство процедур strings
- •Описание
- •12.5.2. Преобразование строк в числовые значения
- •Описание
- •12.5.3. Проверка и преобразование символов
- •12.6. Дополнительные средства
- •12.6.1. Дополнение о сокетах
- •12.6.2. Потоки управления
- •Описание
- •12.6.3. Расширения режима реального времени
- •12.6.4. Получение параметров локальной системы
- •12.6.5. Интернационализация
- •12.6.6. Математические функции
- •12.6.7. Работа с портами ввода вывода
- •Глава 13. Задачи с решениями
- •13.1. Введение
- •13.2. Обработка текста
- •13.3. Бинарные файлы
- •13.4. Каталоги
- •13.5. Файловые системы
- •13.6. Файловая системаproc
- •13.7. Управление файлами
- •13.8. Управление процессами
- •13.9. Программные каналы
- •13.10. Управление терминалом
- •13.11. Дата и время
- •13.12. Генератор лексических анализаторовlex
- •Приложение 1. Коды ошибок переменной linuxerror и связанные с ними сообщения Введение
- •Список кодов и сообщений об ошибках
- •Приложение 2. История unix
- •Основные стандарты
- •Ieee/posix
- •Приложение 3. Модульstdio
- •Приложение4. Замечания о компиляции воFree Pascal 2.0
- •Литература
9.4. Псевдотерминалы
Еще одно применение модуля дисциплины линии связи заключается в поддержке работы так называемого псевдотерминала (pseudo terminal), применяемого для организации дистанционного доступа через сеть. Псевдотерминал предназначен для обеспечения соединения терминала одного компьютера с командным интерпретатором другого компьютера. Пример такого соединения приведен на рис. 9.4. На нем пользователь подключен к компьютеру А (клиенту), но использует командный интерпретатор на компьютере В (сервере). Стрелки на схеме показывают направление пересылки вводимых с клавиатуры символов. Здесь схема несколько упрощена за счет исключения стеков сетевых протоколов на клиентской и серверной системе.
Когда пользователь подключается к командному интерпретатору на другом компьютере (обычно при помощи команды rlogin), то локальное терминальное соединение должно быть изменено. По мере того как данные считываются с локального терминала, они должны без изменений передаваться через модуль дисциплины линии связи клиентскому процессу rlogin, выполняющемуся на локальном компьютере. Поэтому дисциплина линии связи на локальном компьютере должна работать в режиме прямого доступа. Следующий фрагмент должен напомнить, как включается такой режим:
uses stdio, linux;
var
attr:termios;
.
.
.
(* Получает текущую дисциплину линии связи *)
tcgetattr(0, attr);
(* Возврат из вызова fdread разрешен только после считывания одного символа *)
attr.c_cc[VMIN] := 1;
attr.c_cc(VTIME] := 0;
attr.c_lflag := attr.c_lflag and not (ISIG or ECHO or ICANON);
(* Устанавливает новую дисциплину линии связи *)
tcsetattr(0, TCSAFLUSH, attr);
Рис. 9.4. Удаленный вход в систему в ОС UNIX
Теперь процесс клиента rlogin может передавать данные по сети в исходном виде.
Когда сервер (В) получает первоначальный запрос на вход в систему, он выполняет вызовы fork и ехес, порождая новый командный интерпретатор. С новым командным интерпретатором не связан управляющий терминал, поэтому создается псевдотерминал, который имитирует обычный драйвер устройства терминала. Псевдотерминал (pseudo tty) действует подобно двунаправленному каналу и просто позволяет двум процессам передавать данные. В рассматриваемом примере псевдотерминал связывает командный интерпретатор с соответствующим сетевым процессом. Псевдотерминал является парой устройств, которые называются ведущим и ведомым устройствами, или портами псевдотерминала. Сетевой процесс открывает ведущий порт устройства псевдотерминала, а затем выполняет запись и чтение. Процесс командного интерпретатора открывает ведомый порт, а затем работает с ним (через дисциплину линии связи). Данные, записываемые в ведущий порт, попадают на вход ведомого порта, и наоборот. В результате пользователь на клиентском компьютере (А) как бы напрямую обращается к командному интерпретатору, который на самом деле выполняется на сервере (В). Аналогично, по мере того как данные выводятся командным интерпретатором на сервере, они обрабатываются дисциплиной линии связи на сервере (которая работает в каноническом режиме) и затем передаются без изменений клиентскому терминалу, не подвергаясь модификации со стороны дисциплины линии связи клиента.
Хотя средства, при помощи которых выполняется инициализация псевдотерминалов, были улучшены в новых версиях ОС UNIX и спецификации XSI, они все еще остаются довольно громоздкими. Система UNIX обеспечивает конечное число псевдотерминалов, и процесс командного интерпретатора должен открыть следующий доступный псевдотерминал. В системе SVR4 это выполняется и помощи открытия устройства /dev/ptmx, которое определяет и открывает первое неиспользуемое ведущее устройство псевдотерминала. С каждым ведущим устройством связано ведомое устройство. Для того, чтобы предотвратить открытие ведомого устройства другим процессом, открытие устройства /dev/ptmx также блокирует соответствующее ведомое устройство.
uses linux;
var
mfd:longint;
.
.
.
(* Открыть псевдотерминал -
* получить дескриптор файла главного устройства *)
masterfd := fdopen ('/dev/ptmx', Open_RDWR);
if masterfd = -1 then
begin
perror('Ошибка при открытии главного устройства');
halt(1);
end;
Перед тем как открыть и «разблокировать» ведомое устройство, необходимо убедиться, что только один процесс с соответствующими правами доступа сможет выполнять чтение из устройства и запись в него. Функция grantpt изменяет режим доступа и идентификатор владельца ведомого устройства в соответствии с параметрами связанного с ним главного устройства. Функция unlockpt снимает флаг, блокирующий ведомое устройство (то есть делает его доступным). Далее нужно открыть ведомое устройство. Но его имя пока еще не известно. Функция ptsname возвращает имя ведомого устройства, связанного с заданным ведущим устройством, которое обычно имеет вид /dev/pts/pttyXX. Следующий фрагмент демонстрирует последовательность необходимых действий:
uses stdio, linux;
var
mfd, sfd:longint;
slavenm:pchar;
.
.
.
(* Открываем ведущее устройство, как и раньше *)
mfd := fdopen ('/dev/ptmx', Open_RDWR);
if mfd = -1 then
begin
perror('Ошибка при открытии ведущего устройства');
halt(1);
end;
(* Изменяем права доступа ведомого устройства *)
if grantpt (mfd) = -1 then
begin
perror('Невозможно разрешить доступ к ведомому устройству');
halt(1);
end;
(* Разблокируем ведомое устройство, связанное с mfd *)
if unlockpt(mfd) = -1 then
begin
perror('Невозможно разблокировать ведомое устройство');
halt(1);
end;
(* Получаем имя ведомого устройства и затем пытаемся открыть его *)
slavenm := ptsname (mfd);
if slavenm = nil then
begin
perror('Невозможно получить имя ведомого устройства');
halt(1);
end;
sfd := fdopen (slavenm, Open_RDWR);
if slavefd = -1 then
begin
perror('Ошибка при открытии ведомого устройства');
halt(1);
end;
Теперь, когда получен доступ к драйверу устройства псевдотерминала, нужно установить для него дисциплину линии связи. До сих пор дисциплина линии связи рассматривалась как единое целое, тогда как в действительности она состоит из набора внутренних модулей ядра, известных как модули STREAM. Стандартная дисциплина линии связи псевдотерминала состоит из трех модулей: ldterm (модуль дисциплины линии связи терминала), ptem (модуль эмуляции псевдотерминала) и ведомой части псевдотерминала. Вместе они работают как настоящий терминал. Эта конфигурация показана на рис. 9.5.
|
|
|
|
|
ldterm |
|
|
|
|
|
|
|
pterm |
|
Дисциплина линии связи |
|
|
|
|
|
Ведомый порт псевдотерминала |
|
|
|
|
|
|
Рис. 9.5. Дисциплина линии связи в виде модулей STREAM для устройства псевдотерминала
Для создания дисциплины линии связи нужно «вставить» дополнительные модули STREAM в ведомое устройство. Это достигается при помощи многоцелевой функции ioctl, например:
/*
* Заголовочный файл stdio содержит интерфейс STREAMS
* и определяет макрокоманду I_PUSH, используемую в качестве
* второго аргумента функции ioctl().
*/
uses stdio;
.
.
.
(* Открываем ведущее и ведомое устройства, как и раньше *)
(* Вставляем два модуля в ведомое устройство *)
ioctl(sfd, I_PUSH, 'ptem');
ioctl(sfd, I_PUSH, 'ldterm');
Обратимся теперь к главному примеру, программе tscript, которая использует псевдотерминал в пределах одного компьютера для перехвата вывода командного интерпретатора в процессе интерактивного сеанса, не влияя на ход этого сеанса. (Эта программа аналогична команде UNIX script.) Данный пример можно расширить и для дистанционного входа через сеть.