Системное программирование в UNIX средствами Free Pascal

         

подобно Fork, создает дочерний процесс,


uses linux;
TCloneFunc=function(args:pointer):longint;cdecl;
Clone(func:TCloneFunc;sp:pointer;flags:longint;args:pointer):longint;
Clone, подобно Fork, создает дочерний процесс, являющийся копией родительского. Однако, в отличие от Fork, процесс-потомок совместно с родителем использует некоторые части контекста, что делает его подходящим для реализации потоков: множества экземпляров программы, разделяющих общую память.
При создании потомка запускается функция Func, которой передаются параметры Args. Возвращаемым значением Func является код завершения потомка.
Указатель sp
хранит адрес памяти, зарезервированной под стек дочернего процесса.
Параметр Flags определяет поведение вызова Clone. Младший байт Flags содержит номер сигнала, который будет послан родителю при завершении потомка. Он может быть объединен с помощью побитового ИЛИ со следующими константами:
CLONE_VM               Родитель и потомок разделяют память, включая отображенную вызовом mmap.
CLONE_FS               Родитель и потомок разделяют параметры файловой системы; вызовы chroot, chdir и umask действуют на оба процесса.
CLONE_FILES         Таблица дескрипторов файлов разделяется родителем и потомком.
CLONE_SIGHAND     Таблица обработчиков сигналов разделяется родителем и потомком, однако маски сигналов различаются.
CLONE_PID             Родитель и потомок имеют одинаковый pid.
Clone возвращает идентификатор потомка или -1 в случае ошибки, устанавливая значение linuxerror в Sys_EAGAIN (слишком много процессов) и Sys_ENOMEM (недостаточно памяти для создания дочернего процесса).
Пример использования Clone:
uses
  Linux, Errors, crt;
const
  Ready : Boolean = false;
  aChar : Char    = 'a';
function CloneProc( Arg: Pointer ): LongInt; Cdecl;


begin
  WriteLn('Hello from the clone ',PChar(Arg));
  repeat
    Write(aChar);
    Select(0,0,0,0,600);
  until Ready;
  WriteLn( 'Clone finished.');
  CloneProc := 1;
end;
var
  PID : LongInt;
procedure MainProc;


begin
а WriteLn(' cloned process PID: ', PID );
а WriteLn('Press <ESC> to kill ... ' );
а repeat
ааа Write('.');
ааа Select(0,0,0,0,300);
ааа if KeyPressed then
ааааа case ReadKey of
ааааааа #27: Ready := true;
ааааааа 'a': aChar := 'A';
ааааааа 'A': aChar := 'a';
ааааааа 'b': aChar := 'b';
ааааааа 'B': aChar := 'B';
ааааа end;
а until Ready;
а WriteLn('Ready.');
end;
const
а StackSze = 16384;
а theFlags = CLONE_VM+CLONE_FS+CLONE_FILES+CLONE_SIGHAND;
а aMsgаааа : PChar = 'Oops !';
var
а theStack : Pointer;
а ExitStat : LongInt;
begin
а GetMem(theStack,StackSze);
а PID := Clone(@CloneProc,
аааааааааааааа Pointer( LongInt(theStack)+StackSze),
ааааааааа аааааtheFlags,
аааааааааааааа aMsg);
а if PID < 0 then
ааа WriteLn('Error : ', LinuxError, ' when cloning.')
а else
ааа begin
ааа MainProc;
ааа case WaitPID(0,@ExitStat,Wait_Untraced or wait_clone) of
ааааа -1: WriteLn('error:',LinuxError,'; ',StrError(LinuxError));
аааааа 0: WriteLn('error:',LinuxError,'; ',StrError(LinuxError));
ааа else
ааааа WriteLn('Clone exited with: ',ExitStat shr 8);
ааа end;
ааа end;
а FreeMem( theStack, StackSze );
end.

Определение доступности файла при помощи вызова access


Системный вызов access определяет, может ли процесс получить доступ к файлу в соответствии с истинным (real), а не действующим (effective) идентификатором пользователя (и группы) процесса. Такой вызов позволяет процессу, получившему права при помощи бита STAT_ISUID, определить настоящие права пользователя, запустившего это процесс, что облегчает написание безопасных программ.



Определение имени текущего рабочего каталога


Спецификация XSI определяет функцию (а не системный вызов) getcwd, которая возвращает имя текущего рабочего каталога.



Определение структуры termios


Теперь изучим состав структуры termios. Определение структуры termios находится в файле linux и содержит следующие элементы:

termios = record

   c_iflag,               (* Режимы ввода *)

   c_oflag,               (* Режимы вывода *)

   c_cflag,               (* Управляющие режимы *)

   c_lflag : Cardinal;    (* Режимы дисциплины линии связи *)

   c_line  : char;        (* Дисциплина линии связи *)

   c_cc    : array [0..NCCS-1] of char; (* Управляющие символы *)

end;

Проще всего рассматривать структуру, начав с ее последнего элемента с_сс.



Основные понятия


Система UNIX автоматически открывает три дескриптора файла для любой выполняющейся программы. Эти дескрипторы называются стандартным вводом (standard input), стандартным выводом (standard output) и стандартным выводом диагностики (standard error). Они всегда имеют значения 0, 1 и 2 соответственно. Недопустимо путать эти дескрипторы с похожими по названию стандартными потоками stdin, stdout и stderr из стандартной библиотеки ввода/ вывода.

По умолчанию вызов fdread для стандартного ввода приведет к чтению данных с клавиатуры. Аналогично запись в стандартный вывод или стандартный вывод диагностики приведет по умолчанию к выводу сообщения на экран терминала. Это первый пример использования примитивов доступа к файлам для ввода/вывода на устройства, отличные от обычных файлов.

Программа, применяющая эти стандартные дескрипторы файлов, тем не менее, не ограничена использованием терминала. Каждый из этих дескрипторов может быть независимо переназначен, если программа вызывается с использованием средств перенаправления, обеспечиваемых стандартным командным интерпретатором UNIX. Например, команда

$ prog_name < infile

приведет к тому, что при чтении из дескриптора со значением 0 программа будет получать данные из файла infile, а не с терминала, обычного источника для стандартного ввода.

Аналогично все данные, записываемые в стандартный вывод, могут быть перенаправлены в выходной файл, например:

$ prog_name > outfile

Возможно, наиболее полезно то, что можно связать стандартный вывод одной программы со стандартным вводом другой при помощи каналов UNIX. Следующая команда оболочки означает, что все данные, записываемые программой prog_1 в ее стандартный вывод, попадут на стандартный ввод программы prog_2 (такие команды называются конвейерами):

$ prog_1 | prog_2

Дескрипторы файлов стандартного ввода и вывода позволяют писать гибкие и совместимые программы. Программа может представлять собой инструмент, способный при необходимости принимать ввод от пользователя, из файла, или даже с выхода другой программы. Программа настроена на чтение из стандартного ввода, использует файловый дескриптор 0, а выбор входного источника данных откладывается до момента запуска программы.



Основные стандарты


В книге упомянуты следующие стандарты:

SVID

Название стандарта SVID является сокращением от AT&T System V Interface Definition (определение интерфейса ОС System V). Первоначально был разработан весной 1985 г. для описания интерфейса версии System V операционной системы UNIX. Стандарт SVID имеет ряд версий, последней из которых является третья редакция, вышедшая в 1989 г. Первое издание этой книги основывалось на стандарте SVID.

ANSI С

Комитет ANSI определяет стандарты различных информационных технологий. Наиболее интересным для системных программистов UNIX является стандарт языка ANSI С.

IEEE/POSIX

Институт электротехники и радиоэлектроники (Institute of Electrical and Electronics Engineers, сокращенно IEEE) разрабатывает в числе прочих стандарт интерфейса переносимой операционной системы (Portable Operating Systems Interface, сокращенно POSIX), который непосредственно базируется на ОС UNIX. Этот стандарт был впервые опубликован в 1988 г. и с тех пор несколько раз обновлялся. С темой данной книги наиболее тесно связаны следующие стандарты:

–        стандарт IEEE 1003.1-1990, идентичный стандарту ISO POSIX-1 – ISO/IEC 9945-1, 1990, полное название которого: Information Technology – Portable Operating System Interface (POSIX) – Part 1: System Application Program Interface (API) [C language] (Информационные технологии – стандарт интерфейса переносимой операционной системы – часть 1. Системный интерфейс для прикладных программ [язык С]);

–        стандарт IEEE 1003.2-1992, идентичный стандарту ISO POSIX-2 – ISO/IEC 9945-2, 1993, полное название которого: Information Technology – Portable Operating System Interface (POSIX) – Part 2: Shell and Utilities (Информационные технологии – стандарт интерфейса переносимой операционной системы – часть 2. Командный интерпретатор и утилиты).

Позже к стандарту были добавлены расширения и добавления, включая стандарты 1003.1b-1993, 1003.1с-1995, 1003.li-1995, охватывающие такие темы, как потоки управления и расширения реального времени.




Х/Open (в настоящее время Open Group)

Группа X/ Open Group объединила вышеупомянутые стандарты с другими; эти стандарты, вместе взятые, получили название спецификации Common Application Environment (общей среды приложений, сокращенно САЕ). Спецификация САЕ охватывает как системные, так и сетевые интерфейсы.

Текст книги лучше всего соответствует Issue 4 Version 2 X/Open САЕ Specification, System Interface and Headers (четвертому выпуску второй версии спецификации САЕ консорциума X/Open, системные интерфейсы и заголовки), вышедшей в августе 1994 г. Эта спецификация представляет собой базовый документ X/Open. Пятое издание спецификации САЕ, вышедшее в 1997 г., включает некоторые из недавно введенных в стандарт POSIX средств работы с потоками управления в режиме реального времени, а также других наработок из практики промышленного использования. Эти обновления также вошли в Version 2 of the Single UNIX Specification (вторую версию единой спецификации ОС UNIX) консорциума Open Group и в описание продукта UNIX98.


Открытие и закрытие каталогов


Для открытия каталога UNIX спецификация XSI определяет особую функцию opendir.



Ожидание завершения определенного потомка: вызов waitpid


Системный вызов wait позволяет родительскому процессу ожидать завершения любого дочернего процесса. Тем не менее, если нужна большая определенность, то можно использовать системный вызов waitpid для ожидания завершения определенного дочернего процесса.



Параметры MIN и TIME


Параметры MIN и TIME имеют значение только при выключенном флаге ICANON. Они предназначены для тонкой настройки управления вводом данных. Параметр MIN задает минимальное число символов, которое должен получить драйвер терминала для возврата из вызова fdread. Параметр TIME задает значение максимального интервала ожидания; этот параметр обеспечивает еще один уровень управления вводом с терминала. Время ожидания измеряется десятыми долями секунды.

Значения параметров MIN и МАХ находятся в массиве с_сс структуры termios, описывающей состояние терминала. Их индексы в массиве определяются постоянными VMIN и VTIME из файла stdio. Следующий фрагмент программы показывает, как можно задать их значения:

uses stdio, linux;

var

  tdes:termios;

  ttyfd:longint;

(* Получает текущее состояние *)

tcgetattr(ttyfd, tdes);

tdes.c_lflag := tdes.c_lflag and (not ICANON); (* Отключает канонический режим *)

tdes.c_cc[VMIN] := 64;   (* В символах *)

tdes.c_cc[VTIME] := 2;   (* В десятых долях секунды *)

tcsetattr(0, TCSAFLUSH, &tdes);

Константы VMIN и VTIME обычно имеют те же самые значения, что и постоянные VEOF и VEOL. Это означает, что параметры MIN и TIME занимают то же положение, что и символы eof и еоl. Следовательно, при переключении из канонического в неканонический режим нужно обязательно задавать значения параметров MIN и TIME, иначе может наблюдаться странное поведение терминала. (В частности, если символу eof

соответствует комбинация клавиш Ctrl+D,

то программа будет читать ввод блоками по четыре символа.) Аналогичная опасность возникает при возврате в канонический режим.

Существуют четыре возможных комбинации параметров MIN и TIME:

–        оба параметра MIN и TIME равны нулю. При этом возврат из вызова fdread обычно происходит немедленно. Если в очереди ввода терминала присутствуют символы (напомним, что попытка ввода может быть осуществлена в любой момент времени), то они будут помещены в буфер процесса. Поэтому, если программа переводит свой управляющий терминал в режим прямого доступа при помощи сброса флага ICANON и оба параметра MIN и TIME равны нулю, то вызов




nread := fdread(0, buffer, SOMESZ);

вернет произвольное число символов от нуля до SOMESZ в зависимости от того, сколько символов находится в очереди в момент выполнения вызова;

–        параметр MIN больше нуля, а параметр TIME равен нулю. В этом случае таймер не используется. Вызов fdread завершится только после того, как будут считаны MIN символов. Это происходит даже в том случае, если вызов read запрашивал меньше, чем MIN символов.

В самом простом варианте параметр MIN равен единице, а параметр TIME – нулю, что приводит к возврату из вызова fdread после получения каждого символа из линии терминала. Это может быть полезно при чтении с клавиатуры терминала, хотя могут возникнуть проблемы с клавишами, посылающими последовательности из нескольких символов;

–        параметр MIN равен нулю, а параметр TIME больше нуля. В этом случае параметр MIN не используется. Таймер запускается сразу же после выполнения вызова fdread. Возврат из вызова read происходит, как только вводится первый символ. Если заданный интервал времени истекает (то есть проход время, заданное в параметре TIME в десятых долях секунды), то вызов read возвращает нулевые символы;

–        оба параметра MIN и TIME больше нуля. Это, возможно, наиболее полезный и гибкий вариант. Таймер запускается после получения первого символа, а не при входе в вызов fdread. Если MIN символов будут получены до истечения заданного интервала времени, то происходит возврат из вызова fdread. Если таймер срабатывает раньше, то в программу пользователя возвращаются только символы, находящиеся при этом в очереди ввода. Этот режим работы полезен при поступлении ввода пакетами, которые посылаются в течение коротких интервалов времени. При этом упрощается программирование и уменьшается число необходимых системных вызовов. Такой режим также полезен, например, при работе с функциональными клавишами, которые посылают при нажатии на них сразу несколько символов.


Передача данных


Основной задачей драйвера терминала является передача данных между процессом и его терминальным устройством. На самом деле это достаточно сложное требование, так как пользователь может печатать символы в любое время, даже во время вывода. Чтобы лучше понять эту ситуацию, вернемся к рис. 9.1 и представим, что по путям от (С) к (В) и от (В) к (D) одновременно передаются данные. Напомним, что программа, представленная на схеме узлом (А), может выполнять только последовательные вызовы fdread или fdwrite.

Поскольку в каждый момент времени программа может обрабатывать только один поток данных терминала, то для одновременного управления двумя потоками дисциплина линии связи запоминает входные и выходные данные во внутренних буферах. Входные данные передаются программе пользователя, когда он выполняет вызов fdread. Входные символы могут быть потеряны при переполнении поддерживаемых ядром буферов или если число символов, поступивших на терминал, превысит ограничение MAX_INPUT, определенное в файле stdio. Это предельное значение обычно равно 255, что достаточно велико для того, чтобы потери данных при обычном использовании были достаточно редки. Тем не менее не существует способа определить, что данные были потеряны, так как система просто молча отбрасывает лишние символы.

Ситуация с выводом несколько проще. Каждый вызов fdwrite для терминала помещает символы в очередь вывода. Если очередь переполняется, то следующий вызов fdwrite будет «заблокирован» (то есть процесс будет приостановлен) до тех пор, пока очередь вывода не уменьшится до нужного уровня.



Переменные программного окружения


Программное окружение (environment) процесса – это просто набор строк, заканчивающихся нулевым символом, представленных в программе просто в виде массива указателей на строки. Эти строки называются переменными окружения (environment variables). По принятому соглашению, каждая строка окружения имеет следующую форму:

имя переменной = ее содержание

В модуле dos определены функция envcount, возвращающие количество строк окружения, и функция envstr, возвращаю строку окружения с заданным номером. Можно напрямую использовать программное окружение процесса с помощью массива envp из модуля syslinux.

В качестве простого примера следующая программа просто выводит свое окружение и завершает работу.

а) с использованием модуля dos:

(* Программа showmyenv.pas - вывод окружения *)

uses dos;

var

  i:integer;

begin

  for i:=1 to envcount do

    writeln(envstr(i));

end.

б) с использованием модуля syslinux:

(* Программа showmyenv.pas - вывод окружения *)

uses syslinux;

var

  i:integer;

begin

  i:=0;

  while envp[i]<>nil do

  begin

    writeln(envp[i]);

    inc(i);

  end;

end.

При запуске этой программы на одной из машин были получены следующие результаты:

CDPATH=:..:/

HOME=/usr/keith

LOGNAME=keith

MORE=-h -s

PATH=/bin:/etc:/usr/bin:/usr/cbin:/usr/lbin

SHELL=/bin/ksh

TERM=vti00

TZ=GMTOBST

Этот список может быть вам знакомым. Это окружение процесса командного интерпретатора (оболочки), вызвавшего программу showmyenv, и оно включает

такие важные переменные, как HOME и PATH.

Пример показывает, что по умолчанию окружение процесса совпадает с окружением процесса, создавшего его при помощи вызова fork или ехес. Поскольку окружение передается указанным способом, то можно записывать в окружении информацию, которую иначе пришлось бы задавать заново для каждого нового процесса. Переменная окружения TERM, в которой записан тип текущего терминала, является хорошим примером того, насколько полезным может быть использование окружения.

Чтобы задать для процесса новое окружение, необходимо использовать для его запуска один из двух вызовов из семейства exec: execle или execve. Они вызываются следующим образом:

execle(path, envp);

и:

execve(path, argv, envp);

Эти вызовы дублируют соответственно вызовы

execv и execl. Единственное различие заключается в добавлении параметра envp, который является заканчивающимся нулевым символом массивом строк, задающим окружение новой программы. Следующий пример использует вызов

execle для передачи нового программного окружения программе showmyenv:

(* Программа setmyenv.pas - установка окружения программы *)

uses linux,stdio;

const

  argv:array [0..1] of pchar=('showmyenv',nil);

  envp:array [0..2] of pchar=('foo=bar','bar=foo',nil);

begin

  execve ('./showmyen', argv, envp);

  perror ('Ошибка вызова execve');

end.

Для поиска в переменной envp имени переменной окружения, заданной в форме name=string, можно использовать стандартную библиотечную функцию getenv.



Пересылка данных


Теперь уже освоена процедура установления соединения между клиентом и сервером. Для сокетов типа SOCK_STREAM и клиент, и сервер получают дескрипторы файлов, которые могут использоваться для чтения или записи. В большинстве случаев для этого годятся обычные вызовы fdread и fdwrite. Если же необходимо задавать дополнительные параметры пересылки данных по сети, то можно использовать два новых системных вызова – send и recv. Эти вызовы имеют схожий интерфейс и ведут себя точно так же, как вызовы fdread и fdwrite, если их четвертый аргумент равен нулю.



Подключение клиента


Для выполнения запроса на подключение к серверному процессу клиент использует системный вызов connect.



Подпрограмма perror


Кроме linuxerror UNIX обеспечивает библиотечную процедуру (не системный вызов) perror. Для большинства традиционных команд UNIX использование perror является стандартным способом вывода сообщений об ошибках. Она имеет единственный аргумент строчного типа и при вызове отправляет на стандартный вывод диагностики сообщение, состоящее из переданного ей строчного аргумента, двоеточия и дополнительного описания ошибки, соответствующей текущему значению переменной linuxerror. Важно, что сообщение об ошибке отправляется на стандартный вывод диагностики, а не в стандартный вывод.

В вышеприведенном примере можно заменить строку, содержащую вызов writeln на:

perror('Ошибка при открытии файла nonesuch');

Если файл nonesuch не существует, то функция perror выведет сообщение:

Ошибка при открытии файла nonesuch: No such file or directory

Упражнение 2.17. Напишите процедуры, выполняющие те же действия, что и примитивы доступа к файлам, описанные в этой главе, но вызывающие функцию perror при возникновении ошибок или исключительных ситуаций.



Подробнее о вызове chmod


Системный вызов fstat расширяет использование вызова chmod, поскольку позволяет предварительно узнать значение кода доступа к файлу, что дает возможность изменять отдельные биты, а не менять весь код доступа целиком.

Следующая программа addx демонстрирует сказанное. Она вначале вызывает fstat для получения режима доступа к файлу из списка аргументов вызова программы. В случае успешного завершения вызова программа изменяет существующие права доступа так, чтобы файл был доступен для выполнения его владельцем. Эта программа может быть полезна для придания командным файлам, составленным пользователем, статуса исполняемых файлов.

(* Программа addx разрешает доступ на выполнение файла *)

uses linux,stdio;

const XPERM=0100;      (* Право на выполнение для владельца *)

var

  k:integer;

  statbuf:tstat;

 

begin

  (* Выполнить для всех файлов в списке аргументов *)

  for k := 1 to paramcount do

  begin

    (* Получить текущий код доступа к файлу *)

    if not fstat(paramstr(k), statbuf) then

    begin

      writeln('addx: ошибка вызова stat для ',paramstr(k));

      continue;

    end;

      (*

         Попытаться разрешить доступ на выполнение

         при помощи оператора побитового ИЛИ

       *)

    statbuf.mode := statbuf.mode or octal(XPERM);

    if not chmod (paramstr(k), statbuf.mode) then

      writeln('addx: ошибка изменения прав доступа для файла ', paramstr(k));

    end; (* Конец цикла *)

  halt(0);

end.

Наиболее интересный момент заключается здесь в способе изменения кода доступа файла при помощи побитового оператора ИЛИ. Это гарантирует, что устанавливается бит, заданный определением XPERM. Фактически мы могли бы расписать этот оператор в виде:

statbuf.mode := statbuf.mode or octal(XPERM);

Для ясности использована более короткая форма. Можно было бы также использовать вместо XPERM предусмотренную в системе постоянную STAT_IXUSR.

Упражнение 3.11. Приведенную задачу можно решить проще. Если вы знаете, как это сделать, напишите эквивалент этой программы при помощи командного интерпретатора.

Упражнение 3.12. Напишите свою версию команды chmod, используя ее описание в справочном руководстве вашей системы UNIX.



Поле c_cflag


Поле c_cflag содержит параметры, управляющие состоянием порта терминала. Обычно процессу не нужно изменять значения поля c_cflag своего управляющего терминала. Изменения этого поля могут понадобиться специальным коммуникационным приложениям, или программам, открывающим коммуникационные линии для дополнительного оборудования, например, для принтера. Значения поля c_cflag образуются объединением при помощи операции ИЛИ битовых констант, определенных в файле stdio. В общем случае каждая константа представляет бит поля c_cflag, который может быть установлен или сброшен. Не будем объяснять назначение всех битов этого поля (за полным описанием обратитесь к справочному руководству системы). Тем не менее есть четыре функции, которые позволяют опрашивать и устанавливать скорость ввода и вывода, закодированную в этом поле, не беспокоясь о правилах манипулирования битами.



Поле c_iflag


Поле c_iflag в структуре termios служит для общего управления вводом с терминала. Не будем рассматривать все возможные установки, а выберем из них только наиболее общие.

Три из связанных с этим полем флага связаны с обработкой символа возврата каретки. Они могут быть полезны в терминалах, которые посылают для обозначения конца строки последовательность, включающую символ возврата каретки CR. ОС UNIX, конечно же, ожидает в качестве символа конца строки символ LF (line feed) или символ перевода строки ASCII, символ NL (new line). Следующие флаги могут исправить ситуацию:

INLCR         Преобразовывать символ новой строки в возврат каретки

IGNCR         Игнорировать возврат каретки

ICRNL    Преобразовывать возврат каретки в символ новой строки

Три других поля c_iflag связаны с управлением потоком данных:

IXON     Разрешить старт/стопное управление выводом

IXANY    Продолжать вывод при нажатии любого символа

IXOFF    Разрешить старт/стопное управление вводом

Флаг IXON дает пользователю возможность управления выводом. Если этот флаг установлен, то пользователь может прервать вывод, нажав комбинацию клавиш Ctrl+S. Вывод продолжится после нажатия комбинации Ctrl+Q. Если также установлен параметр IXANY, то для возобновления приостановленного вывода достаточно нажатия любой клавиши, хотя для остановки вывода должна быть нажата именно комбинация клавиш Ctrl+S. Если установлен флаг IXOFF, то система сама пошлет терминалу символ остановки (как обычно, Ctrl+S), когда буфер ввода будет почти заполнен. После того как система будет снова готова к приему данных, для продолжения ввода будет послана комбинация символов Ctrl+Q.



Поле c_oflag


Поле с_оflag позволяет управлять режимом вывода. Наиболее важным флатом в этом поле является флаг OPOST. Если он не установлен, то выводимые символы передаются без изменений. В противном случае символы подвергаются обработке, заданной остальными флагами, устанавливаемыми в поле

c_oflag. Некоторые из них вызывают подстановку символа возврата каретки (CR) при выводе на терминал:

ONLCR

Преобразовать символ возврата каретки (CR) в символ возврата каретки (CR) и символ перевода строки (NL)

OCRNL

Преобразовать символ возврата каретки (CR) в символ перевода строки (NL)

ONOCR

Не выводить символ возврата каретки (CR) в нулевом столбце

ONLRET

Символ перевода строки (NL) выполняет функцию символа возврата каретки (CR)

Если установлен флаг ONLCR, то символы перевода строки NL

преобразуются в последовательность CR+NL (символ возврата каретки и символ перевода строки). Это гарантирует, что каждая строка будет начинаться с левого края экрана. И наоборот, если установлен флаг OCRNL, то символ возврат каретки будет преобразовываться в символ перевода строки. Установка флага

ONLRET сообщает драйверу терминала, что для используемого терминала символы перевода строки будут автоматически выполнять и возврат каретки. Если установлен флаг ONOCR, то символ возврата каретки не будет посылаться при выводе строки нулевой длины.

Большинство остальных флагов поля c_oflag относятся к задержкам в передаче, связанным с интерпретацией специальных символов, таких как перевод строки, табуляция, перевод страницы и др. Эти задержки учитывают время позиционирования указателя знакоместа, где должен быть выведен следующий символ на экране или принтере. Подробное описание этих флагов должно содержать справочное руководство системы.



Поле с_lflag


Возможно, наиболее интересным элементом структуры termios для программиста является поле c_lflag. Оно используется текущей дисциплиной линии связи для управления функциями терминала. Это поле содержит следующие флаги:

ICANON

Канонический построчный ввод

ISIG

Разрешить обработку прерываний

IEXTEN

Разрешить дополнительную (зависящую от реализации) обработку вводимых символов

ECHO

Разрешить отображение вводимых символов на экране

ЕСНОЕ

Отображать символ удаления как возврат-пробел-возврат

ЕСНОК

Отображать новую строку после удаления строки

ECHONL

Отображать перевод строки

NOFLSH

Отменить очистку буфера ввода после прерывания

TOSTOP

Посылать сигнал SIGTTOU при попытке вывода фонового процесса

Если установлен флаг ICANON, то включается канонический режим работы терминала. Как уже было видно выше, это позволяет использовать символы редактирования строки в процессе построчного ввода. Если флаг ICANON не установлен, то терминал находится в режиме прямого доступа (raw mode), который чаще всего используется полноэкранными программами и коммуникационными пакетами. Вызовы read будут при этом получать данные непосредственно из очереди ввода. Другими словами, основной единицей ввода будет одиночный символ, а не логическая строка. Программа при этом может считывать данные по одному символу (что обязательно для экранных редакторов) или большими блоками фиксированного размера (что удобно для коммуникационных программ). Но для того чтобы полностью управлять поведением вызова fdread, программист должен задать еще два дополнительных параметра. Это параметр VMIN, наименьшее число символов, которые должны быть приняты до возврата из вызова read, и параметр VTIME, максимальное время ожидания для вызова fdread. Оба параметра записываются в массиве с_сс. Это важная тема, которая будет подробно изучена в следующем разделе. А пока просто обратим внимание на то, как в следующем примере сбрасывается флаг ICANON.

uses stdio, linux;




var

  tdes:termios;

.

.

.

tcgetattr(0, tdes);

tdes.c_lflag := tdes.c_lflag and (not ICANON);

tcsetattr(0, TCSAFLUSH, tdes);

Если установлен флаг ISIG, то разрешается обработка клавиш прерывания (intr) и аварийного завершения (quit).

Обычно это позволяет пользователю завершить выполнение программы. Если флаг ISIG не установлен, то проверка не выполняется, и символы intr и quit передаются программе без изменений.

Если установлен флаг ECHO, то символы будут отображаться на экране по мере их набора. Сброс этого флага полезен для процедур проверки паролей и программ, которые используют клавиатуру для особых функций, например, для перемещения курсора или команд экранного редактора.

Если одновременно установлены флаги ЕСНОЕ и ECHO, то символ удаления будет отображаться как последовательность символов backspace-space-backspace (возврат–пробел–возврат). При этом последний символ на терминале немедленно стирается с экрана, и пользователь видит, что символ действительно был удален. Если флаг ЕСНОЕ установлен, а флаг ECHO нет, то символ удаления будет отображаться как space-backspace, тогда при его вводе будет удаляться символ в позиции курсора алфавитно-цифрового терминала.

Если установлен флаг ECHONL, то перевод строки будет всегда отображать на экране, даже если отображение символов отключено, что может быть полезным при выполнении самим терминалом локального отображения вводима символов. Такой режим часто называется полудуплексным режимом (half-duplex mode).

Последним флагом, заслуживающим внимания в этой группе флагов, является флаг NOFLSH, который подавляет обычную очистку очередей ввода и вывод при нажатии клавиш intr и quit

и очистку очереди ввода при нажатии клавиши susp.

Альтернативой TCGetAttr может быть вызов IOCtl:

uses Linux;

var

  tios : Termios;

begin

  IOCtl(1,TCGETS,@tios);

  WriteLn('Input Flags  : $',hexstr(tios.c_iflag,8));

  WriteLn('Output Flags : $',hexstr(tios.c_oflag,8));

  WriteLn('Line Flags   : $',hexstr(tios.c_lflag,8));

  WriteLn('Control Flags: $',hexstr(tios.c_cflag,8));

end.

Для удобства изменения параметров терминала в файле linux определена функция CFMakeRaw:


Получение информации о файле: вызов fstat


До сих пор были лишь рассмотрены вопросы, как можно установить или изменить основные связанные с файлами свойства. Системный вызов fstat позволяет процессу определить значения этих свойств в существующем файле.



Получение параметров локальной системы


В книге были рассмотрены некоторые процедуры, сообщающие системные ограничения (например, pathconf). Есть также и другие процедуры, служащие для той же цели:

sysconf

Обеспечивает доступ к конфигурационным параметрам, находящимся в файлах <limits.h> и <unistd.h>

SysInfo

Возвращает информацию о системе

GetHostName

Возвращает имя локального компьютера

uname

Возвращает указатель на структуру utsname, содержащую название операционной системы, имя узла, которое может использоваться системой в сети для установления связи, а также номер версии системы

UNIX

getpwent

Это семейство процедур обеспечивает доступ к данным из файла паролей /etc/passwd. Все следующие вызовы возвращают указатель на структуру passwd, определенную в файле <pwd.h>:

getpwnam(const char *username);

getpwuid(uid_t uid);

getpwent(void);

getgrent

Это семейство процедур связано с доступом к файлу описания групп /etc/group

getrlimit

Обеспечивает доступ к предельным значениям системных ресурсов, таких как память или доступное дисковое пространство

getlogin

cuserid

Получает имя пользователя для текущего процесса

Пример использования SysInfo:

Uses Linux;

Function Mb(L : Longint) : longint;

begin

  Mb:=L div (1024*1024);

end;

  

Var Info : TSysInfo;

    D,M,Secs,H : longint;

begin

  If Not SysInfo(Info) then

    Halt(1);

  With Info do

    begin

    D:=Uptime div (3600*24);

    UpTime:=UpTime mod (3600*24);

    h:=uptime div 3600;

    uptime:=uptime mod 3600;

    m:=uptime div 60;

    secs:=uptime mod 60;

    Writeln('Uptime : ',d,'days, ',h,' hours, ',m,' min, ',secs,' s.');

    Writeln('Loads  : ',Loads[1],'/',Loads[2],'/',Loads[3]);

    Writeln('Total Ram  : ',Mb(totalram),'Mb.');

    Writeln('Free Ram   : ',Mb(freeram),'Mb.');

    Writeln('Shared Ram : ',Mb(sharedram),'Mb.');

    Writeln('Buffer Ram : ',Mb(bufferram),'Mb.');

    Writeln('Total Swap : ',Mb(totalswap),'Mb.');

    Writeln('Free Swap  : ',Mb(freeswap),'Mb.');

    end;

end.

Пример использования uname:

Uses linux;

Var UN : utsname;

begin

  if Uname (UN) then

    With UN do

      begin

      Writeln ('Name       : ',pchar(@sysname[0]));

      Writeln ('Nodename   : ',pchar(@Nodename[0]));

      Writeln ('release    : ',pchar(@Release[0]));

      Writeln ('Version    : ',pchar(@Version[0]));

      Writeln ('Machine    : ',pchar(@Machine[0]));

      Writeln ('Domainname : ',pchar(@domainname[0]));

      end;

end.



Пользователи и права доступа


Для каждого файла в системе UNIX задан его владелец

(owner – один из пользователей системы; обычно пользователь, создавший файл). Истинный идентификатор пользователя представлен неотрицательным числом user-id, сокращенно uid, которое связывается с файлом при его создании.

В типичной системе UNIX связанный с определенным именем пользователя идентификатор uid находится в третьем поле записи о пользователе в файле паролей, то есть в строке файла /etc/passwd/, которая идентифицирует пользователя в системе. Типичная запись

keith:x:35:10::/usr/keith:/bin/ksh

указывает, что пользователь keith имеет uid 35.

Поля в записи о пользователе в файле паролей разделяются двоеточием. Первое поле задает имя пользователя. Второе, в данном случае х, – это маркер пароля пользователя. В отличие от ранних версий UNIX сам зашифрованный пароль обычно находится в другом файле, отличающемся для разных систем. Как уже было показано, третье поле содержит идентификатор пользователя uid. В четвертом поле находится идентификатор группы пользователя по умолчанию – group-id, сокращенно gid; подробнее он будет рассмотрен ниже. Пятое поле – это необязательное поле комментария. Шестое задает домашний каталог пользователя. Последнее поле – полное имя программы, которая запускается после входа пользователя в систему. Например, /bin/ksh – одна из стандартных оболочек UNIX.

Фактически для идентификации пользователя в системе UNIX нужен только идентификатор user-id. Каждый процесс UNIX обычно связывается с идентификатором пользователя, который запустил его на выполнение. При этом процесс является просто экземпляром выполняемой программы. При создании файла система устанавливает его владельца на основе идентификатора uid, создающего файл процесса.

Владелец файла позже может быть изменен, но только суперпользователем или владельцем файла. Следует отметить, что суперпользователь имеет имя root и его идентификатор uid всегда равен 0.

Помимо владельца файлы могут быть связаны с группами пользователей

(groups) которые представляют произвольный набор пользователей, благодаря чему становится возможным простой способ управления проектами, включающими несколько человек. Каждый пользователь принадлежит как минимум к одной группе.

Группы пользователей определяются в файле /etc/group. Каждая из них определена своим идентификатором gid, который, как и uid, является неотрицательным числом. Группа пользователя по умолчанию задается четвертым полем записи о нем в файле паролей.

Так же, как идентификатор пользователя uid, идентификатор группы gid пользователя наследуется процессом, который запускает пользователь. Поэтому при создании файла связанный с создающим его процессом идентификатор группы gid записывается наряду с идентификатором пользователя uid.



Понятие процесса


Как было уже рассмотрено в главе 1, процессом в терминологии UNIX является просто экземпляр выполняемой программы, соответствующий определению задачи в других средах. Каждый процесс объединяет код программы, значения данных в переменных программы и более экзотические элементы, такие как значения регистров процессора, стек программы и т.д.[4]

Командный интерпретатор для выполнения команд обычно запускает один или несколько процессов. Например, командная строка

$ cat file1 file2

приведет к созданию процесса для выполнения команды cat. Немного более сложная команда

$ ls | wc -l

приведет к созданию двух процессов для одновременного выполнения команд ls и wc. (Кроме этого, результат программы ls, вывод списка файлов в каталоге, перенаправляется

с помощью программного канала (pipe) на вход программы подсчета числа слов wc.)

Так как процессы соответствуют выполняемым программам, не следует путать их с программами, которые они выполняют. Несколько процессов могут выполнять одну и ту же программу. Например, если несколько пользователей выполняют одну и ту же программу редактора, то каждый из экземпляров программы будет отдельным процессом.

Любой процесс UNIX может, в свою очередь, запускать другие процессы. Это придает среде процессов UNIX иерархическую структуру, подобную дереву каталогов файловой системы. На вершине дерева процессов находится единственный управляющий процесс, экземпляр очень важной программы init, которая является предком всех системных и пользовательских процессов.

Система UNIX предоставляет программисту набор системных вызовов для создания процессов и управления ими. Если исключить различные средства для межпроцессного взаимодействия, то наиболее важными из оставшихся будут:

fork

Используется для создания нового процесса, дублирующего вызывающий. Вызов fork является основным примитивом создания процессов

ехес

Семейство библиотечных процедур и один системный вызов, выполняющих одну и ту же функцию – смену задачи процесса за счет перезаписи его пространства памяти новой программой. Различие между вызовами ехес в основном лежит в способе задания списка их аргументов

wait

Этот вызов обеспечивает элементарную синхронизацию процессов. Он позволяет процессу ожидать завершения другого процесса, обычно логически связанного с ним

halt

Используется для завершения процесса

Далее рассмотрим, что представляют собой процессы UNIX в целом и вышеприведенные четыре важных системных вызова в частности.



Порты


Кроме адреса компьютера, клиентская программа должна иметь возможность подключения к нужному серверному процессу. Серверный процесс ждет подключения к заданному номеру порта (port number). Поэтому клиентский процесс должен выполнить запрос на подключение к определенному порту на заданном компьютере. Если продолжить аналогию с пересылкой писем по почте, то это равносильно дополнению адреса номером комнаты или квартиры.

Некоторые номера портов по соглашению считаются отведенными для стандартных сервисов, таких как ftp или rlogin. Эти номера портов записаны в файле /etc/services. В общем случае порты с номерами, меньшими 1024, считаются зарезервированными для системных процессов UNIX. Все остальные порты доступны для пользовательских процессов.



Построчный ввод и вывод


Существует также набор простых процедур для ввода и вывода строк (под которыми понимается последовательность символов, завершаемая символом перевода строки). Эти процедуры удобно использовать в интерактивных программах, которые выполняют чтение с клавиатуры и вывод на экран терминала. Основные процедуры для ввода строк называются gets и fgets.



Посылка сигналов другим процессам: вызов kill


Процесс вызывает процедуру sigaction для установки реакции на поступление сигнала. Обратную операцию, посылку сигнала, выполняет системный вызов kill, описанный следующим образом:



Посылка сигналов самому процессу: вызовы sigraise и alarm


Функция sigraise просто посылает сигнал выполняющемуся процессу:



Потоки управления


Потоки управления, или нити (threads) являются облегченной версией процессов – поддерживающие их системы позволяют выполнять одновременно несколько таких потоков в рамках одного процесса, при этом все потоки могут работать с данными процесса. Их применение может быть очень ценным для повышения производительности и интерактивности определенных классов задач, но техника программирования многопотоковых программ является довольно сложной. Многие версии

UNIX поддерживали различные подходы к организации потоков выполнения, но теперь выработана стандартная модель (POSIX threads), включенная в стандарт POSIX и пятую версию спецификации XSI. Интерфейс функций работы с потоками управления может быть описан в справочном руководстве системы под заголовком pthread_.

Для создания потока может использоваться вызов Clone:



Права доступа и режимы файлов


Владелец обладает исключительными правами обращения с файлами. В частности, владелец может изменять связанные с файлом права доступа

(permissions). Права доступа определяют возможности доступа к файлу других пользователей. Они затрагивают три группы пользователей:

1)

владелец файла;

2)      все пользователи, кроме владельца файла, принадлежащие к связанной с файлом группе;

3)      все пользователи, не входящие в категории 1 или 2.

Для каждой категории пользователей существуют три основных типа прав доступа к файлам. Они определяют, может ли пользователь определенной категории выполнять:

–        чтение из файла;

–        запись в файл;

–        запуск файла на выполнение. В этом случае файл обычно является программой или последовательностью команд оболочки.

Как обычно, суперпользователь выделен в отдельную категорию и может оперировать любыми файлами, независимо от связанных с ними прав чтения, записи или выполнения.

Система хранит связанные с файлом права доступа в битовой маске, называемой кодом доступа к файлу (file mode). Хотя модуль linux и определяет символьные имена для битов прав доступа, большинство программистов все еще предпочитает использовать восьмеричные постоянные, приведенные в табл. 3.1 – при этом символьные имена являются относительно недавним и весьма неудобным нововведением. Следует обратить внимание, что в языке С восьмеричные постоянные всегда начинаются с нуля, иначе компилятор будет расценивать их как десятичные. В Паскале же для записи восьмеричных чисел удобно использовать функцию octal, параметром которой является восьмеричное число, записанное десятичными числами, а результатом – значение в десятеричной системе:



Права доступа к каталогам


Так же как и с обычными файлами, с каталогами связаны права доступа, определяющие возможность доступа к ним различных пользователей.

Права доступа к каталогам организованы точно так же, как и права доступа к обычным файлам, разбиты на три группы битов rwx, определяющих права владельца файла, пользователей из его группы и всех остальных пользователей системы.

Тем не менее, хотя эти права доступа представлены так же, как и у обычных файлов, интерпретируются они по-другому:

–        право доступа к каталогу на чтение показывает, что соответствующий класс пользователей может выводить список содержащихся в каталоге файлов и подкаталогов. Однако это не означает, что пользователи могут читать содержащуюся в файлах информацию – это определяется правами доступа к отдельным файлам;

–        право доступа к каталогу на запись позволяет пользователю создавать в каталоге новые файлы и удалять существующие. И снова это не дает пользователю права изменять содержимое существующих файлов, если это не разрешено правами доступа к отдельным файлам. Вместе с тем при этом можно удалить существующий файл и создать новый с тем же самым именем, и это, по сути, означает то же самое, что и изменение содержимого исходного файла;

–        право доступа к каталогу на выполнение, называемое также правом выполнения, или правом прохождения, поиска

(search permission), позволяет пользователю перейти в каталог при помощи команды cd или системного вызова chdir в программе (который будет рассмотрен позже). Кроме этого, чтобы иметь возможность открыть файл или выполнить программу, пользователь должен иметь право доступа на выполнение для всех ведущих к файлу каталогов, входящих в абсолютный путь файла;

–        бит фиксации, STAT_ISVTX, позволяет установить дополнительную защиту файлов, находящихся в каталоге. Из такого каталога пользователь может удалить только те файлы, которыми он владеет или на которые он имеет явное право доступа на запись, даже при наличии права на запись в каталог. Примером является каталог /tmp.








Каталог ben


123


.


\0






247


.


.


\0





260


b


o


o


k


\0



401


m


e


m


o


s


\0


Каталог book


260


.


\0






123


.


.


\0





566


c


h


a


p


1


\0


567


c


h


a


p


2


\0


590


c


h


a


p


3


\0


Каталог memos


401


.


\0






123


.


.


\0





800


k


h


\0





810


k


d


\0





077


m


w


\0




Рис. 4.5. Каталоги ben, book и memos

На уровне командного интерпретатора связанные с каталогами права доступа можно вывести при помощи команды ls с параметром -l. Подкаталоги будут обозначаться буквой d

в первой позиции, например:

$ ls -l

total 168

-rw-r-----    1 ben other 39846 Oct 12 21:21 dir_t

drwxr-x---    2 ben other 32    Oct 12 22:02 expenses

-rw-r-----    1 ben other 46245 Oct 13 10:34 new

-rw-r-----    1 ben other 3789  Sep 2  18:40 pwd_text

-rw-r-----    1 ben other 1310  Sep 13 10:38 test.с

Здесь строка, описывающая подкаталог expenses, помечена буквой d

в начале строки. Видно, что владелец этого каталога (пользователь ben) имеет права на чтение, запись и выполнение (поиск), пользователи группы файла (называющейся other) имеют права на чтение и выполнение (переход в каталог), а для всех остальных пользователей доступ полностью запрещен.

Если требуется получить информацию о текущем каталоге, можно задать в команде ls

кроме параметра -l еще и параметр -d, например:

$ ls -ld

drwxr-x---  3 ben other 128 Oct 12 22:02 .

Помните, что имя .

(точка) в конце листинга обозначает текущий каталог.


Предостережение


Здесь необходимо сделать несколько предостережений. Во-первых, существует предельное число файлов, которые могут быть одновременно открыты программой, – в стандарте POSIX (а следовательно, и в спецификации XSI) не менее двадцати[1]. Чтобы обойти эту проблему, требуется использовать системный вызов fdclose, тем самым сообщая системе, что работа с файлом закончена. Вызов fdclose будет рассмотрен в разделе 2.1.5. Может также существовать предел суммарного числа файлов, открытых всеми процессами, определяемый размером таблицы в ядре. Во-вторых, в ранних версиях UNIX и в параметре flags непосредственно задавались численные значения. Это все еще достаточно распространенный, хотя и не совсем удовлетворительный прием – задание численных значений вместо имен постоянных, определенных в модуле linux. Поэтому может встретиться оператор типа

filedes := fdopen(filename, 0);

который в обычных условиях открывает файл только для чтения и эквивалентен следующему оператору:

filedes := fdopen (filename, Open_RDONLY);

Упражнение 2.1. Создайте небольшую программу, описанную выше. Проверьте ее работу при условии, что файл junk не существует. Затем создайте файл junk с помощью текстового редактора и снова запустите программу. Содержимое файла junk не имеет значения.



Преобразование строк в числовые значения


Стандарт ANSI С определяет два набора функций для преобразования строк в числовые величины:



Прием и передача UDP-сообщений


Для сокетов UDP есть два новых системных вызова –

sendto и recvfrom. Параметр sockfd в обоих вызовах задает связанный с локальным адресом сокет, через который принимаются и передаются пакеты.



Прием запроса на установку TCP-соединения


Когда сервер получает от клиента запрос на соединение, он должен создать новый сокет для работы с новым соединением. Первый же сокет используется только для установки соединения. Дополнительный сокет создается при помощи вызова accept, принимающего очередное соединение.



История UNIX


ОС UNIX начала свою историю с того момента, как в 1969 г. Кен Томсон (Ken Thomson) и Деннис Ричи (Dennis Ritchie) с коллегами начали работу над ней на «стоящем в углу свободном компьютере PDP-7». С тех пор ОС UNIX нашла применение в сфере бизнеса и образования, а также стала основой ряда международных стандартов.

Кратко перечислим некоторые вехи истории UNIX:

шестая редакция системы, 1975 (Sixth Edition, или Version 6). Первый широко известный в техническом сообществе коммерческий вариант и основа первой версии Berkeley UNIX;

–        Xenix (1980). Версия от Microsoft – один из коммерческих вариантов UNIX с измененным названием, возникших в начале восьмидесятых годов;

–        System V (1983-92). Одна из наиболее важных версий от создателя UNIX – корпорации AT&T. Наследница Version 7 и последовавшей за ней System III;

–        Berkeley UNIX (версия 4.2 в 1984 г., 4.4 в 1993 г.). Разработанная в университете Berkeley, эта версия также была одной из наиболее важных версий UNIX и ввела в семейство UNIX много новых средств;

–        POSIX (с 1988 г. и далее). Ключевой набор стандартов комитета IEEE (см. ниже), сыгравший важную роль в развитии UNIX;

–        Open Portability Guides (XPG3 в 1990 г., XPG4.2 в 1994 г.). Практическая спецификация, объединившая целый ряд основных стандартов и рецептов использования UNIX систем. Консорциум Х/Open в конце концов приобрел торговую марку UNIX.

Кроме того, существовало множество коммерческих взаимодействий, таких как передача торговой марки UNIX от компании AT&T к компании Novell, а затем консорциуму Х/Open, а также слияние организаций Х/Open и Open Software Foundation. Однако это достаточно упрощенный взгляд на историю UNIX. На самом деле дерево семейства UNIX является очень сложным.



Приложение Модуль stdio


Назначение модуля – восполнить недостаток средств прикладного и системного программирования на языке Паскаль аналогичными средствами библиотек языка Си.

unit stdio;

{$mode objfpc}

interface

(* A streammarker remembers a position in a buffer. *)

uses linux,sockets;

const

  EOF=-1;         (* признак конца файла для stdio *)

 

  WCOREFLAG=$80;  (* флаг сброса дампа памяти *)

 

  (* Размер буфера для чтения и записи *)

  BUFSIZ=8192;

  NULL=nil;     

  _IOFBF=0;  (* setvbuf should set fully buffered *)

  _IOLBF=1;  (* setvbuf should set line buffered *)

  _IONBF=2;  (* setvbuf should set unbuffered *)

  MAX_INPUT=255;  (* size of the type-ahead buffer *)

 

  _SIGSET_NWORDS=1024 div (8 * sizeof (longint)); (* sizeof(sigset_t) *)

 

  (* флаги для pathconf/fpathconf *)

  (* returns  the maximum number of links to the file.  If filedes or

   * path refer to a directory, then the value applies to  the  whole

   * directory.

   *)

  _PC_LINK_MAX=0;

  (* returns  the  maximum  length  of  a formatted input line, where

   * filedes or path must refer to  a  terminal.

   *)

  _PC_MAX_CANON=1;

  (* returns  the  maximum  length of an input line, where filedes or

   * path must refer to  a  terminal.

   *)

  _PC_MAX_INPUT=2;

  (* returns  the  maximum length of a filename in the directory path

   * or filedes.  the process is allowed to create.

   *)

  _PC_NAME_MAX=3;

  (* returns  the  maximum length of a relative pathname when path or

   * filedes is the current  working  directory.

   *)

  _PC_PATH_MAX=4;

  (* returns the size of the pipe buffer, where filedes must refer to

   * a pipe or FIFO and path must refer to a FIFO.

   *)

  _PC_PIPE_BUF=5;

  (* returns  nonzero  if  the  chown(2) call may not be used on this

   * file.  If filedes or  path  refer  to  a  directory,  then  this

   * applies to all files in that directory.

   *)

  _PC_CHOWN_RESTRICTED=6;

  (* returns   nonzero   if   accessing   filenames    longer    than




аа * _POSIX_NAME_MAXа generates an error.

аа *)

а _PC_NO_TRUNC=7;

а (* returns nonzero if special character processing can be disabled,

аа * where filedes or path must refer to a terminal.

аа *)

а _PC_VDISABLE=8;

а _PC_SYNC_IO=9;

а _PC_ASYNC_IO=10;

а _PC_PRIO_IO=11;

а _PC_SOCK_MAXBUF=12;

а _PC_FILESIZEBITS=13;

а _PC_REC_INCR_XFER_SIZE=14;

а _PC_REC_MAX_XFER_SIZE=15;

а _PC_REC_MIN_XFER_SIZE=16;

а _PC_REC_XFER_ALIGN=17;

а _PC_ALLOC_SIZE_MIN=18;

а _PC_SYMLINK_MAX=19;

а (* For posix fcntl() and `l_type' field of a `struct flock' for lockf().а *)

а F_RDLCK=0;аааааа (* Read lock.а *)

а F_WRLCK=1;ааа ааа(* Write lock.а *)

а F_UNLCK=2;аааааа (* Remove lock.а *)

а F_DUPFD=0;аааааа (* Duplicate fildes *)

а (* Constants for ulimit *)

а UL_GETFSIZE=1;ааааа (* тючтЁр•рхЄ єёЄрэютыхээюх юуЁрэшўхэшх эр ЁрчьхЁ

аааааааааааааааааааааааа Їрщыр (т сыюърї яю 512 срщЄ) *)

а UL_SETFSIZE=2;ааааа (* єёЄрэртыштрхЄ яЁхфхы№эvщ ЁрчьхЁ Їрщыр *)

а __UL_GETMAXBRK=3;аа (* тючтЁр•рхЄ ьръёшьры№эю тючьюцэvщ рфЁхё ёхуьхэЄр фрээvї *)

а __UL_GETOPENMAX=4;а (* тючтЁр•рхЄ ьръёшьры№эюх ъюышўхёЄтю Їрщыют, ъюЄюЁюх

аааааааааааааааааааааааа яЁюЎхёё ьюцхЄ юЄъЁvЄ№ *)

а FD_SETSIZE=64;

а

а (* Macros used as `request' argument to `ioctl'.а *)

а __SID=byte('S') shl 8;

а I_NREADаааа =__SID or 1;а (* Counts the number of data bytes in the data

аааааааааааааааааааааааааааааа block in the first message.а *)

а I_PUSHааааа =__SID or 2;а (* Push STREAMS module onto top of the current

аааааааааааааааааааааааааааааа STREAM, just below the STREAM head.а *)

а I_POPаааааа =__SID or 3;а (* Remove STREAMS module from just below the

аааааааааааааааааааааааааааааа STREAM head.а *)

а I_LOOKааааа =__SID or 4;а (* Retrieve the name of the module just below

аааааааааааааааааааааааааааааа the STREAM head and place it in a character

аааааааааааааааааааааааааааааа string.а *)

а I_FLUSHаааа =__SID or 5;а (* Flush all input and/or output.а *)

а I_SRDOPTааа =__SID or 6;а (* Sets the read mode.а *)



а I_GRDOPTааа =__SID or 7;а (* Returns the current read mode setting.а *)

а I_STRаааааа =__SID or 8;а (* Construct an internal STREAMS `ioctl'

аааааааааааааааааааааааааааааа message and send that message downstream. *)

а I_SETSIGааа =__SID or 9;а (* Inform the STREAM head that the process

ааааааааааааааааааа аааааааааааwants the SIGPOLL signal issued.а *)

а I_GETSIGааа =__SID or 10; (* Return the events for which the calling

аааааааааааааааааааааааааааааа process is currently registered to be sent

аааааааааааааааааааааааааааааа a SIGPOLL signal.а *)

а I_FINDа аааа=__SID or 11; (* Compares the names of all modules currently

аааааааааааааааааааааааааааааа present in the STREAM to the name pointed to

аааааааааааааааааааааааааааааа by `arg'.а *)

а I_LINKааааа =__SID or 12; (* Connect two STREAMs.а *)

а I_UNLINKааа =__SID or 13; (* Disconnects the two STREAMs.а *)

а I_PEEKааааа =__SID or 15; (* Allows a process to retrieve the information

аааааааааааааааааааааааааааааа in the first message on the STREAM head read

аааааааааааааааааааааааааааааа queue without taking the message off the

аааааааааааааааааааааааааааааа queue.а *)

а I_FDINSERTа =__SID or 16; (* Create a message from the specifiedааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааа

аааааааааааааааааааааааааааааа buffer(s), adds information about anotherаааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааа

аааааааааааааааааааааааааааааа STREAM, and send the message downstream.а *)

а I_SENDFDааа =__SID or 17; (* Requests the STREAM associated with `fildes'

аааааааааааааааааааааааааааааа to send a message, containing a file

аааааааааааааааааааааааааааааа pointer, to the STREAM head at the other end

аааааааааааааааа ааааааааааааааof a STREAMS pipe.а *)

а I_RECVFDааа =__SID or 14; (* Non-EFT definition.а *)

а I_SWROPTааа =__SID or 19; (* Set the write mode.а *)



а I_GWROPTааа =__SID or 20; (* Return the current write mode setting.а *)

а I_LISTааааа =__SID or 21; (* List all the module names on the STREAM, up

аааааааааааааааааааааааааааааа to and including the topmost driver name. *)

а I_PLINKаааа =__SID or 22; (* Connect two STREAMs with a persistent

ааааааааааааааааааааааааааааааа link.а *)

а I_PUNLINKаа =__SID or 23; (* Disconnect the two STREAMs that were

аааааааааааааааааааааааааааааа connected with a persistent link.а *)

а I_FLUSHBAND =__SID or 28; (* Flush only band specified.а *)

а I_CKBANDааа =__SID or 29; (* Check if the message of a given priority

ааааааааааааа аааааааааааааааааband exists on the STREAM head read

аааааааааааааааааааааааааааааа queue.а *)

а I_GETBANDаа =__SID or 30; (* Return the priority band of the first

аааааааааааааааааааааааааааааа message on the STREAM head read queue.а *)

а I_ATMARKааа =__SID or 31; (* See if the current message on the STREAM

аааааааааааааааааааааааааааааа head read queue is "marked" by some module

аааааааааааааааааааааааааааааа downstream.а *)

а I_SETCLTIME =__SID or 32; (* Set the time the STREAM head will delay when

аааа ааааааааааааааааааааааааааa STREAM is closing and there is data on

аааааааааааааааааааааааааааааа the write queues.а *)

а I_GETCLTIME =__SID or 33; (* Get current value for closing timeout.а *)

а I_CANPUTааа =__SID or 34; (* Check if a certain band is writable.а *)

а (* фы  шэЄхЁЇхщёр ёюъхЄют *)а

а INADDR_ANY=0;

а { Maximum queue length specificable by listen.аа }

а SOMAXCONN = 5;

а { Flags we can use with send/ and recv.а }

а { process out-of-band dataа }

а MSG_OOB = $1;

а { peek at incoming messageа }

ааMSG_PEEK = $2;

а { send without using routing tablesа }

а MSG_DONTROUTE = $4;

а { Setsockoptions(2) level. Thanks to BSD these must match IPPROTO_xxxа }

а SOL_IP = 0;

а SOL_IPX = 256;

а SOL_AX25 = 257;

а SOL_ATALK = 258;

а SOL_NETROM = 259;

а SOL_TCP = 6;

а SOL_UDP = 17;

а { IP optionsа }

а IPTOS_LOWDELAY = $10;



а IPTOS_THROUGHPUT = $08;

а IPTOS_RELIABILITY = $04;

а { These need to appear somewhere around hereа }

а IP_DEFAULT_MULTICAST_TTL = 1;

а IP_DEFAULT_MULTICAST_LOOP = 1;

а IP_MAX_MEMBERSHIPS = 20;

а { IP options for use with WinSockа }

а IP_OPTIONS = 1;

а IP_MULTICAST_IF = 2;

а IP_MULTICAST_TTL = 3;

а IP_MULTICAST_LOOP = 4;

а IP_ADD_MEMBERSHIP = 5;

а IP_DROP_MEMBERSHIP = 6;

а IP_TTL = 7;

а IP_TOS = 8;

а IP_DONTFRAGMENT = 9;

а { IPX optionsа }

а IPX_TYPE = 1;

а { TCP options }

а TCP_NODELAY = $0001;

а TCP_MAXSEG = 2;

а { The various priorities.а }

а SOPRI_INTERACTIVE = 0;

а SOPRI_NORMAL = 1;

а SOPRI_BACKGROUND = 2;

а (* єяЁртыхэшх ЄхЁьшэрыюь *)

а TCOOFF = 0;

а TCOON = 1;

а TCIOFF = 2;

а TCION = 3;

а TCGETA = 5;

а TCSETA = 6;

а TCSETAW = 7;

а TCSETAF = 8;

а TCIFLUSH = 0;

а TCOFLUSH = 1;

а TCIOFLUSH = 2;

а TCFLSH = 3;

а TCSAFLUSH = 1;

а TCSANOW = 2;

а TCSADRAIN = 3;

а TCSADFLUSH = 4;

а TIOCPKT = 6;

а TIOCPKT_DATA = 0;

а TIOCPKT_FLUSHREAD = 1;

а TIOCPKT_FLUSHWRITE = 2;

а TIOCPKT_STOP = 4;

а TIOCPKT_START = 8;

а TIOCPKT_NOSTOP = 16;

а TIOCPKT_DOSTOP = 32;

а { To be compatible with socket versionа }

а FIONBIO = $8004667e;

а { iflag bitsа }

а IGNBRK = $00001;

а BRKINT = $00002;

а IGNPAR = $00004;

а IMAXBEL = $00008;

а INPCK = $00010;

а ISTRIP = $00020;

а INLCR = $00040;

а IGNCR = $00080;

а ICRNL = $00100;

а IXON = $00400;

а IXOFF = $01000;

а IUCLC = $04000;

а IXANY = $08000;

а PARMRK = $10000;

а { oflag bitsа }

а OPOST = $00001;

а OLCUC = $00002;

а OCRNL = $00004;

а ONLCR = $00008;

а ONOCR = $00010;

а ONLRET = $00020;

а OFILL = $00040;

а CRDLY = $00180;

а CR0 = $00000;

а CR1 = $00080;

а CR2 = $00100;

а CR3 = $00180;

а NLDLY = $00200;

а NL0 = $00000;

а NL1 = $00200;

а BSDLY = $00400;

а BS0 = $00000;

а BS1 = $00400;

а TABDLY = $01800;

а TAB0 = $00000;

а TAB1 = $00800;

а TAB2 = $01000;

а TAB3 = $01800;

а XTABS = $01800;

а VTDLY = $02000;

а VT0 = $00000;

а VT1 = $02000;



а FFDLY = $04000;

а FF0 = $00000;

а FF1 = $04000;

а OFDEL = $08000;

а { cflag bitsа }

а { Baud rate values.а These must fit in speed_t, which is unsigned

а char.а See also the extended baud rates below.а These baud rates

а set an additional bit.а }

а CBAUD = $0100f;

а B0 = $00000;

а B50 = $00001;

а B75 = $00002;

а B110 = $00003;

а B134 = $00004;

а B150 = $00005;

а B200 = $00006;

а B300 = $00007;

а B600 = $00008;

а B1200 = $00009;

а B1800 = $0000a;

а B2400 = $0000b;

а B4800 = $0000c;

а B9600 = $0000d;

а B19200 = $0000e;

а B38400 = $0000f;

а CSIZE = $00030;

а CS5 = $00000;

а CS6 = $00010;

а CS7 = $00020;

а CS8 = $00030;

а CSTOPB = $00040;

а CREAD = $00080;

а PARENB = $00100;

а PARODD = $00200;

а HUPCL = $00400;

а CLOCAL = $00800;

а CBAUDEX = $0100f;

а B57600 = $01001;

ааB115200 = $01002;

а B128000 = $01003;

а B256000 = $01003;

а CRTSXOFF = $04000;

а CRTSCTS = $08000;

а { lflag bitsа }

а ISIG = $0001;

а ICANON = $0002;

а ECHO = $0004;

а ECHOE = $0008;

а ECHOK = $0010;

а ECHONL = $0020;

а NOFLSH = $0040;

а TOSTOP = $0080;

а IEXTEN = $0100;

а FLUSHO = $0200;

а ECHOKE = $0400;

а ECHOCTL = $0800;

а VDISCARD = 1;

а VEOL = 2;

а VEOL2 = 3;

а VEOF = 4;

а VERASE = 5;

а VINTR = 6;

а VKILL = 7;

а VLNEXT = 8;

а VMIN = 9;

а VQUIT = 10;

а VREPRINT = 11;

а VSTART = 12;

а VSTOP = 13;

а VSUSP = 14;

а VSWTC = 15;

а VTIME = 16;

а VWERASE = 17;

а CNUL = 0;

а CDEL = $0007f;

а CESC = byte('\');

а CINTR = byte('C') and $1f;

а CQUIT = $0001c;

а CERASE = byte('H') and $1f;

а CKILL = byte('U') and $1f;

а CEOT = byte('D') and $1f;

а CEOL = 0;

а CEOL2 = 0;

а CEOF = byte('D') and $1f;

а CSTART = byte('Q') and $1f;

а CSTOP = byte('S') and $1f;

а CSWTCH = $0001a;

а NSWTCH = 0;

а CSUSP = byte('Z') and $1f;

а CDSUSP = byte('Y') and $1f;

а CRPRNT = byte('R') and $1f;

а CFLUSH = byte('O') and $1f;

а CWERASE = byte('W') and $1f;

а CLNEXT = byte('V') and $1f;

ааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааа ааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааа



а

type PFILE=^TFILE;

_IO_marker=record

а next:^_IO_marker;

а sbuf:PFILE;

а _pos:integer;

end;

P_IO_marker=^_IO_marker;

ppchar=^pchar;

pinteger=^integer;

plongint=^longint;

sigset_t=record

а __val:array [0.._SIGSET_NWORDS-1] of longint;

end;

psigset_t=^sigset_t;

__jmp_buf=array [0..5] of longint;

__jmp_buf_tag=record

а __jmpbuf:__jmp_buf;аааааааааааааа (* Calling environment.а *)

а __mask_was_saved:longint;аааааааа (* Saved the signal mask?а *)

а __saved_mask:sigset_t;ааааааааааааа (* Saved signal mask.а *)

end;

jmp_buf=array [0..0] of __jmp_buf_tag;

sigjmp_buf=jmp_buf;

flockrec=record

а l_type:word;аааа (* Type of lock: F_RDLCK, F_WRLCK, or F_UNLCK.а *)

а l_whence:word;аа (* Where `l_start' is relative to (like `lseek').а *)

а l_start:longint;ааа (* Offset where the lock begins.а *)

а l_len:longint;ааааа (* Size of the locked area; zero means until EOF.а *)

а l_pid:longint;ааааа (* Process holding the lock.а *)

end;

(* фы  шэЄхЁЇхщёр ёюъхЄют *)

in_addr=record

а s_addr:cardinal;

end;

pin_addr=^in_addr;

in_addr_t=cardinal;

(* фы  ЁрсюЄv ёю тЁхьхэхь *)

tm=record

а tm_sec:longint;аааааааа (* seconds *)

а tm_min:longint;аааааааа (* minutes *)

а tm_hour:longint;ааааааа (* hours *)

а tm_mday:longint;ааааааа (* day of the month *)

а tm_mon:longint;аааааааа (* month *)

а tm_year:longint;ааааааа (* year *)

а tm_wday:longint;ааааааа (* day of the week *)

а tm_yday:longint;ааааааа (* day in the year *)

а tm_isdst:longint;аааааа (* daylight saving time *)

end;

ptm=^tm;

TFILE=record

а _flags:integer;ааааа (* High-order word is _IO_MAGIC; rest is flags. *)

а (* The following pointers correspond to the C++ streambuf protocol. *)

а (* Note:а Tk uses the _IO_read_ptr and _IO_read_end fields directly. *)

а _IO_read_ptr:pchar;аа (* Current read pointer *)

а _IO_read_end:pchar;аа (* End of get area. *)

а _IO_read_base:pchar;а (* Start of putback+get area. *)



а _IO_write_base:pchar; (* Start of put area. *)

а _IO_write_ptr:pchar;а (* Current put pointer. *)

а _IO_write_end:pchar;а (* End of put area. *)

а _IO_buf_base:pchar;аа (* Start of reserve area. *)

а _IO_buf_end:pchar;ааа (* End of reserve area. *)

а (* The following fields are used to support backing up and undo. *)

а _IO_save_base:pchar; (* Pointer to start of non-current get area. *)

а _IO_backup_base:pchar;а (* Pointer to first valid character of backup area *)

а _IO_save_end:pchar; (* Pointer to end of non-current get area. *)

а _markers:P_IO_marker;

а _chain:^TFILE;

а _fileno:integer;

а _blksize:integer;

а _old_offset:longint; (* This used to be _offset but it's too small.а *)

а _cur_column:word;

а _vtable_offset:byte;

а _shortbuf:array[1..1]of char;

а _lock:pointer;

end;

var

а stdin,stdout:pfile;

function tmpfile:pfile;cdecl;external 'c';

(* Generate a temporary filename.а *)

function tmpnam(__s:pchar):pchar;cdecl;external 'c';

(* Close STREAM.а *)

function fclose(__stream:pfile):integer;cdecl;external 'c';

(* Flush STREAM, or all streams if STREAM is NULL.а *)

function fflush (__stream:pfile):integer;cdecl;external 'c';

(* Close all streams.а *)

function fcloseall:integer;cdecl;external 'c';

(* Open a file and create a new stream for it.а *)

function fopen(fname:pchar;mode:pchar):pfile;cdecl;external 'c';

(* Open a file by fd and create a new stream for it.а *)

function fdopen(fildes:longint;mode:pchar):pfile;cdecl;external 'c';

а

(* Open a file, replacing an existing stream with it. *)

function freopen (_filename:pchar;__modes:pchar;__stream:pfile):pfile;cdecl;external 'c';

(* Create a new stream that refers to a memory buffer.а *)

function fmemopen(__s:pointer;__len:longint;__modes:pchar):pfile;cdecl;external 'c';

(* Open a stream that writes into a malloc'd buffer that is expanded as

аа necessary.а *BUFLOC and *SIZELOC are updated with the buffer's location



аа and the number of characters written on fflush or fclose.а *)

function open_memstream(__bufloc:ppchar;__sizeloc:plongint):pfile;cdecl;external 'c';

(* If BUF is NULL, make STREAM unbuffered.

аа Else make it use buffer BUF, of size BUFSIZ.а *)

procedure setbuf(__stream:pfile;__buf:pchar);cdecl;external 'c';

(* Make STREAM use buffering mode MODE.

аа If BUF is not NULL, use N bytes of it for buffering;

аа else allocate an internal buffer N bytes long.а *)

function setvbuf(__stream:pfile;__buf:pchar;

аааааааааааааааа __modes:longint;__n:longint):integer;cdecl;external 'c';

(* If BUF is NULL, make STREAM unbuffered.

аа Else make it use SIZE bytes of BUF for buffering.а *)

procedure setbuffer(__stream:pfile;__buf:pchar;

аааааааааа ааа __size:longint);cdecl;external 'c';

(* Make STREAM line-buffered.а *)

procedure setlinebuf(__stream:pfile);cdecl;external 'c';

(* Write formatted output to STREAM.а *)

function fprintf(__stream:pfile;fm:pchar;args:array of const):integer;cdecl;external 'c';

(* Write formatted output to stdout.а *)

function printf(fm:pchar;args:array of const):integer;cdecl;external 'c';

(* Write formatted output to S.а *)

function sprintf(__s:pchar;fm:pchar;args:array of const):integer;cdecl;external 'c';

(* Read formatted input from STREAM.а *)

function fscanf(__stream:pfile;fm:pchar;args:array of const):integer;cdecl;external 'c';

(* Read formatted input from stdin.а *)

function scanf(fm:pchar;args:array of const):integer;cdecl;external 'c';

(* Read formatted input from S.а *)

function sscanf(s:pchar;fm:pchar;args:array of const):integer;cdecl;external 'c';

(* Read a character from STREAM.а *)

function fgetc(__stream:pfile):integer;cdecl;external 'c';

function getc(__stream:pfile):integer;cdecl;external 'c';

(* Read a character from stdin.а *)

function getchar:integer;cdecl;external 'c';

(* These are defined in POSIX.1:1996.а *)



function getc_unlocked(__stream:pfile):integer;cdecl;external 'c';

function getchar_unlocked:integer;cdecl;external 'c';

(* Faster version when locking is not necessary.а *)

function fgetc_unlocked(__stream:pfile):integer;cdecl;external 'c';

(* Write a character to STREAM.а *)

function fputc(__c:integer;__stream:pfile):integer;cdecl;external 'c';

function putc(__c:integer;__stream:pfile):integer;cdecl;external 'c';

(* Write a character to stdout.а *)

function putchar(__c:integer):integer;cdecl;external 'c';

(* Faster version when locking is not necessary.а *)

function fputc_unlocked(__c:integer;__stream:pfile):integer;cdecl;external 'c';

(* These are defined in POSIX.1:1996.а *)

function putc_unlocked(__c:integer;__stream:pfile):integer;cdecl;external 'c';

function putchar_unlocked(__c:integer):integer;cdecl;external 'c';

(* Get a word (int) from STREAM.а *)

function getw(__stream:pfile):integer;cdecl;external 'c';

(* Write a word (int) to STREAM.а *)

function putw(__w:integer;__stream:pfile):integer;cdecl;external 'c';

(* Get a newline-terminated string of finite length from STREAM.а *)

function fgets(__s:pchar;__n:integer;__stream:pfile):pchar;cdecl;external 'c';

(* This function does the same as `fgets' but does not lock the stream.а *)

function fgets_unlocked(__s:pchar;__n:integer;__stream:pfile):pchar;cdecl;external 'c';

(* Get a newline-terminated string from stdin, removing the newline.

аа DO NOT USE THIS FUNCTION!!а There is no limit on how much it will read.а *)

function gets(__s:pchar):pchar;cdecl;external 'c';

(* Read up to (and including) a DELIMITER from STREAM into *LINEPTR

аа (and null-terminate it). *LINEPTR is a pointer returned from malloc (or

аа NULL), pointing to *N characters of space.а It is realloc'd as

аа necessary.а Returns the number of characters read (not including the

аа null terminator), or -1 on error or EOF.а *)

function __getdelim(__lineptr:ppchar;__n:plongint;__delimiter:integer;



ааааааааааааааа аааааа __stream:pfile):integer;cdecl;external 'c';

function getdelim(__lineptr:ppchar;__n:plongint;__delimiter:integer;

ааааааааааааааа аааааа __stream:pfile):integer;cdecl;external 'c';

(* Like `getdelim', but reads up to a newline.а *)

function getline(__lineptr:ppchar;__n:plongint;

аааааааааа аааа аааааа __stream:pfile):integer;cdecl;external 'c';

(* Write a string to STREAM.а *)

function fputs(__s:pchar;__stream:pfile):integer;cdecl;external 'c';

(* This function does the same as `fputs' but does not lock the stream.а *)

function fputs_unlocked(__s:pchar;__stream:pfile):integer;cdecl;external 'c';

(* Write a string, followed by a newline, to stdout.а *)

function puts(__s:pchar):integer;cdecl;external 'c';

(* Push a character back onto the input buffer of STREAM.а *)

function ungetc(__c:integer;__stream:pfile):integer;cdecl;external 'c';

(* Read chunks of generic data from STREAM.а *)

function fread(__ptr:pointer;__size, n:longint;

аааааааааа аааа __stream:pfile):longint;cdecl;external 'c';

аааааааааа аааа

(* Write chunks of generic data to STREAM.а *)

function fwrite(__ptr:pointer;__size, n:longint;

аааааааааа аааа __stream:pfile):longint;cdecl;external 'c';

(* Faster versions when locking is not necessary.а *)

function fread_unlocked(__ptr:pointer;__size, n:longint;

аааааааааа аааа __stream:pfile):longint;cdecl;external 'c';

function fwrite_unlocked(__ptr:pointer;__size, n:longint;

аааааааааа аааа __stream:pfile):longint;cdecl;external 'c';

(* Seek to a certain position on STREAM.а *)

function fseek(__stream:pfile;__off:longint;__whence:integer):longint;cdecl;external 'c';

(* Return the current position of STREAM.а *)

function ftell(__stream:pfile):longint;cdecl;external 'c';

(* Rewind to the beginning of STREAM.а *)

procedure rewind(__stream:pfile);cdecl;external 'c';

(* Clear the error and EOF indicators for STREAM.а *)

procedure clearerr(__stream:pfile);cdecl;external 'c';



(* Return the EOF indicator for STREAM.а *)

function feof(__stream:pfile):integer;cdecl;external 'c';

(* Return the error indicator for STREAM.а *)

function ferror(__stream:pfile):integer;cdecl;external 'c';

(* Faster versions when locking is not required.а *)

procedure clearerr_unlocked(__stream:pfile);cdecl;external 'c';

function feof_unlocked(__stream:pfile):integer;cdecl;external 'c';

function ferror_unlocked(__stream:pfile):integer;cdecl;external 'c';

(* Print a message describing the meaning of the value of errno.а *)

procedure perror(__s:pchar);cdecl;external 'c';

(* Return the system file descriptor for STREAM.а *)

function fileno(__stream:pfile):longint;cdecl;external 'c';

(* Faster version when locking is not required.а *)

function fileno_unlocked(__stream:pfile):integer;cdecl;external 'c';

(* Create a new stream connected to a pipe running the given command.а *)

function pipeopen(__command, __modes:pchar):pfile;cdecl;external 'c' name 'popen';

(* Close a stream opened by popen and return the status of its child.а *)

function pipeclose (__stream:pfile):integer;cdecl;external 'c' name 'pclose';

(* Return the name of the controlling terminal.а *)

function ctermid(__s:pchar):pchar;cdecl;external 'c';

(* Return the name of the current user.а *)

function cuserid(__s:pchar):pchar;cdecl;external 'c';

(* Acquire ownership of STREAM.а *)

procedure flockfile(__stream:pfile);cdecl;external 'c';

(* Try to acquire ownership of STREAM but do not block if it is not

аа possible.а *)

function ftrylockfile(__stream:pfile):integer;cdecl;external 'c';

(* Relinquish the ownership granted for STREAM.а *)

procedure funlockfile(__stream:pfile);cdecl;external 'c';

(* ёючфрэшх Їрщыр *)

function fdCreat(PathName:Pchar;mode:longint):longint;cdecl;external 'c' name 'creat';

(* "яхЁхьюЄър" єърчрЄхы  эр яхЁтvщ ¤ыхьхэЄ ърЄрыюур *)



procedure rewinddir(dp:pdir);cdecl;external 'c';

(* юцшфрэшх чртхЁ°хэш  яЁюЎхёёр *)

function wait(__stat_loc:pinteger):longint;cdecl;external 'c';

(* ЇєэъЎшш фы  ёючфрэш  эютюую яЁюЎхёёр ё яхЁхьхээvь ўшёыюь ярЁрьхЄЁют *)

function linuxexecl(path:pchar;arg0:pchar;args:array of const):integer;cdecl;external 'c' name 'execl';

function linuxexeclp(filename:pchar;arg0:pchar;args:array of const):integer;cdecl;external 'c' name 'execlp';

(* ЇєэъЎшш фы  юяЁхфхыхэш  ёюёЄю эш  чртхЁ°хэш  яЁюЎхёёр *)

function WEXITSTATUS(status:integer):integer;

function WSTOPSIG(status:integer):integer;

function WTERMSIG(status:integer):integer;

function WCOREDUMP(status:integer):boolean;

function WIFEXITED(status:integer):boolean;

function WIFSIGNALED(status:integer):boolean;

function WIFSTOPPED(status:integer):boolean;

(* яЁшюёЄрэртыштрхЄ Єхъє•шщ яЁюЎхёё эр чрфрээюх ъюышўхёЄтю ёхъєэф *)

function sleep(seconds:word):word;cdecl;external 'c';

(* єфры хЄ тёх ёшуэрыv шч эрсюЁр ёшуэрыюта *)

function sigemptyset(__set:psigset_t):integer;cdecl;external 'c';

(* єёЄрэртыштрхЄ тёх ёшуэрыv т эрсюЁх ёшуэрыюта *)

function sigfillset(__set:psigset_t):integer;cdecl;external 'c';

(* фюсрты хЄ ёшуэры SIGNO т эрсюЁ ёшуэрыют *)

function sigaddset(__set:psigset_t;__signo:integer):integer;cdecl;external 'c';

(* єфры хЄ ёшуэры SIGNO шч эрсюЁр ёшуэрыют *)

function sigdelset(__set:psigset_t;__signo:integer):integer;cdecl;external 'c';

(* тючтЁр•рхЄ шёЄшэє, хёыш SIGNO т эрсюЁх, ш ыюц№ т яЁюЄштэюь ёыєўрх *)

function sigismember(__set:psigset_t;__signo:integer):boolean;cdecl;external 'c';

(* тючтЁр•рхЄ шёЄшээюх чэрўхэшх, хёыш эрсюЁ ёшуэрыют эх яєёЄ *)

function sigisemptyset(__set:psigset_t):boolean;cdecl;external 'c';

(* ёючфрхЄ эютvщ эрсюЁ ёшуэрыют шч фтєї тїюфэvї ё яюью•№¦ ыюушўхёъюую "L" *)

function sigandset(__set, __left, __right:psigset_t):integer;cdecl;external 'c';

(* ёючфрхЄ эютvщ эрсюЁ ёшуэрыют шч фтєї тїюфэvї ё яюью•№¦ ыюушўхёъюую "LTL" *)



function sigorset(__set, __left, __right:psigset_t):integer;cdecl;external 'c';

(* эрсюЁ ЇєэъЎшщ фы  эхыюъры№эvї яхЁхїюфют *)

(* Store the calling environment in ENV, also saving the signal mask.

аа Return 0.а *)

function setjmp(var __env:jmp_buf):integer;cdecl;external 'c';

(* Store the calling environment in ENV, not saving the signal mask.

аа Return 0.а *)

function _setjmp(var __env:jmp_buf):integer;cdecl;external 'c';

(* Store the calling environment in ENV, also saving the

аа signal mask if SAVEMASK is nonzero.а Return 0.а *)

function sigsetjmp(var __env:jmp_buf;__savemask:longint):integer;cdecl;external 'c' name '__sigsetjmp';

(* Jump to the environment saved in ENV, making the

аа `setjmp' call there return VAL, or 1 if VAL is 0.а *)

procedure longjmp(var __env:jmp_buf;__val:integer);cdecl;external 'c';

(* Jump to the environment saved in ENV, making the

аа sigsetjmp call there return VAL, or 1 if VAL is 0.

аа Restore the signal mask if that sigsetjmp call saved it.

аа This is just an alias `longjmp'.а *)

procedure siglongjmp(var __env:jmp_buf;__val:integer);cdecl;external 'c';

(* fpathconf, pathconf - get configuration values for files *)

function fpathconf(filedes,name:longint):longint;cdecl;external 'c';

function pathconf(path:pchar;name:longint):longint;cdecl;external 'c';

function sysconf(name:integer):longint;cdecl;external 'c';

(* setpgid, getpgid, setpgrp, getpgrp - set/get process group *)

function setpgid(pid, pgid:longint):longint;cdecl;external 'c';

function getpgid(pid:longint):longint;cdecl;external 'c';

function setpgrp:longint;cdecl;external 'c';

function getpgrp:longint;cdecl;external 'c';

function getsid(pid:longint):longint;cdecl;external 'c';

function setsid:longint;cdecl;external 'c';

(* grantpt - grant access to the slave pseudotty*)

function grantpt(fd:longint):longint;cdecl;external 'c';

(* unlockpt - unlock a pseudotty master/slave pair*)



function unlockpt(fd:longint):longint;cdecl;external 'c';

(* ptsname - get the name of the slave pseudotty*)

function ptsname(fd:longint):pchar;cdecl;external 'c';

(* inet_aton() converts the Internet host addressа cpа fromа theа standardааааааааааааааааааааааааааа

аа numbers-and-dotsа notation into binary data and stores it in the structure

аа that inp points to. inet_aton returns nonzero ifа theа addressа isааааааааааааааааааааааааааааааааааааааааа

аа valid, zero if not.*)

function inet_aton(cp:pchar; inp:pin_addr):longint;cdecl;external 'c';

(* Theа inet_addr()а functionа convertsа the Internet host address cp fromааааааа

аа numbers-and-dots notation into binary data in network byteа order.аа Ifаааааааааааааа

аа the input is invalid, INADDR_NONE (usually -1) is returned.а This is anааааааааааааааааааааа

аа obsolete interface to inet_aton, describedа immediatelyа above;а itа isаааааааааааааааааааааааааааа

аа obsolete because -1 is a valid address (255.255.255.255), and inet_atonааааааааааааааааааааааааааааааааааа

аа provides a cleaner way to indicate error return. *)

function inet_addr(cp:pchar):in_addr_t;cdecl;external 'c';

(* The inet_network() function extracts the network numberа inа hostа byte

аа orderа fromа the address cp in numbers-and-dots notation.а If the input

аа is invalid, -1 is returned. *)

function inet_network(cp:pchar):in_addr_t;cdecl;external 'c';

(* The inet_ntoa() function converts the Internet host address in given in

аа networkа byteа order to a string in standard numbers-and-dots notation.

аа The string is returned in a statically allocated buffer,а whichа subse-

аа quent calls will overwrite. *)

function inet_ntoa(n:in_addr):pchar;cdecl;external 'c';

(* Theа inet_makeaddr() function makes an Internet host address in network

аа byte order by combining the network number net with theа localа address

аа host in network net, both in local host byte order. *)

function inet_makeaddr(net, host:longint):in_addr;cdecl;external 'c';



(* Theа inet_lnaof()а functionа returns the local host address part of the

аа Internet address in.а The local host address is returned in localа host

аа byte order. *)

function inet_lnaof(n:in_addr):in_addr_t;cdecl;external 'c';

аааааа

(* The inet_netof() function returns the network number part of the Inter-

аа net Address in.а The network number isа returnedа inа localа hostа byte

аа order. *)

function inet_netof(n:in_addr):in_addr_t;cdecl;external 'c';

ааааааааааа

(* sendto - юЄяЁртшЄ№ ёююс•хэшх т ёюъхЄ.

аа send, sendto, ш sendmsg шёяюы№чє¦Єё  фы  яхЁхёvыъш ёююс•хэшщ эра фЁєующ

аа ёюъхЄ.ааа sendа ьюцэюа шёяюы№чютрЄ№,а Єюы№ъюа хёыша ёюъхЄа эрїюфшЄё а т

аа ёюхфшэхээюь ёюёЄю эшш, Єюуфр ъръ sendto ш sendmsg ьюцэю шёяюы№чютрЄ№а т

аа ы¦сюх тЁхь . *)

function sendto(sock:longint;var addr;addrlen,flags:longint;

ааааааааааааааа var sato:tsockaddr;tolen:longint):longint;cdecl;external 'c';

ааааааааааааааа

(* recvfrom шёяюы№чєхЄё  фы  яюыєўхэш  ёююс•хэшщ шч ёюъхЄр, ш ьюцхЄ

аа шёяюы№чютрЄ№ё  фы  яюыєўхэш  фрээvї, эхчртшёшью юЄ Єюую,  ты хЄё  ыш

аа ёюъхЄ юЁшхэЄшЁютрээvь эр ёюхфшэхэш  шыш эхЄ. *)

function recvfrom(sock:longint;var addr;addrlen,flags:longint;

ааааааааааааааааа var from:tsockaddr;var fromlen:longint):longint;cdecl;external 'c';

(* character classification routines *)

function isalnum(c:integer):boolean;cdecl;external 'c';

function isalpha(c:integer):boolean;cdecl;external 'c';

function isascii(c:integer):boolean;cdecl;external 'c';

function isblank(c:integer):boolean;cdecl;external 'c';

function iscntrl(c:integer):boolean;cdecl;external 'c';

function isdigit(c:integer):boolean;cdecl;external 'c';

function isgraph(c:integer):boolean;cdecl;external 'c';

function islower(c:integer):boolean;cdecl;external 'c';

function isprint(c:integer):boolean;cdecl;external 'c';

function ispunct(c:integer):boolean;cdecl;external 'c';

function isupper(c:integer):boolean;cdecl;external 'c';

function isxdigit(c:integer):boolean;cdecl;external 'c';



function isspace(c:integer):boolean;

function tolower(c:integer):integer;cdecl;external 'c';

function _tolower(c:integer):integer;cdecl;external 'c';

function toupper(c:integer):integer;cdecl;external 'c';

function _toupper(c:integer):integer;cdecl;external 'c';

function toascii(c:integer):integer;cdecl;external 'c';

(* transform date and time to broken-down time or ASCII *)

(* The ctime(), gmtime() and localtime() functions all take an argument of

аа dataа typeа time_t which represents calendar time.а When interpreted as

аа an absolute time value, it represents theа numberа ofа secondsа elapsed

аа since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC). *)

function ctime(var t:longint):pchar;cdecl;external 'c';

function ctime_r(var timep:longint;buf:pchar):pchar;cdecl;external 'c';

function gmtime(var timep:longint):ptm;cdecl;external 'c';

function gmtime_r(var timep:longint; result:ptm):ptm;cdecl;external 'c';

function localtime(var timep:longint):ptm;cdecl;external 'c';

function localtime_r(var timep:longint; result:ptm):ptm;cdecl;external 'c';

(* The asctime() and mktime() functions both take an argument representing

аа broken-down time which is a representation separated into year,а month,

аа day, etc. *)

function asctime(const t:ptm):pchar;cdecl;external 'c';

function asctime_r(const t:ptm;buf:pchar):pchar;cdecl;external 'c';

function mktime(t:ptm):longint;cdecl;external 'c';

function difftime(time1, time2:longint):double;cdecl;external 'c';

(* ЇєэъЎшш фы  ЁрсюЄv ё ярь Є№¦ *)

function calloc(nmemb, size:longint):pointer;cdecl;external 'c';

function malloc(size:longint):pointer;cdecl;external 'c';

procedure free(ptr:pointer);cdecl;external 'c';

function realloc(ptr:pointer; size:longint):pointer;cdecl;external 'c';

а

(* ЇєэъЎшш фы  ЁрсюЄv ё ърЄрыюурьш *)

function mkdir(pathname:pchar;mode:integer):integer;cdecl;external 'c';

function rmdir(pathname:pchar):integer;cdecl;external 'c';

function chdir(path:pchar):integer;cdecl;external 'c';



function getcwd(name:pchar; size:longint):pchar;cdecl;external 'c';

(* ЁрсюЄр ё сєЇхЁрьш *)

procedure sync;cdecl;external 'c';

function fsync(filedes:integer):integer;cdecl;external 'c';

(* тvяюыэхэшх ъюьрэфv shell'р *)

function runshell(__commandline:pchar):longint;cdecl;external 'c' name 'system';

(* єэшўЄюцхэшх тvчтрт°хую яЁюЎхёёр *)

procedure _exit(status:longint);cdecl;external 'c';

(* шчьхэхэшх шыш Ёрё°шЁхэшх юъЁєцхэш  *)

function putenv(str:pchar):longint;cdecl;external 'c';

(* ёьхэр ъюЁэхтюую ърЄрыюур *)

function chroot(path:pchar):longint;cdecl;external 'c';

(* єёЄрэютър Ёхры№эvї ш фхщёЄтє¦•шї шфхэЄшЇшърЄюЁют яюы№чютрЄхы  ш уЁєяяv *)

function setuid(uid:longint):longint;cdecl;external 'c';

function setgid(gid:longint):longint;cdecl;external 'c';

(* єчэрЄ№ шыш шчьхэшЄ№ юуЁрэшўхэш  яЁюЎхёёр *)

function ulimit(cmd:longint;args:array of const):longint;cdecl;external 'c';

(* ртрЁшщэюх чртхЁ°хэшх яЁюЎхёёр *)

function abort:longint;cdecl;external 'c';

(* ¦юыєўшЄ№ ёъюЁюёЄ№ ттюфр *)

function cfgetispeed(var tdes:TermIOS):longint;cdecl;external 'c';

(* ¦юыєўшЄ№ ёъюЁюёЄ№ тvтюфр *)

function cfgetospeed(var tdes:TermIOS):longint;cdecl;external 'c';

(* шёяюы№чєхЄё  фы  яюёvыъш ёшуэрыр яЁхЁvтрэш  ёхрэёр ёт чш *)

Function tcsendbrk(ttyfd, duration:longint):longint;cdecl;external 'c';

(* ЇєэъЎшш фы  ЁрсюЄv ё ярь Є№¦ *)

function memset(buf:pointer; character:longint; size:longint):pointer;cdecl;external 'c';

function memcpy(buf1:pointer; const buf2:pointer; size:longint):pointer;cdecl;external 'c';

function memmove(buf1:pointer; const buf2:pointer; size:longint):pointer;cdecl;external 'c';

function memcmp(const buf1, buf2:pointer; size:longint):longint;cdecl;external 'c';

function memchr(const buf:pointer; character:longint; size:longint):pointer;cdecl;external 'c';

(* ЇєэъЎшш фы  ЁрсюЄv ёю ёЄЁюърьш *)

function strpbrk(const s1, s2:pchar):pchar;cdecl;external 'c';



function strspn(const s1, s2:pchar):longint;cdecl;external 'c';

function strcspn(const s1, s2:pchar):longint;cdecl;external 'c';

function strtok(s1:pchar; const s2:pchar):pchar;cdecl;external 'c';

function strtol(const str:pchar; endptr:ppchar; base:longint):longint;cdecl;external 'c';

function atoi(const str:pchar):longint;cdecl;external 'c';

function atol(const str:pchar):longint;cdecl;external 'c';

function strtod(const str:pchar; endptr:ppchar):double;cdecl;external 'c';

function atof(const str:pchar):double;cdecl;external 'c';

ааааааааааааааааааааааааааааааааааа

implementation

(* If WIFEXITED(STATUS), the low-order 8 bits of the status.а *)

function WEXITSTATUS(status:integer):integer;

begin

а WEXITSTATUS:=(status and $ff00) shr 8;

end;

(* If WIFSTOPPED(STATUS), the signal that stopped the child.а *)

function WSTOPSIG(status:integer):integer;

begin

а WSTOPSIG:=WEXITSTATUS(status);

end;

(* If WIFSIGNALED(STATUS), the terminating signal.а *)

function WTERMSIG(status:integer):integer;

begin

а WTERMSIG:=status and $7f;

end;

(* Nonzero if STATUS indicates the child dumped core.а *)

function WCOREDUMP(status:integer):boolean;

begin

а WCOREDUMP:=(status and WCOREFLAG) = 0;

end;

(* Nonzero if STATUS indicates normal termination.а *)

function WIFEXITED(status:integer):boolean;

begin

а WIFEXITED:=(status and $7f) = 0;

end;

(* If WIFSIGNALED(STATUS), the terminating signal.а *)

function WIFSIGNALED(status:integer):boolean;

begin

а WIFSIGNALED:=(not WIFSTOPPED(status)) and (not WIFEXITED(status));

end;

(* Nonzero if STATUS indicates the child is stopped.а *)

function WIFSTOPPED(status:integer):boolean;

begin

а WIFSTOPPED:=(status and $ff) = $7f;

end;

(* test on space character: space, CR, LF, Tab, VTab, FF *)

function isspace(c:integer):boolean;

begin

а isspace:=c in [$9,$20,$a,$b,$c,$d];

end;

begin

а stdin:=fdopen(0,'r');

а stdout:=fdopen(1,'w');

end.


Когда работа над книгой подходила


Когда работа над книгой подходила к концу, была выпущена новая версия компилятора Free Pascal, в котором де-юре зафиксирована возможность использования интерфейса системных вызовов не только в ОС семейства Linux/BSD, но и других клонов UNIX. Для того, чтобы скомпилировать компилятором версии 2.0 программы, представленные в книге, следует заменить в них подключаемый модуль linux на oldlinux. Платформенно-независимая функциональность модуля linux разделена между unix, unixtype, baseunix и unixutil. В модуль X86 вынесены части, специфичные для X86-архитектуры.

Для удобства перехода на новую версию компилятора приводим таблицу соответствия модулей в разных версиях компилятора.

а) константы:


Название (1.x)


Описание


Название (2.x)


Модуль



Maximum number of arguments to a program.


ARG_MAX


unix, baseunix, unixtype



Number of bits in a word.


BITSINWORD


baseunix


CLONE_FILES


Clone option: open files shared between processes


CLONE_FILES


linux


CLONE_FS


Clone option: fs info shared between processes


CLONE_FS


linux


CLONE_PID


Clone option: PID shared between processes


CLONE_PID


linux


CLONE_SIGHAND


Clone option: signal handlers shared between processes


CLONE_SIGHAND


linux


CLONE_VM


Clone option: VM shared between processes


CLONE_VM


linux


CSIGNAL


Clone option: Signal mask to be sent at exit


CSIGNAL


linux



System error: Argument list too long


ESysE2BIG


baseunix



System error: Permission denied


ESysEACCES


baseunix



System error: Address already in use


ESysEADDRINUSE


baseunix



System error: Cannot assign requested address


ESysEADDRNOTAVAIL


baseunix



System error: Advertise error


ESysEADV


baseunix



System error: Address family not supported by protocol


ESysEAFNOSUPPORT


baseunix



System error: Try again


ESysEAGAIN


baseunix



System error: Operation already in progress


ESysEALREADY


baseunix



System error: Invalid exchange


ESysEBADE


baseunix



System error: Bad file number


ESysEBADF


baseunix



System error: File descriptor in bad state


ESysEBADFD


baseunix



System error: Not a data message


ESysEBADMSG


baseunix



System error: Invalid request descriptor


ESysEBADR


baseunix



System error: Invalid request code


ESysEBADRQC


baseunix



System error: Invalid slot


ESysEBADSLT


baseunix



System error: Bad font file format


ESysEBFONT


baseunix



System error: Device or resource busy


ESysEBUSY


baseunix



System error: No child processes


ESysECHILD


baseunix



System error: Channel number out of range


ESysECHRNG


baseunix



System error: Communication error on send


ESysECOMM


baseunix



System error: Software caused connection abort


ESysECONNABORTED


baseunix



System error: Connection refused


ESysECONNREFUSED


baseunix



System error: Connection reset by peer


ESysECONNRESET


baseunix



System error: Resource deadlock would occur


ESysEDEADLK


baseunix



System error: File locking deadlock error


ESysEDEADLOCK


baseunix



System error: Destination address required


ESysEDESTADDRREQ


baseunix



System error: Math argument out of domain of func


ESysEDOM


baseunix



System error: RFS specific error


ESysEDOTDOT


baseunix



System error: Quota exceeded


ESysEDQUOT


baseunix



System error: File exists


ESysEEXIST


baseunix



System error: Bad address


ESysEFAULT


baseunix



System error: File too large


ESysEFBIG


baseunix



System error: Host is down


ESysEHOSTDOWN


baseunix



System error: No route to host


ESysEHOSTUNREACH


baseunix



System error: Identifier removed


ESysEIDRM


baseunix



System error: Illegal byte sequence


ESysEILSEQ


baseunix



System error: Operation now in progress


ESysEINPROGRESS


baseunix



System error: Interrupted system call


ESysEINTR


baseunix



System error: Invalid argument


ESysEINVAL


baseunix



System error: I/O error


ESysEIO


baseunix



System error: Transport endpoint is already connected


ESysEISCONN


baseunix



System error: Is a directory


ESysEISDIR


baseunix



System error: Is a named type file


ESysEISNAM


baseunix



System error: Level 2 halted


ESysEL2HLT


baseunix



System error: Level 2 not synchronized


ESysEL2NSYNC


baseunix



System error: Level 3 halted


ESysEL3HLT


baseunix



System error: Level 3 reset


ESysEL3RST


baseunix



System error: Can not access a needed shared library


ESysELIBACC


baseunix



System error: Accessing a corrupted shared library


ESysELIBBAD


baseunix



System error: Cannot exec a shared library directly


ESysELIBEXEC


baseunix



System error: Attempting to link in too many shared libraries


ESysELIBMAX


baseunix



System error: .lib section in a.out corrupted


ESysELIBSCN


baseunix



System error: Link number out of range


ESysELNRNG


baseunix



System error: Too many symbolic links encountered


ESysELOOP


baseunix



System error: Too many open files


ESysEMFILE


baseunix



System error: Too many links


ESysEMLINK


baseunix



System error: Message too long


ESysEMSGSIZE


baseunix



System error: Multihop attempted


ESysEMULTIHOP


baseunix



System error: File name too long


ESysENAMETOOLONG


baseunix



System error: No XENIX semaphores available


ESysENAVAIL


baseunix



System error: Network is down


ESysENETDOWN


baseunix



System error: Network dropped connection because of reset


ESysENETRESET


baseunix



System error: Network is unreachable


ESysENETUNREACH


baseunix



System error: File table overflow


ESysENFILE


baseunix



System error: No anode


ESysENOANO


baseunix



System error: No buffer space available


ESysENOBUFS


baseunix



System error: No CSI structure available


ESysENOCSI


baseunix



System error: No data available


ESysENODATA


baseunix



System error: No such device


ESysENODEV


baseunix



System error: No such file or directory


ESysENOENT


baseunix



System error: Exec format error


ESysENOEXEC


baseunix



System error: No record locks available


ESysENOLCK


baseunix



System error: Link has been severed


ESysENOLINK


baseunix



System error: Out of memory


ESysENOMEM


baseunix



System error: No message of desired type


ESysENOMSG


baseunix



System error: Machine is not on the network


ESysENONET


baseunix



System error: Package not installed


ESysENOPKG


baseunix



System error: Protocol not available


ESysENOPROTOOPT


baseunix



System error: No space left on device


ESysENOSPC


baseunix



System error: Out of streams resources


ESysENOSR


baseunix



System error: Device not a stream


ESysENOSTR


baseunix



System error: Function not implemented


ESysENOSYS


baseunix



System error: Block device required


ESysENOTBLK


baseunix



System error: Transport endpoint is not connected


ESysENOTCONN


baseunix



System error: Not a directory


ESysENOTDIR


baseunix



System error: Directory not empty


ESysENOTEMPTY


baseunix



System error: Not a XENIX named type file


ESysENOTNAM




System error: Socket operation on non-socket


ESysENOTSOCK


baseunix



System error: Not a typewriter


ESysENOTTY


baseunix



System error: Name not unique on network


ESysENOTUNIQ


baseunix



System error: No such device or address


ESysENXIO


baseunix



System error: Operation not supported on transport endpoint



ESysEOPNOTSUPP



System error: Value too large for defined data type


ESysEOVERFLOW


baseunix



System error: Operation not permitted.


ESysEPERM


baseunix



System error: Protocol family not supported


ESysEPFNOSUPPORT


baseunix



System error: Broken pipe


ESysEPIPE


baseunix



System error: Protocol error


ESysEPROTO


baseunix



System error: Protocol not supported


ESysEPROTONOSUPPORT


baseunix



System error: Protocol wrong type for socket


ESysEPROTOTYPE


baseunix



System error: Math result not representable


ESysERANGE


baseunix



System error: Remote address changed


ESysEREMCHG


baseunix



System error: Object is remote


ESysEREMOTE


baseunix



System error: Remote I/O error


ESysEREMOTEIO


baseunix



System error: Interrupted system call should be restarted


ESysERESTART


baseunix



System error: Read-only file system


ESysEROFS


baseunix



System error: Cannot send after transport endpoint shutdown


ESysESHUTDOWN


baseunix



System error: Socket type not supported


ESysESOCKTNOSUPPORT


baseunix



System error: Illegal seek


ESysESPIPE


baseunix



System error: No such process


ESysESRCH


baseunix



System error: Srmount error


ESysESRMNT


baseunix



System error: Stale NFS file handle


ESysESTALE


baseunix



System error: Streams pipe error


ESysESTRPIPE


baseunix



System error: Timer expired


ESysETIME


baseunix



System error: Connection timed out


ESysETIMEDOUT


baseunix



System error: Too many references: cannot splice


ESysETOOMANYREFS


baseunix



System error: Text (code segment) file busy


ESysETXTBSY


baseunix



System error: Structure needs cleaning


ESysEUCLEAN


baseunix



System error: Protocol driver not attached


ESysEUNATCH


baseunix



System error: Too many users


ESysEUSERS


baseunix



System error: Operation would block


ESysEWOULDBLOCK


baseunix



System error: Cross-device link


ESysEXDEV


baseunix



System error: Exchange full


ESysEXFULL


baseunix



Maximum elements in a TFDSet array.


FD_MAXFDSET


baseunix


fs_ext


File system type (FSStat): (ext) Extended


fs_ext


unix


fs_ext2


File system type (FSStat): (ext2) Second extended


fs_ext2


unix


fs_iso


File system type (FSStat): ISO 9660


fs_iso


unix


fs_minix


File system type (FSStat): Minix


fs_minix


unix


fs_minix_30


File system type (FSStat): Minix 3.0


fs_minix_30


unix


fs_minux_V2


File system type (FSStat): Minix V2


fs_minux_V2


unix


fs_msdos


File system type (FSStat): MSDOS (FAT)


fs_msdos


unix


fs_nfs


File system type (FSStat): NFS


fs_nfs


unix


fs_old_ext2


File system type (FSStat): (ext2) Old second extended


fs_old_ext2


unix


fs_proc


File system type (FSStat): PROC fs


fs_proc


unix


fs_xia


File system type (FSStat): XIA


fs_xia


unix


F_GetFd


fpFCntl command: Get close-on-exec flag


F_GetFd


baseunix


F_GetFl


fpFCntl command: Get filedescriptor flags


F_GetFl


baseunix


F_GetLk


fpFCntl command: Get lock


F_GetLk


baseunix


F_GetOwn


fpFCntl command: get owner of filedescriptor events


F_GetOwn


baseunix


F_OK


fpAccess call test: file exists.


F_OK


baseunix


F_SetFd


fpFCntl command: Set close-on-exec flag


F_SetFd


baseunix


F_SetFl


fpFCntl command: Set filedescriptor flags


F_SetFl


baseunix


F_SetLk


fpFCntl command: Set lock


F_SetLk


baseunix


F_SetLkW


fpFCntl command: Test lock


F_SetLkW


baseunix


F_SetOwn


FCntl command: Set owner of filedescriptor events F_SetOwn


baseunix



IOCtl_TCGETS


IOCTL call number: get Terminal Control settings


IOCtl_TCGETS


unix



Last bit in word.


ln2bitmask


baseunix



Power of 2 number of bits in word.


ln2bitsinword


baseunix


LOCK_EX


FLock Exclusive lock


LOCK_EX


unix


LOCK_NB


FLock Non-blocking operation


LOCK_NB


unix


LOCK_SH


FLock Shared lock


LOCK_SH


unix


LOCK_UN


FLock unlock


LOCK_UN


unix


MAP_ANONYMOUS


fpMMap map type: Don't use a file


MAP_ANONYMOUS


baseunix


MAP_DENYWRITE


MMap option: Ignored.


MAP_DENYWRITE


unix


MAP_EXECUTABLE


MMap option: Ignored.


MAP_EXECUTABLE


unix


MAP_FIXED


MMap map type: Interpret addr exactly


MAP_FIXED


unix


MAP_GROWSDOWN


MMap option: Memory grows downward (like a stack)


MAP_GROWSDOWN


unix


MAP_LOCKED


MMap option: lock the pages in memory.


MAP_LOCKED


unix


MAP_NORESERVE


MMap option: Do not reserve swap pages for this memory.


MAP_NORESERVE


unix


MAP_PRIVATE


MMap map type: Changes are private


MAP_PRIVATE


unix, baseunix


MAP_SHARED


MMap map type: Share changes


MAP_SHARED


unix


MAP_TYPE


MMap map type: Bitmask for type of mapping


MAP_TYPE


unix


MINSIGSTKSZ


NCC: Number of control characters in termio record.





Maximum filename length.


NAME_MAX


unix, unixtype, baseunix


NCCS


Number of control characters in termios record.





fpOpen file open mode: Append to file


O_APPEND


baseunix



fpOpen file open mode: Create if file does not yet exist.


O_CREAT


baseunix



fpOpen file open mode: Minimize caching effects


O_DIRECT


baseunix



fpOpen file open mode: File must be directory.


O_DIRECTORY


baseunix



fpOpen file open mode: Open exclusively


O_EXCL


baseunix



fpOpen file open mode: Open for 64-bit I/O


O_LARGEFILE


baseunix



fpOpen file open mode: Alias for O_NonBlock


O_NDELAY


baseunix



fpOpen file open mode: No TTY control.


O_NOCTTY


baseunix



fpOpen file open mode: Fail if file is symbolic link.


O_NOFOLLOW


baseunix



fpOpen file open mode: Open in non-blocking mode


O_NONBLOCK


baseunix



fpOpen file open mode: Read only


O_RDONLY


baseunix



fpOpen file open mode: Read/Write


O_RDWR


baseunix



fpOpen file open mode: Write to disc at once


O_SYNC


baseunix



fpOpen file open mode: Truncate file to length 0


O_TRUNC


baseunix



fpOpen file open mode: Write only


O_WRONLY


baseunix


Open_Accmode


Bitmask to determine access mode in open flags.


Open_Accmode


unix


Open_Append


File open mode: Append to file


Open_Append


unix


Open_Creat


File open mode: Create if file does not yet exist.


Open_Creat


unix


Open_Direct


File open mode: Minimize caching effects


Open_Direct


unix


Open_Directory


File open mode: File must be directory.


Open_Directory


unix


Open_Excl


File open mode: Open exclusively


Open_Excl


unix


Open_LargeFile


File open mode: Open for 64-bit I/O


Open_LargeFile


unix


Open_NDelay


File open mode: Alias for Open_NonBlock


Open_NDelay


unix


Open_NoCtty


File open mode: No TTY control.


Open_NoCtty


unix


Open_NoFollow


File open mode: Fail if file is symbolic link.


Open_NoFollow


unix


Open_NonBlock


File open mode: Open in non-blocking mode


Open_NonBlock


unix


Open_RdOnly


File open mode: Read only


Open_RdOnly


unix


Open_RdWr


File open mode: Read/Write


Open_RdWr


unix


Open_Sync


File open mode: Write to disc at once


Open_Sync


unix


Open_Trunc


File open mode: Truncate file to length 0


Open_Trunc


unix


Open_WrOnly


File open mode: Write only


Open_WrOnly


unix



Maximum pathname length.


PATH_MAX


unix, unixtype, baseunix


Prio_PGrp


Get/set process group priority


Prio_PGrp


unixtype


Prio_Process


Get/Set process priority


Prio_Process


unixtype


Prio_User


Get/set user priority


Prio_User


unixtype


PROT_EXEC


FpMMap memory access: page can be executed


PROT_EXEC


unix


PROT_NONE


FpMMap memory access: page can not be accessed


PROT_NONE


unix


PROT_READ


FpMMap memory access: page can be read


PROT_READ


unix


PROT_WRITE


FpMMap memory access: page can be written


PROT_WRITE


unix


P_IN


Input file descriptor of pipe pair.


P_IN


unix


P_OUT


Output file descriptor of pipe pair.


P_OUT


unix


R_OK


fpAccess call test: read allowed


R_OK


baseunix


SA_INTERRUPT


Sigaction options: ?


SA_INTERRUPT


baseunix


SA_NOCLDSTOP


Sigaction options: Do not receive notification when child processes stop


SA_NOCLDSTOP


baseunix



Sigaction options: ?


SA_NOCLDWAIT


baseunix


SA_NOMASK


Sigaction options: Do not prevent the signal from being received when it is handled.


SA_NOMASK


baseunix


SA_ONESHOT


Sigaction options: Restore the signal action to the default state.


SA_ONESHOT


baseunix


SA_ONSTACK


Socket option




SA_RESTART


Sigaction options: Provide behaviour compatible with BSD signal semantics


SA_RESTART


baseunix


SA_SHIRQ


Sigaction options: ?


SA_SHIRQ


baseunix



Sigaction options: The signal handler takes 3 arguments, not one.


SA_SIGINFO


baseunix


SA_STACK


Sigaction options: Call the signal handler on an alternate signal stack.


SA_STACK


baseunix


Seek_Cur


fpLSeek option: Set position relative to current position.


Seek_Cur


baseunix


Seek_End


fpLSeek option: Set position relative to end of file.


Seek_End


baseunix


Seek_Set


fpLSeek option: Set absolute position.


Seek_Set


baseunix


SIGABRT


Signal: ABRT (Abort)


SIGABRT


baseunix


SIGALRM


Signal: ALRM (Alarm clock)


SIGALRM


baseunix


SIGBUS


Signal: BUS (bus error)


SIGBUS


baseunix


SIGCHLD


Signal: CHLD (child status changed)


SIGCHLD


baseunix


SIGCONT


Signal: CONT (Continue)


SIGCONT


baseunix


SIGFPE


Signal: FPE (Floating point error)


SIGFPE


baseunix


SIGHUP


Signal: HUP (Hangup)


SIGHUP


baseunix


SIGILL


Signal: ILL (Illegal instruction)


SIGILL


baseunix


SIGINT


Signal: INT (Interrupt)


SIGINT


baseunix


SIGIO


Signal: IO (I/O operation possible)


SIGIO


baseunix


SIGIOT


Signal: IOT (IOT trap)


SIGIOT


baseunix


SIGKILL


Signal: KILL (unblockable)


SIGKILL


baseunix


SIGPIPE


Signal: PIPE (Broken pipe


SIGPIPE


baseunix


SIGPOLL


Signal: POLL (Pollable event)


SIGPOLL


baseunix


SIGPROF


Signal: PROF (Profiling alarm)


SIGPROF


baseunix


SIGPWR


Signal: PWR (power failure restart)


SIGPWR


baseunix


SIGQUIT


Signal: QUIT


SIGQUIT


baseunix


SIGSEGV


Signal: SEGV (Segmentation violation)


SIGSEGV


baseunix


SIGSTKFLT


Signal: STKFLT (Stack Fault)


SIGSTKFLT


baseunix


SIGSTKSZ


Signal Stack size error


SIGSTKSZ


baseunix


SIGSTOP


Signal: STOP (Stop, unblockable)


SIGSTOP


baseunix


SIGTerm


Signal: TERM (Terminate)


SIGTerm


baseunix


SIGTRAP


Signal: TRAP (Trace trap)


SIGTRAP


baseunix


SIGTSTP


Signal: TSTP (keyboard stop)


SIGTSTP


baseunix


SIGTTIN


Signal: TTIN (Terminal input, background)


SIGTTIN


baseunix


SIGTTOU


Signal: TTOU (Terminal output, background)


SIGTTOU


baseunix


SIGUNUSED


Signal: Unused


SIGUNUSED


baseunix


SIGURG


Signal: URG (Socket urgent condition)


SIGURG


baseunix


SIGUSR1


Signal: USR1 (User-defined signal 1)


SIGUSR1


baseunix


SIGUSR2


Signal: USR2 (User-defined signal 2)


SIGUSR2


baseunix


SIGVTALRM


Signal: VTALRM (Virtual alarm clock)


SIGVTALRM


baseunix


SIGWINCH


Signal: WINCH (Window/Terminal size change)


SIGWINCH


baseunix


SIGXCPU


Signal: XCPU (CPU limit exceeded)


SIGXCPU


baseunix


SIGXFSZ


Signal: XFSZ (File size limit exceeded)


SIGXFSZ


baseunix


SIG_BLOCK


Sigprocmask flags: Add signals to the set of blocked signals.


SIG_BLOCK


baseunix


SIG_DFL


Signal handler: Default signal handler


SIG_DFL


baseunix


SIG_ERR


Signal handler: error


SIG_ERR


baseunix


SIG_IGN


Signal handler: Ignore signal


SIG_IGN


baseunix



Maximum system signal number.


SIG_MAXSIG


unix, unixtype, baseunix


SIG_SETMASK


Sigprocmask flags: Set of blocked signals is given.


SIG_SETMASK


baseunix


SIG_UNBLOCK


Sigprocmask flags: Remove signals from the set set of blocked signals.


SIG_UNBLOCK


baseunix


SI_PAD_SIZE


Signal information record pad bytes size. Do not use.


SI_PAD_SIZE


baseunix


SS_DISABLE


Socket options




SS_ONSTACK


Socket options




STAT_IFBLK


File (stat record) mode: Block device


STAT_IFBLK


unix


STAT_IFCHR


File (stat record) mode: Character device


STAT_IFCHR


unix


STAT_IFDIR


File (stat record) mode: Directory


STAT_IFDIR


unix


STAT_IFIFO


File (stat record) mode: FIFO


STAT_IFIFO


unix


STAT_IFLNK


File (stat record) mode: Link


STAT_IFLNK


unix


STAT_IFMT


File (stat record) mode: File type bit mask


STAT_IFMT


unix


STAT_IFREG


File (stat record) mode: Regular file


STAT_IFREG


unix


STAT_IFSOCK


File (stat record) mode: Socket


STAT_IFSOCK


unix


STAT_IRGRP


File (stat record) mode: Group read permission


STAT_IRGRP


unix


STAT_IROTH


File (stat record) mode: Other read permission


STAT_IROTH


unix


STAT_IRUSR


File (stat record) mode: Owner read permission


STAT_IRUSR


unix


STAT_IRWXG


File (stat record) mode: Group permission bits mask


STAT_IRWXG


unix


STAT_IRWXO


File (stat record) mode: Other permission bits mask


STAT_IRWXO


unix


STAT_IRWXU


File (stat record) mode: Owner permission bits mask


STAT_IRWXU


unix


STAT_ISGID


File (stat record) mode: GID bit set


STAT_ISGID


unix


STAT_ISUID


File (stat record) mode: UID bit set


STAT_ISUID


unix


STAT_ISVTX


File (stat record) mode: Sticky bit set


STAT_ISVTX


unix


STAT_IWGRP


File (stat record) mode: Group write permission


STAT_IWGRP


unix


STAT_IWOTH


File (stat record) mode: Other write permission


STAT_IWOTH


unix


STAT_IWUSR


File (stat record) mode: Owner write permission


STAT_IWUSR


unix


STAT_IXGRP


File (stat record) mode: Others execute permission


STAT_IXGRP


unix


STAT_IXOTH


File (stat record) mode: Others execute permission


STAT_IXOTH


unix


STAT_IXUSR


File (stat record) mode: Others execute permission


STAT_IXUSR


unix



Max system name length.


SYS_NMLN


unix, unixtype, baseunix



File mode: Block device


S_IFBLK


baseunix



File mode: Character device


S_IFCHR


baseunix



File mode: Directory


S_IFDIR


baseunix



File mode: FIFO


S_IFIFO


baseunix



File mode: Link


S_IFLNK


baseunix



File mode: File type bit mask


S_IFMT


baseunix



File mode: Regular file


S_IFREG


baseunix



File mode: Socket


S_IFSOCK


baseunix



Mode flag: Read by group.


S_IRGRP


baseunix



Mode flag: Read by others.


S_IROTH


baseunix



Mode flag: Read by owner.


S_IRUSR


baseunix



Mode flag: Write by group.


S_IWGRP


baseunix



Mode flag: Write by others.


S_IWOTH


baseunix



Mode flag: Write by owner.


S_IWUSR


baseunix



Mode flag: Execute by group.


S_IXGRP


baseunix



Mode flag: Execute by others.


S_IXOTH


baseunix



Mode flag: Execute by owner.


S_IXUSR


baseunix



Max length of utsname domain name.


UTSNAME_DOMAIN_LENGTH


baseunix



Max length of utsname system name, release, version, machine.


UTSNAME_LENGTH


baseunix



Max length of utsname node name.


UTSNAME_NODENAME_LENGTH


baseunix


Wait_Any


WaitPID: Wait on any process


Wait_Any


unix


Wait_Clone


WaitPID: Wait on clone processes only.


Wait_MyPGRP


unix


Wait_MyPGRP


WaitPID: Wait processes from current process group


Wait_MyPGRP


unix


Wait_NoHang


WaitPID: Do not wait


Wait_NoHang


unix


Wait_UnTraced


WaitPID: Also report stopped but untraced processes


Wait_UnTraced


unix


WNOHANG


Waitpid option: Do not wait for processes to terminate.


WNOHANG


baseunix



Number of words in a TFDSet array


wordsinfdset


baseunix



Number of words in a signal set.


wordsinsigset


baseunix


WUNTRACED


Waitpid option: Also report children wich were stopped but not yet reported


WUNTRACED


baseunix


W_OK


fpAccess call test: write allowed


W_OK


baseunix


X_OK


fpAccess call test: execute allowed


X_OK


baseunix



Mutex options:


_PTHREAD_MUTEX_ADAPTIVE_NP


unixtype



Mutex options:


_PTHREAD_MUTEX_DEFAULT


unixtype



Mutex options:


_PTHREAD_MUTEX_ERRORCHECK


unixtype



Mutex options: double lock returns an error code.


_PTHREAD_MUTEX_ERRORCHECK_NP


unixtype



Mutex options: Fast mutex


_PTHREAD_MUTEX_FAST_NP


unixtype



Mutex options:


_PTHREAD_MUTEX_NORMAL


unixtype



Mutex options:


_PTHREAD_MUTEX_RECURSIVE


unixtype



Mutex options: recursive mutex


_PTHREAD_MUTEX_RECURSIVE_NP


unixtype



Mutex options: ?


_PTHREAD_MUTEX_TIMED_NP


unixtype


__WCLONE


Waitpid option: Wait for clone children only



<


б) типы данных:


Название (1.x)


Описание


Название (2.x)


Модуль



Block count type.


Blkcnt_t


baseunix



Block size type.


Blksize_t


baseunix



C type: 8-bit signed integer


cchar


unix, baseunix, unixtype



Double precision real format.


cDouble


unix, baseunix, unixtype



Floating-point real format


cFloat


unix, baseunix, unixtype



C type: integer (natural size)


cInt


unix, baseunix, unixtype



C type: 16 bits sized, signed integer.


cInt16


unix, baseunix, unixtype



C type: 32 bits sized, signed integer.


cInt32


unix, baseunix, unixtype



C type: 64 bits sized, signed integer.


cInt64


unix, baseunix, unixtype



C type: 8 bits sized, signed integer.


cInt8


unix, baseunix, unixtype



Long double precision real format (Extended)


clDouble


unix, baseunix, unixtype



Clock ticks type


clock_t


unix, baseunix, unixtype



C type: long signed integer (double sized)


cLong


unix, baseunix, unixtype



C type: 64-bit (double long) signed integer.


clonglong


unix, baseunix, unixtype



C type: short signed integer (half sized)


cshort


unix, baseunix, unixtype



C type: 8-bit unsigned integer


cuchar


unix, baseunix, unixtype



C type: unsigned integer (natural size)


cUInt


unix, baseunix, unixtype



C type: 16 bits sized, unsigned integer.


cUInt16


unix, baseunix, unixtype



C type: 32 bits sized, unsigned integer.


cUInt32


unix, baseunix, unixtype



C type: 64 bits sized, unsigned integer.


cUInt64


unix, baseunix, unixtype



C type: 8 bits sized, unsigned integer.


cUInt8


unix, baseunix, unixtype



C type: long unsigned integer (double sized)


cuLong


unix, baseunix, unixtype



C type: 64-bit (double long) unsigned integer.


culonglong


unix, baseunix, unixtype



Alias for cuint


cunsigned


unix, baseunix, unixtype



C type: short unsigned integer (half sized)


cushort


unix, baseunix, unixtype


ComStr


Command-line string type.


ComStr


unixutil


dev_t


Device descriptor type


dev_t


unix, baseunix, unixtype



Record used in fpOpenDir and fpReadDir calls


Dir


baseunix


dirent


Record used in the fpReadDir function to return files in a directory.


dirent


baseunix


DirStr


Filename directory part string type.


DirStr


unixutil


ExtStr


Filename extension part string type.


ExtStr


unixutil


fdSet


Array containing file descriptor bitmask for the Select call.





Lock description type for fpFCntl lock call.


FLock


baseunix



Group ID type.


gid_t


unix, baseunix, unixtype



64-bit inode type.


ino64_t


baseunix



Inode type.


ino_t


unix, baseunix, unixtype



Kernel device type


kDev_t


unixtype



Inode mode type.


mode_t


unix, baseunix, unixtype


NameStr


Filename name part string type.


NameStr


unixutil



Number of links type.


nlink_t


unix, baseunix, unixtype



64-bit offset type.


off64_t


baseunix



Offset type.


off_t


unix, baseunix


PathStr


Filename path part string type.


PathStr


unixutil



pointer to TBlkCnt type.


PBlkCnt


baseunix



Pointer to TBlkSize type.


PBlkSize


baseunix



Alias for cchar


pcchar


unix, baseunix, unixtype



Pointer to cdouble type.


pcDouble


unix, baseunix, unixtype



Pointer to cfloat type.


pcFloat


unix, baseunix, unixtype



Pointer to cInt type.


pcInt


unix, baseunix, unixtype



Pointer to cldouble type.


pclDouble


unix, baseunix, unixtype



Pointer to TClock type.


pClock


unix, baseunix, unixtype, unixtype



Pointer to cLong type.


pcLong


unix, baseunix, unixtype



Pointer to cShort type.


pcshort


unix, baseunix, unixtype



Alias for cuchar


pcuchar


unix, baseunix, unixtype



Pointer to cUInt type.


pcUInt


unix, baseunix, unixtype



Pointer to cuLong type.


pculong


unix, baseunix, unixtype



Alias for cunsigned


pcunsigned


unix, baseunix, unixtype



Pointer to cuShort type.


pcushort


unix, baseunix, unixtype



Pointer to TDev type.


pDev


unix, baseunix, unixtype


PDir


Pointer to TDir record


PDir


baseunix


pdirent


Pointer to Dirent record.


pdirent


baseunix


pfdset


Pointer to FDSet array.


pfdset


baseunix



Pointer to TFilDes type.


pFilDes


baseunix


pfpstate


Pointer to tfpstate record.


pfpstate


baseunix



Pointer to TGid type.


pGid


unix, baseunix, unixtype


pglob


Pointer to TGlob record.





Pointer to TGrpArr array.


pGrpArr


baseunix



Process ID type.


pid_t


unix, baseunix, unixtype



Pointer to TIno type.


pIno


unix, baseunix, unixtype



Pointer to TIno64 type.


pIno64


baseunix



Pointer to TkDev type.


pkDev


unixtype



Pointer to TMode type.


pMode


unix, baseunix, unixtype



Pointer to TnLink type.


pnLink


unix, baseunix, unixtype



Pointer to TOff type.


pOff


unix, baseunix, unixtype



Pointer to TOff64 type.


pOff64


baseunix



Pointer to TPid type.


pPid


unix, baseunix, unixtype


PSigActionRec


Pointer to SigActionRec record.


PSigActionRec


baseunix


PSigAltStack


Pointer to SigAltStack record





Pointer to TSigContext record


PSigContext


baseunix


PSigContextRec


Pointer to SigContextRec record





Pointer to TSigInfo record type.


psiginfo


baseunix


PSignalHandler


Pointer to SignalHandler type.


PSignalHandler


baseunix


PSignalRestorer


Pointer to SignalRestorer type


PSignalRestorer


baseunix


PSigSet


Pointer to signal set.


PSigSet


baseunix



Pointer to sigset_t type.


psigset_t


baseunix



Pointer to TSize type.


pSize


unix, baseunix, unixtype



Pointer to size_t type.


psize_t


unixtype



Pointer to TSockLen type.


pSocklen


unix, baseunix, unixtype



Pointer to TsSize type


psSize


unix, baseunix, unixtype


pstack_t


Pointer to stack_t record




PStat


Pointer to Stat record.


PStat


baseunix


PStatFS


Pointer to StatFS record.


PStatFS


unixtype


PSysCallRegs


Pointer to SysCallRegs record.




PSysInfo


Pointer to TSysInfo record.


PSysInfo


linux



Thread attributes record. Opaque.


pthread_attr_t


unixtype



Conditional variable attributes type (opaque).


pthread_condattr_t


unixtype



Thread conditional variable type.


pthread_cond_t


unix, baseunix, unixtype



Thread local storage key (opaque)


pthread_key_t


unixtype



Mutex attributes type (opaque).


pthread_mutexattr_t


unixtype



Thread mutex type.


pthread_mutex_t


unix, baseunix, unixtype



Thread mutex type (opaque).


pthread_mutex_t


unixtype



R/W lock attributes (opaque).


pthread_rwlockattr_t


unixtype



Read/Write lock type (opaque)


pthread_rwlock_t


unixtype



Posix thread type.


pthread_t


unix, baseunix, unixtype



Pointer to TTime type.


pTime


unix, baseunix, unixtype



Pointer to timespec type.


ptimespec


unix, baseunix, unixtype


ptimeval


Pointer to TTimeVal record


ptimeval


unix, baseunix, unixtype


ptimezone


Pointer to TimeZone record.


ptimezone


baseunix



Pointer to time_t type.


ptime_t


unix, baseunix, unixtype



Pointer to TTms type.


PTms


baseunix



Pointer to TUid type.


pUid


unix, baseunix, unixtype


PUTimeBuf


Pointer to TUTimeBuf record


PUTimeBuf


baseunix


PUTSName


Pointer to TUTSName record.


PUTSName


baseunix



Pointer to wchar_t type.


pwchar_t


unixtype



Scheduling parameter description record.


sched_param


unixtype



Semaphore type. (opaque)


sem_t


unixtype



Callback prototype for a SigActionRec record.


SigActionHandler


baseunix


SigActionRec


Record used in fpSigAction call.


SigActionRec


baseunix


SigAltStack


Alternate stack registers record




SigContextRec


Record describing the context of the program when it receives a signal




SignalHandler


Function prototype for the Signal call.


SignalHandler


baseunix


SignalRestorer


Signal restorer function prototype


SignalRestorer


baseunix


SigSet


Signal set type


SigSet


baseunix



Signal set type


sigset_t


baseunix


Size_T


Size type


Size_T


unix, baseunix, unixtype



Socket address length type.


socklen_t


unix, baseunix, unixtype



Small size type.


ssize_t


unix, baseunix, unixtype


stack_t


Alias for SigAltStack type




Stat


Record describing an inode (file) in the pffstat call.


Stat


baseunix


Statfs


Record describing a file system in the fsstat call.




SysCallRegs


Register describing system calls.





Alias for Blkcnt_t type.


TBlkCnt


baseunix



Alias for blksize_t type.


TBlkSize


baseunix



Alias for clock_t type.


TClock


unix, baseunix, unixtype


TCloneFunc


Clone function prototype.


TCloneFunc


linux



Alias for dev_t type.


TDev


unix, baseunix, unixtype


TDir


Record used in OpenDir and ReadDir calls


TDir


baseunix


TDirEnt


Alias for DirEnt record


TDirEnt


baseunix


Termio


Terminal I/O description record (small)




Termios


Terminal I/O description record




TFDSet


Alias for FDSet type.


TFDSet


baseunix



Array of file descriptors as used in fpPipe call.


TFilDes


baseunix


tfpreg


Record describing floating point register in signal handler.


tfpreg


baseunix


tfpstate


Record describing floating point unit in signal handler.


tfpstate


baseunix



Describes the search strategy used by FSearch


TFSearchOption


unix



Alias for gid_t type.


TGid


unix, baseunix, unixtype



Array of gid_t IDs


TGrpArr


baseunix


tglob


Record containing one entry in the result of Glob




timespec


Time interval for the NanoSleep function.


timespec


unix, baseunix, unixtype


timeval


Record specifying a time inteval.


timeval


unix, baseunix, unixtype


timezone


Record describing a timezone


timezone


baseunix



Time span type


time_t


unix, baseunix, unixtype


tmmapargs


Record containing mmap args.





Alias for ino_t type.


TIno


unix, baseunix, unixtype



Alias for ino64_t type.


TIno64


baseunix



Alias for kDev_t type.


TkDev


unixtype



Alias for mode_t type.


TMode


unix, baseunix, unixtype



Record containing timings for fpTimes call.


tms


baseunix



Alias for nlink_t type.


TnLink


unix, baseunix, unixtype



Alias for off_t type.


TOff


unix, baseunix, unixtype



Alias for off64_t type.


TOff64


baseunix



Alias for pid_t type.


TPid


unix, baseunix, unixtype


Tpipe


Array describing a pipe pair of filedescriptors.


Tpipe


unix


TSigAction


Function prototype for SigAction call.





Alias for SigActionRec record type.


TSigActionRec


baseunix



Record describing the CPU context when a signal occurs.


TSigContext


baseunix



Record describing the signal when a signal occurs.


tsiginfo


baseunix



Alias for SigSet type.


TSigSet


baseunix



Alias for size_t type


TSize


unix, baseunix, unixtype



Alias for socklen_t type.


TSocklen


unix, baseunix, unixtype



Alias for ssize_t type


TsSize


unix, baseunix, unixtype


TStat


Alias for Stat record.


TStat


baseunix


TStatFS


Alias for StatFS type.


TStatFS


unix, baseunix, unixtype


TSysCallRegs


Alias for SysCallRegs record




TSysinfo


Record with system information, used by the SysInfo call.


TSysinfo


linux


TTermio


Alias for TermIO record




TTermios


Alias for Termios record.





Alias for TTime type.


TTime


unix, baseunix, unixtype



Alias for TimeSpec type.


Ttimespec


unix, baseunix, unixtype


TTimeVal


Alias for TimeVal record.


TTimeVal


unix, baseunix, unixtype


TTimeZone


Alias for TimeZone record.


TTimeZone


baseunix



Alias for Tms record type.


TTms


baseunix



Alias for uid_t type.


TUid


unix, baseunix, unixtype


TUTimeBuf


Alias for UTimBuf record.


TUTimeBuf


baseunix


TUTSName


Alias for UTSName record.


TUTSName


baseunix


TWinSize


Alias for WinSize record.





User ID type


uid_t


unix, baseunix, unixtype


UTimBuf


Record used in Utime to set file access and modificaton times.


UTimBuf


baseunix


UTimeBuf


Alias for UTimBuf record.




utsname


Record used to return kernel information in UName function.


utsname


baseunix



Wide character type.


wchar_t


unixtype



Wide character size type.


wint_t


unixtype


winsize


Record describing terminal window size.





Fast lock (mutex) type (opaque).


_pthread_fastlock


unixtype

<


в) процедуры и функции:


Название (1.x)


Описание


Название (2.x)


Модуль


Access


Check file access


fpAccess


baseunix


Alarm


Schedule an alarm signal to be delivered


fpAlarm


baseunix



Concert an array of string to an array of null-terminated strings


ArrayStringToPPchar


unixutil


AssignPipe


Create a set of pipe file handlers


AssignPipe


unix


AssignStream


Assign stream for in and output to a program


AssignStream


unix


Basename


Return basename of a file


Basename


unixutil


CFMakeRaw


Sets flags in Termios record.




CFSetISpeed


Set input baud rate in Termios record




CFSetOSpeed


Set output baud rate in Termios record




Chmod


Change file permission bits


fpChmod


baseunix


Chown


Change owner of file


fpChown


baseunix


Clone


Clone current process (create new thread)


Clone


linux


CloseDir


Close directory file descriptor


fpCloseDir


baseunix


CreateShellArgV


Create an array of null-terminated strings




Dirname


Extract directory part from filename


Dirname


unixutil


Dup


Duplicate a file handle


fpDup


baseunix


Dup2


Duplicate one filehandle to another


fpDup2


baseunix


EpochToLocal


Convert epoch time to local time


EpochToLocal


unixutil


Execl


Execute process (using argument list)


FpExecL


unix


Execle


Execute process (using argument list, environment)


FpExecLE


unix


Execlp


Execute process (using argument list, environment; search path)


FpExecLP


unix


Execv


Execute process


FpExecV


unix, baseunix


Execve


Execute process using environment


fpExecve


baseunix


Execvp


Execute process, search path


FpExecVP


unix



Execute process, search path using environment


FpExecVPE


unix


ExitProcess


Exit the current process


fpExit


baseunix


Fcntl


File control operations.


fpFcntl


baseunix


fdClose


Close file descriptor


fpClose


baseunix


fdFlush


Flush kernel file buffer




fdOpen


Open file and return file descriptor


fpOpen


baseunix


fdRead


Read data from file descriptor


fpRead


baseunix


fdSeek


Set file pointer position.


FpLseek


baseunix


fdTruncate


Truncate file on certain size.


FpFtruncate


baseunix


fdWrite


Write data to file descriptor


fpWrite


baseunix


FD_Clr


Clears a filedescriptor in a set


fpFD_Clr


baseunix


FD_IsSet


Check whether a filedescriptor is set


fpFD_IsSet


baseunix


FD_Set


Set a filedescriptor in a set


fpFD_Set


baseunix


FD_Zero


Clear all file descriptors in set


fpFD_Zero


baseunix


FExpand


Expand filename to fully qualified path




Flock


Lock a file (advisory lock)


fpFlock


unix


FNMatch


Check whether filename matches wildcard specification


FNMatch


unixutil


Fork


Create child process


fpFork


baseunix



Change current working directory.


FpChdir


baseunix



Set all filedescriptors in the set.


fpfdfillset


baseunix



Retrieve the current working directory.


FpGetcwd


baseunix



Retrieve extended error information.


fpgeterrno


baseunix



Get the list of supplementary groups.


FpGetgroups


baseunix



Get process group ID


FpGetpgrp


baseunix



Create a new directory


FpMkdir


baseunix



Create a set of pipe file handlers


FpPipe


baseunix



Rename file


FpRename


baseunix



Remove a directory.


FpRmdir


baseunix



Set extended error information.


fpseterrno


baseunix



Set the current group ID


FpSetgid


baseunix



Create a new session


FpSetsid


baseunix



Set kernel time


fpsettimeofday


baseunix



Set the current user ID


FpSetuid


baseunix



Set a signal in a signal set.


FpSigAddSet


baseunix



Remove a signal from a signal set.


FpSigDelSet


baseunix



Clear all signals from signal set.


FpsigEmptySet


baseunix



Set all signals in signal set.


FpSigFillSet


baseunix



Check whether a signal appears in a signal set.


FpSigIsMember


baseunix



Suspend process for several seconds


FpSleep


baseunix



Retrieve file information about a file descriptor.


FpStat


baseunix



Return execution times for the current process


FpTimes


baseunix



Wait for a child to exit.


FpWait


baseunix


FReName


Rename file




FSearch


Search for file in search path.


FSearch


unix


FSplit


Split filename into path, name and extension


FSplit


unixutil


FSStat


Retrieve filesystem information.




FStat


Retrieve information about a file


fpFStat


baseunix



Retrieve filesystem information from a file descriptor.


fStatFS


unix



Synchronize file's kernel data with disk.


fsync


unix


GetDate


Return the system date




GetDateTime


Return system date and time




GetDomainName


Return current domain name


GetDomainName


unix


GetEGid


Return effective group ID


fpGetEGid


baseunix


GetEnv


Return value of environment variable.


fpGetEnv


baseunix


GetEpochTime


Return the current unix time


fptime


baseunix


GetEUid


Return effective user ID


fpGetEUid


baseunix


GetFS


Return file selector


GetFS


unixutil


GetGid


Return real group ID


fpGetGid


baseunix


GetHostName


Return host name


GetHostName


unix


GetLocalTimezone


Return local timzeone information


GetLocalTimezone


unix


GetPid


Return current process ID


fpGetPid


baseunix


GetPPid


Return parent process ID


fpGetPPid


baseunix


GetPriority


Return process priority


fpGetPriority


baseunix


GetTime


Return current system time




GetTimeOfDay


Return kernel time of day in GMT


fpGetTimeOfDay


unix


GetTimezoneFile


Return name of timezone information file


GetTimezoneFile


unix


GetUid


Return current user ID


fpGetUid


baseunix


Glob


Find filenames matching a wildcard pattern




Globfree


Free result of Glob call





Converts a gregorian date to a julian date


GregorianToJulian


unixutil



Converts a julian date to a gregorian date


JulianToGregorian


unixutil


IOCtl


General kernel IOCTL call.


fpIOCtl


baseunix


IOperm


Set permission on IO ports


fpIOperm


x86


IoPL


Set I/O privilege level


fpIoPL


x86


IsATTY


Check if filehandle is a TTY (terminal)




Kill


Send a signal to a process


fpKill


baseunix


Link


Create a hard link to a file


fpLink


baseunix


LocalToEpoch


Convert local time to epoch (unix) time


LocalToEpoch


unixutil


Lstat


Return information about symbolic link. Do not follow the link


fpLstat


baseunix


mkFifo


Create FIFO (named pipe) in file system


fpmkFifo


baseunix


MMap


Create memory map of a file


fpMMap


baseunix


MUnMap


Unmap previously mapped memory block


fpMUnMap


baseunix


NanoSleep


Suspend process for a short time


fpNanoSleep


baseunix


Nice


Set process priority


fpNice


baseunix


Octal


Convert octal to decimal value




OpenDir


Open directory for reading


fpOpenDir


baseunix


Pause


Wait for a signal


fpPause


baseunix


PClose


Close file opened with POpen


PClose


unix


POpen


Pipe file to standard input/output of program


POpen


unix


ReadDir


Read entry from directory


fpReadDir


baseunix


ReadLink


Read destination of symbolic link


ReadLink


baseunix



Read data from a PC port


ReadPort


x86



Read bytes from a PC port


ReadPortB


x86



Read longints from a PC port


ReadPortL


x86



Read Words from a PC port


ReadPortW


x86


ReadTimezoneFile


Read the timezone file and initialize time routines


ReadTimezoneFile


unix


SeekDir


Seek to position in directory


SeekDir


unix


Select


Wait for events on file descriptors


fpSelect


baseunix


SelectText


Wait for event on typed ontyped file.


SelectText


unix


SetDate


Set the current system date.




SetDateTime


Set the current system date and time




SetPriority


Set process priority


fpSetPriority


baseunix


SetTime


Set the current system time.




Shell


Execute and feed command to system shell


Shell


unix


SigAction


Install signal handler


fpSigAction


baseunix


Signal


Install signal handler (deprecated)


fpSignal


baseunix


SigPending


Return set of currently pending signals


fpSigPending


baseunix


SigProcMask


Set list of blocked signals


fpSigProcMask


baseunix


SigRaise


Raise a signal (send to current process)


SigRaise


unix


SigSuspend


Set signal mask and suspend process till signal is received


fpSigSuspend


baseunix



Retrieve filesystem information from a path.


StatFS


unix


StringToPPChar


Split string in list of null-terminated strings


StringToPPChar


unixutil


SymLink


Create a symbolic link


fpSymLink


baseunix


SysCall


Execute system call.




Sysinfo


Return kernel system information


Sysinfo


linux



Execute and feed command to system shell


fpSystem


unix


S_ISBLK


Is file a block device


fpS_ISBLK


baseunix


S_ISCHR


Is file a character device


fpS_ISCHR


baseunix


S_ISDIR


Is file a directory


fpS_ISDIR


baseunix


S_ISFIFO


Is file a FIFO


fpS_ISFIFO


baseunix


S_ISLNK


Is file a symbolic link


fpS_ISLNK


baseunix


S_ISREG


Is file a regular file


fpS_ISREG


baseunix


S_ISSOCK


Is file a unix socket


fpS_ISSOCK


baseunix


TCDrain


Terminal control: Wait till all data was transmitted




TCFlow


Terminal control: Suspend transmission of data




TCFlush


Terminal control: Discard data buffer




TCGetAttr


Terminal Control: Get terminal attributes




TCGetPGrp


Terminal control: Get process group




TCSendBreak


Terminal control: Send break




TCSetAttr


Terminal control: Set attributes




TCSetPGrp


Terminal control: Set process group




TellDir


Return current location in a directory


TellDir


unix


TTYname


Terminal control: Get terminal name




Umask


Set file creation mask.


fpUmask


baseunix


Uname


Return system name.


fpUname


baseunix


UnLink


Unlink (i.e. remove) a file.


fpUnLink


baseunix


Utime


Set access and modification times of a file (touch).


Utime


baseunix


WaitPid


Wait for a process to terminate


fpWaitPid


baseunix


WaitProcess


Wait for process to terminate.


WaitProcess


unix


WEXITSTATUS


Extract the exit status from the WaitPID result.


WEXITSTATUS


baseunix


WIFEXITED


Check whether the process exited normally


WIFEXITED


baseunix


WIFSIGNALED


Check whether the process was exited by a signal.


WIFSIGNALED


baseunix


WIFSTOPPED


Check whether the process is currently stopped.


WIFSTOPPED


unix



Write data to PC port


WritePort


x86



Write byte to PC port


WritePortB


x86



Write longint to PC port.


WritePortl


x86



Write Word to PC port


WritePortW


x86


WSTOPSIG


Return the exit code from the process.


WSTOPSIG


baseunix


WTERMSIG


Return the signal that caused a process to exit.


WTERMSIG


baseunix


W_EXITCODE


Construct an exit status based on an return code and signal.


W_EXITCODE


unix


W_STOPCODE


Construct an exit status based on a signal.


W_STOPCODE


unix

<


г) переменные:


Название (1.x)


Описание


Название (2.x)


Модуль


ErrNo


Error number of last operation.




LinuxError


Last operating system error




tzdaylight


Indicates whether daylight savings time is active.


tzdaylight


unix


tzname


Timezone name.


tzname


unix


tzseconds


Seconds west of GMT


tzseconds


unixutil, unix


Пример аккуратный выход


Предположим, что программа использует временный рабочий файл. Следующая простая процедура удаляет файл:

(* Аккуратный выход из программы *)

uses linux;

procedure g_exit(s:integer);cdecl;

begin

  unlink ('tempfile');

  writeln (stderr, 'Прерывание - выход из программы');

  halt(1);

end;

Можно связать эту процедуру с определенным сигналом:

var

  act:sigactionrec;

.

.

  act.handler.sh := @g_exit;

  sigaction (SIGINT, @act, nil);

Если после этого вызова пользователь нажмет клавишу прерывания, то управление будет автоматически передано процедуре g_exit. Можно дополнить процедуру g_exit другими необходимыми для завершения операциями.



Пример copyfile


Теперь можем закрепить материал на практике. Задача состоит в написании функции copyfile, которая будет копировать содержимое одного файла в другой. Возвращаемое значение должно быть равно нулю в случае успеха или отрицательному числу – в случае ошибки.

Основная логика действий понятна: открыть первый файл, затем создать второй и выполнять чтение из первого файла и запись во второй до тех пор, пока не будет достигнут конец первого файла. И, наконец, закрыть оба файла.

Окончательное решение может выглядеть таким образом:

(* Программа copyfile: скопировать файл name1 в файл name2 *)

uses linux;

const

  BUFSIZE=512;         (* Размер считываемого блока *)

  PERM=0644;           (* Права доступа для нового файла в форме,

                         похожей на восьмеричную *)

(* Скопировать файл name1 в файл name2 *)

function copyfile(name1, name2: string):integer;

var

  infile, outfile: integer; (*дескрипторы файлов*)

  nread: longint;

  buffer: array [0..BUFSIZE-1] of byte; (*буфер для чтения/записи*)

begin

  infile := fdopen (name1, Open_RDONLY);

  if infile=-1 then

  begin

    copyfile:=-1; (*ошибка открытия первого файла*)

    exit;

  end;

  outfile := fdopen (name2, Open_WRONLY or Open_CREAT or Open_TRUNC, octal(PERM));

  if outfile=-1 then

  begin

    fdclose(infile);

    copyfile:=-2; (*ошибка открытия второго файла*)

    exit;

  end;

  (* Чтение из файла name1 по BUFSIZE символов *)

  nread := fdread (infile, buffer, BUFSIZE);

  while nread > 0 do

  begin

    (* Записать buffer в выходной файл. *)

    if fdwrite (outfile, buffer, nread) < nread then

    begin

      fdclose (infile);

      fdclose (outfile);

      copyfile:=-3;          (* ошибка записи *)

      exit;

    end;

    nread := fdread (infile, buffer, BUFSIZE);

  end;

  (*закрываем прочитанные файлы*)

  fdclose (infile);

  fdclose (outfile);

  if (nread = -1) then

    copyfile := -4           (* ошибка при последнем чтении *)




  else                    

    copyfile := 0;              (* все порядке *)

end;

(* Программа для тестирования функции copyfile *)

var

  retcode:integer;

begin

  if paramcount<2 then

  begin

    writeln('Используйте: ',paramstr(0),' файл-источник файл-приемник');

    exit;

  end;

  retcode := copyfile(paramstr(1), paramstr(2));

  case retcode of

     0:  writeln('Файл ',paramstr(1),' успешно скопирован в файл ',paramstr(2));

    -1:  writeln('Ошибка открытия файла ',paramstr(1),' для чтения');

    -2:  writeln('Ошибка открытия файла ',paramstr(2),' для записи');

    -3:  writeln('Ошибка записи в файл ',paramstr(2));

    -4:  writeln('Ошибка чтения из файла ',paramstr(1));

  end;

end.

Теперь функцию copyfile можно вызывать так:

retcode := copyfile('squarepeg', 'roundhole');

Упражнение 2.6. Измените функцию copyfile так, чтобы в качестве ее параметров могли выступать дескрипторы, а не имена файлов. Проверьте работу новой версии программы.

Упражнение 2.7. Если вы умеете работать с аргументами командной строки, используйте одну из процедур copyfile для создания программы mycp, копирующей первый заданный в командной строке файл во второй.


Пример docommand


Модуль stdio предоставляет библиотечную процедуру runshell, которая позволяет выполнить в программе команду оболочки. Для примера создадим упрощенную версию этой процедуры docommand, используя вызовы fork и ехес. В качестве посредника вызовем стандартную оболочку (заданную именем /bin/sh), а не будем пытаться выполнять программу напрямую. Это позволит программе docommand воспользоваться преимуществами, предоставляемыми оболочкой, например, раскрытием шаблонов имен файлов. Задание параметра -с

вызова оболочки определяет, что команды передаются не со стандартного ввода, а берутся из следующего строчного аргумента.

 (* Программа docommand -- запуск команды оболочки, первая версия *)

uses linux,stdio;

function docommand(command:pchar):integer;

var

  pid:longint;

begin

  pid := fork;

  if pid < 0 then

  begin

    docommand:=-1;

    exit;

  end;

  if pid = 0 then            (* дочерний процесс *)

  begin

    linuxexecl('/bin/sh', 'sh', ['-c', command, nil]);

    perror ('execl');

    halt(1);

  end;

  (* Код родительского процесса *)

  (* Ожидание возврата из дочернего процесса *)

  wait(nil);

  docommand:=0;

end;

begin

  docommand('ls -l | wc -l');

end.

Следует сказать, что это только первое приближение к настоящей библиотечной процедуре runshell. Например, если конечный пользователь программы нажмет клавишу прерывания во время выполнения команды оболочки, то и вызывающая программа, и команда остановятся. Существуют способы обойти это ограничение, которые будут рассмотрены в следующей главе.



Пример: гостиница


В качестве несколько надуманного, но возможно наглядного примера, предположим, что имеется файл residents, в котором записаны фамилии постояльцев гостиницы. Первая строка содержит фамилию жильца комнаты 1, вторая – жильца комнаты 2 и т.д. (очевидно, это гостиница с прекрасно организованной системой нумерации комнат). Длина каждой строки составляет ровно 41 символ, в первые 40 из которых записана фамилия жильца, а 41-й символ является символом перевода строки для того, чтобы файл можно было вывести на дисплей при помощи команды UNIX cat.

Следующая функция getoccupier вычисляет по заданному целому номеру комнаты положение первого байта фамилии жильца, затем перемещается в эту позицию и считывает данные. Она возвращает либо указатель на строку с фамилией жильца, либо нулевой указатель в случае ошибки (мы будем использовать для этого значение nil). Обратите внимание, что мы присвоили переменной дескриптора файла infile исходное значение –1. Благодаря этому мы можем гарантировать, что файл будет открыт всего один раз.

(* Функция getoccupier - получить фамилию из файла residents *)

uses linux;

const

  NAMELENGTH=41;

var

  namebuf:array [0..NAMELENGTH-1] of char;     (* Буфер для фамилии *)

const

  infile:integer=-1;         (* Для хранения дескриптора файла *)

function getoccupier(roomno:integer):pchar;

var

  offset, nread:longint;

begin

  (* Убедиться, что файл открывается впервые *)

  if infile = -1 then

  begin

    infile := fdopen ('residents', Open_RDWR);

    if infile = -1 then

    begin

      getoccupier := nil;          (* Невозможно открыть файл *)

      exit;

    end;

  end;

  offset := (roomno - 1) * NAMELENGTH;

  (* Найти поле комнаты и считать фамилию жильца *)

  if fdseek (infile, offset, SEEK_SET) = -1 then

  begin

    getoccupier := nil;

    exit;

  end;

  nread := fdread (infile, namebuf, NAMELENGTH);

  if nread <= 0 then

  begin

    getoccupier := nil;

    exit;




  end;

  (* Создать строку, заменив символ перевода строки на '\0' *)

  namebuf[nread - 1] := #0;

  getoccupier := namebuf;

end;

Если предположить, что в гостинице 10 комнат, то следующая программа будет последовательно вызывать функцию getoccupier для просмотра файла и выводить каждую найденную фамилию при помощи процедуры writeln из стандартного модуля system:

(* Программа listoc выводит все фамилии жильцов *)

const

  NROOMS=10;

var

  j:integer;

  p:pchar;

begin

  for j := 1 to NROOMS do

  begin

    p := getoccupier (j);

    if p<>nil then

      writeln('Комната ', j:2, ', ', p)

    else

      writeln('Ошибка для комнаты ', j);

  end;

end.

Упражнение 2.9. Придумайте алгоритм для определения пустых комнат. Измените функцию getoccupier и файл данных, если это необходимо, так, чтобы он отражал эти изменения. Затем напишите процедуру с названием findfree для поиска свободной комнаты с наименьшим номером.

Упражнение 2.10. Напишите процедуру freeroom для удаления записи о жильце. Затем напишите процедуру addguest для внесения новой записи о жильце, с предварительной проверкой того, что выделяемая комната свободна.

Упражнение 2.11. Объедините процедуры getoccupier, freeroom, addguest и findfree в простой программе с названием frontdesk, которая управляет файлом данных. Используйте аргументы командной строки или напишите интерактивную программу, которая вызывает функции writeln и readln. В обоих случаях для вычисления номера комнаты вам потребуется преобразовывать строки в целые числа. Вы можете использовать для этого библиотечную процедуру StrToInt:

i := StrToInt(str);

где string – указатель на строку символов, а i

– целое число.

Упражнение 2.12. В качестве обобщенного примера напишите программу на основе системного вызова fdseek, которая копирует в обратном порядке байты из одного файла в другой. Насколько эффективным получилось ваше решение?

Упражнение 2.13. Используя вызов fdseek, напишите процедуры для копирования последних 10 символов, последних 10 слов и последних 10 строк из одного файла в другой.


Пример игнорирование сигнала SIGINT


Для того чтобы процесс игнорировал сигнал прерывания SIGINT, все, что нужно для этого сделать – это заменить следующую строку в программе:

act.handler.sh := @catchint;

на

act.handler.sh := SIG_IGN;

После выполнения этого оператора нажатие клавиши прерывания будет безрезультатным. Снова разрешить прерывание можно так:

act.handler.sh := SIG_DFL;

sigaction (SIGINT, @act, nil);

Можно игнорировать сразу несколько сигналов, например:

act.handler.sh := SIG_IGN;

sigaction(SIGINT, @act, nil);

При этом игнорируются оба сигнала SIGINT и SIGQUIT. Это может быть полезно в программах, которые не должны прерываться с клавиатуры.

Некоторые командные интерпретаторы используют этот подход, чтобы гарантировать, что фоновые процессы не останавливаются при нажатии пользователем клавиши прерывания. Это возможно вследствие того, что игнорируемые процессом сигналы будут игнорироваться и после вызова ехес. Поэтому командный интерпретатор может вызвать sigaction для игнорирования сигналов SIGQUIT и SIGINT, а затем запустить новую программу при помощи вызова ехес.



Пример использования функции malloc: связные списки


В информатике существует множество типов динамических структур данных. Одним из классических примеров таких структур является связный список, в котором группа одинаковых объектов связывается в единый логический объект. В этом разделе составляется простой пример, использующий односвязный список для демонстрации применения функций семейства malloc.

(* list.inc - заголовочный файл для примера. *)

uses strings,stdio;

(* Определение основной структуры *)

type

pMEMBER=^MEMBER;

MEMBER=record

  m_data:pchar;

  m_next:pMEMBER;

end;

ppMEMBER=^pMEMBER;

(* Определение функций *)

function new_member(data:pchar):pMEMBER;forward;

procedure add_member(head:ppMEMBER; newmem:pMEMBER);forward;

procedure free_list (head:ppMEMBER);forward;

procedure printlist (listhead:pMEMBER);forward;

Оператор type вводит тип MEMBER, имеющий два поля. Первое поле, m_data, в экземпляре типа MEMBER будет указывать на произвольную строку. Второе поле, m_next, указывает на другой объект типа MEMBER.

Каждый элемент в связном списке структур типа MEMBER будет указывать на следующий в списке объект MEMBER, то есть если известен один элемент списка, то следующий можно найти при помощи указателя m_next. Поскольку на каждый объект MEMBER в списке ссылается только один указатель, список можно обходить только в одном направлении. Такие списки называются односвязными (singly linked). Если бы был определен еще один указатель m_prev, то список можно было бы обходить и в обратном направлении, и в этом случае он был бы двусвязным (doubly linked).

Адрес начала, или головы, списка обычно записывается в отдельном указателе, определенным следующим образом:

const

  head:pmember = nil;

Конец списка обозначается нулевым значением поля

m_next последнего элемента списка.

На рис. 12.1 показан простой список из трех элементов. Его начало обозначено указателем head.

Теперь представим небольшой набор процедур для работы с этими структурами. Первая функция называется new_member. Она отводит память под структуру MEMBER с помощью вызова malloc. Обратите внимание, что указателю m_next присвоено нулевое значение, в данном случае обозначенное как nil. Это связано с тем, что функция malloc не обнуляет выделяемую память. Поэтому при создании структуры




MEMBER поле m_text может содержать ложный, но правдоподобный адрес.

Рис. 12.1. Связный список объектов MEMBER
{$i list.inc}
(* Функция new_member - выделить память для нового элемента *)
function new_member (data:pchar):pMEMBER;
var
  newmem:pMEMBER;
begin
  newmem := malloc (sizeof (MEMBER));
  if newmem = nil then
    writeln (stderr, 'new_member: недостаточно памяти')
  else
  begin
    (* Выделить память для копирования данных *)
    newmem^.m_data := malloc (strlen (data) + 1);
    (* Скопировать данные в структуру *)
    strcopy (newmem^.m_data, data);
    (* Обнулить указатель в структуре *)
    newmem^.m_next := nil;
  end;
  new_member:=newmem;
end;
Следующая процедура add_member добавляет в список, на который указывает head, новый элемент
MEMBER. Как видно из текста процедуры, элемент MEMBER добавляется всегда в начало списка.
{$i list.inc}
(* Процедура add_member - добавить новый элемент MEMBER *)
procedure add_member (head:ppMEMBER; newmem:pMEMBER);
begin
  (* Эта простая процедура вставляет новый
   * элемент в начало списка
   *)
  newmem^.m_next := head^;
  head^ := newmem;
end;
Последняя процедура – free_list. Она принимает указатель на начало списка head^ и освобождает память, занятую всеми структурами MEMBER, образующими список. Она также обнуляет указатель head^, гарантируя, что в указателе head^ не содержится прежнее значение (иначе при случайном использовании head^ могла бы возникать трудноуловимая ошибка).
{$i list.inc}
(* Процедура free_list - освободить занятую списком память *)
procedure free_list (head:ppMEMBER);
var
  curr, next:pMEMBER;
begin
  curr := head^;
  while curr <> nil do
  begin
    next := curr^.m_next;
    (* Освободить память, занятую данными *)
    free (curr^.m_data);
    (* Освободить память, отведенную под структуру списка *)
    free (curr);
    curr := next;
  end;
  (* Обнулить указатель на начало списка *)
  head^ := nil;
end;


Следующая простая программа использует описанные процедуры. Она создает односвязный список, изображенный на рис. 12.1, а затем удаляет его. Обратите внимание на способ обхода списка процедурой
printlist. Используемый в ней цикл while типичен для программ, в которых применяются связные списки.
{$i list.inc}
(* Обход и вывод списка *)
procedure printlist (listhead:pMEMBER);
var
  m:pMEMBER;
begin
  writeln (#$a'Содержимое списка:');
  if listhead = nil then
    writeln (#9'(пусто)')
  else
  begin
    m := listhead;
    while m <> nil do
    begin
      writeln (#9, m^.m_data);
      m := m^.m_next;
    end;
  end;
end;
const
  strs:array [0..2] of pchar=('again', 'world', 'Hello');
(* Программа для проверки процедур работы со списком *)
var
  head, newm:pMEMBER;
  j:integer;
begin
  (* Инициализация списка *)
  head := nil;
  (* Добавление элементов к списку *)
  for j:=0 to 2 do
  begin
    newm := new_member (strs[j]);
    add_member (@head, newm);
  end;
  (* Вывести элементы списка *)
  printlist (head);
  (* Удалить список *)
  free_list (@head);
  (* Вывести элементы списка *)
  printlist (head);
end.
Следует обратить внимание и на то, как в начале программы был инициализирован список присвоением указателю head значения nil. Это важно, так как иначе список мог оказаться заполненным «мусором», что неизбежно привело бы к ошибочной работе или к аварийному завершению программы.
Рассматриваемая программа должна выдать такой результат:
Содержимое списка:
        Hello world again
Содержимое списка:
        (пусто)

Пример передачи сообщений: очередь с приоритетами


В этом разделе разработаем простое приложение для передачи сообщений. Его целью является реализация очереди, в которой для каждого элемента может быть задан приоритет. Серверный процесс будет выбирать элементы из очереди и обрабатывать их каким-либо образом. Например, элементы очереди могут быть именами файлов, а серверный процесс может копировать их на принтер. Этот пример аналогичен примеру использования FIFO из раздела 7.2.1.

Отправной точкой будет следующий заголовочный файл q.inc:

(* q.h - заголовок для примера очереди сообщений *)

const

  QKEY:tkey=(1 shl 6) + 5{0105};       (* ключ очереди *)

  QPERM=(6 shl 6) + (6 shl 3){0660};   (* права доступа *)

  MAXOBN=50;                           (* макс. длина имени объекта *)

  MAXPRIOR=10;                         (* максимальный приоритет *)

type

  q_entry=record

    mtype:longint;

    mtext:array [0..MAXOBN] of char;

  end;

  pq_entry=^q_entry;

Определение QKEY задает значение ключа, которое будет обозначать очередь сообщений в системе. Определение QPERM устанавливает связанные с очередью права доступа. Так как код доступа равен octal(0660), то владелец очереди и члены его группы смогут выполнять чтение и запись. Как увидим позже, определения MAXOBN и MAXPRIOR будут налагать ограничения на сообщения, помещаемые в очередь. Последняя часть этого включаемого файла содержит определение структуры q_entry. Структуры этого типа будут использоваться в качестве сообщений, передаваемых и принимаемых следующими процедурами.

Первая рассматриваемая процедура называется enter, она помещает в очередь имя объекта, заканчивающееся нулевым символом, и имеет следующую форму:

{$i q.inc}

(* Процедура enter - поместить объект в очередь *)

function enter (objname:string;priority:longint):boolean;

var

  len, s_qid:longint;

  s_entry:q_entry;     (* структура для хранения сообщений *)

begin

  (* Проверка длины имени и уровня приоритета *)

  len := length (objname);

  if len > MAXOBN then




  begin
    warn ('слишком длинное имя');
    enter:=false;
    exit;
  end;
  if (priority > MAXPRIOR) or (priority < 0) then
  begin
    warn ('недопустимый уровень приоритета');
    enter:=false;
    exit;
  end;
  (* Инициализация очереди сообщений, если это необходимо *)
  s_qid := init_queue;
  if s_qid = -1 then
  begin
    enter:=false;
    exit;
  end;
  (* Инициализация структуры переменной s_entry *)
  s_entry.mtype := priority;
  strlcopy (s_entry.mtext, @objname[1], MAXOBN);
  (* Посылаем сообщение, выполнив ожидание, если это необходимо *)
  if not msgsnd (s_qid, @s_entry, len, 0) then
  begin
    perror ('Ошибка вызова msgsnd');
    enter:=false;
    exit;
  end
  else
    enter:=true;
end;
Первое действие, выполняемое процедурой
enter, заключается в проверке длины имени объекта и уровня приоритета. Обратите внимание на то, что минимальное значение переменной приоритета priority равно 1, так как нулевое значение приведет к неудачному завершению вызова msgsnd. Затем процедура enter «открывает» очередь, вызывая процедуру init_queue, реализацию которой приведем позже.
После завершения этих действий процедура формирует сообщение и пытается послать его при помощи вызова msgsnd. Здесь для хранения сообщения использована структура s_entry типа q_entry, и последний параметр вызова msgsnd равен нулю. Это означает, что система приостановит выполнение текущего процесса, если очередь заполнена (так как не задан флаг IPC_NOWAIT).
Процедура enter сообщает о возникших проблемах при помощи функции warn или библиотечной функции perror. Для простоты функция warn реализована следующим образом:
procedure warn (s:pchar);
begin
  writeln(stderr, 'Предупреждение: ', s);
end;
В реальных системах функция warn должна записывать сообщения в специальный файл протокола.
Назначение функции init_queue очевидно. Она инициализирует идентификатор очереди сообщений или возвращает идентификатор очереди сообщений, который с ней уже связан.


{$i q.inc}
(* Инициализация очереди - получить идентификатор очереди *)
function init_queue:longint;
var
  queue_id:longint;
begin
  (* Попытка создания или открытия очереди сообщений *)
  queue_id := msgget (QKEY, IPC_CREAT or QPERM);
  if queue_id = -1 then
    perror ('Ошибка вызова msgget');
  init_queue:=queue_id;
end;
Следующая процедура, serve, используется серверным процессом для получения сообщений из очереди и противоположна процедуре
enter.
{$i q.inc}
(* Процедура serve - принимает и обрабатывает сообщение обслуживает
 * объект очереди с наивысшим приоритетом
 *)
function serve:integer;
var
  r_qid:longint;
  r_entry:q_entry;
begin
  (* Инициализация очереди сообщений, если это необходимо *)
  r_qid := init_queue;
  if r_qid = -1 then
  begin
    serve:=-1;
    exit;
  end;
  (* Получить и обработать следующее сообщение *)
  while true do
  begin
    if not msgrcv(r_qid, @r_entry, MAXOBN, -1*MAXPRIOR, MSG_NOERROR) then
    begin
      perror ('Ошибка вызова msgrcv');
      serve:=-1;
      exit;
    end
    else
    begin
      (* Обработать имя объекта *)
      proc_obj (@r_entry);
    end;
  end;
end;
Обратите внимание на вызов msgrcv. Так как в качестве параметра типа задано отрицательное значение (-1 * MAXPRIOR), то система вначале проверяет очередь на наличие сообщений со значением
mtype равным 1, затем равным 2 и так далее, до значения MAXPRIOR включительно. Другими словами, сообщения с наименьшим номером будут иметь наивысший приоритет. Процедура proc_obj работает с объектом. Для системы печати она может просто копировать файл на принтер.
Две следующих простых программы демонстрируют взаимодействие этих процедур: программа etest помещает элемент в очередь, а программа stest обрабатывает его (в действительности она всего лишь выводит содержимое и тип сообщения).

Пример перехват сигнала SIGINT


Этот пример демонстрирует, как можно перехватить сигнал, а также проясняет лежащий в его основе механизм сигналов. Программа sigex просто связывает с сигналом SIGINT функцию catchint, а затем выполняет набор операторов sleep и writeln. В данном примере определена структура act типа sigactionrec как глобальная, поэтому при инициализации структуры все поля, и в частности поле sa_flags обнуляются.

(* Программа sigex - демонстрирует работу sigaction *)

uses linux,stdio;

(* Простая функция для обработки сигнала SIGINT *)

procedure catchint (signo:integer);cdecl;

begin

  writeln (#$a'сигнал CATCHINT: signo=', signo);

  writeln ('сигнал CATCHINT: возврат'#$a);

end;

 

var

  act,oact:sigactionrec;

  mask:sigset_t;

begin

  (* Определение процедуры обработчика сигнала catchint *)

  (* Задание действия при получении сигнала SIGINT *)

  act.handler.sh := @catchint;

  (* Создать маску, включающую все сигналы *)

  sigfillset (@mask);

  act.sa_mask:=mask.__val[0];

  (* До вызова процедуры sigaction сигнал SIGINT

   * приводил к завершению процесса (действие по умолчанию)

   *)

  sigaction (SIGINT, @act, @oact);

  (* При получении сигнала SIGINT управление

   * будет передаваться процедуре catchint

   *)

  writeln ('вызов sleep номер 1');

  sleep (1);

  writeln ('вызов sleep номер 2');

  sleep (1);

  writeln ('вызов sleep номер 3');

  sleep (1);

  writeln ('вызов sleep номер 4');

  sleep (1);

  writeln ('Выход');

  halt(0);

end.

Сеанс обычного запуска sigex будет выглядеть так:

$ sigex

Вызов sleep номер 1

Вызов sleep номер 2

Вызов sleep номер 3

Вызов sleep номер 4

Выход

Пользователь может прервать выполнение программы sigex, нажав клавишу прерывания задания. Если она была нажата до того, как в программе sigex была выполнена процедура sigaction, то процесс просто завершит работу. Если же нажать да клавишу прерывания после вызова sigaction, то управление будет передано функции catchint:

$ sigex

Вызов sleep номер 1

<прерывание> (пользователь нажимает на клавишу прерывания)

Сигнал CATCHINT: signo = 2

Сигнал CATCHINT: возврат

Вызов sleep номер 2

Вызов sleep номер 3

Вызов sleep номер 4

Выход

Обратите внимание на то, как передается управление из тела программы процедуре catchint. После завершения процедуры catchint, управление продолжится с точки, в которой программа была прервана. Можно попробовать прервать программу sigex и в другом месте:

$ sigex

Вызов sleep номер 1

Вызов sleep номер 2

<прерывание> (пользователь нажимает на клавишу прерывания)

Сигнал CATCHINT: signo = 2

Сигнал GATCHINT: возврат

Вызов sleep номер 3

Вызов sleep номер 4

Выход



Пример работы с разделяемой памятью: программа shmcopy


В этом разделе создадим простую программу shmcopy для демонстрации практического использования разделяемой памяти. Программа shmcopy просто копирует данные со своего стандартного ввода на стандартный вывод, но позволяет избежать лишних простоев в вызовах fdread и fdwrite. При запуске программы shmcopy создаются два процесса, один из которых выполняет чтение, а другой – запись, и которые совместно используют два буфера, реализованных в виде сегментов разделяемой памяти. Когда первый процесс считывает данные в первый буфер, второй записывает содержимое второго буфера, и наоборот. Так как чтение и запись выполняются одновременно, пропускная способность возрастает. Этот подход используется, например, в программах, которые выводят информацию на ленточный накопитель.

Для согласования двух процессов (чтобы записывающий процесс не писал в буфер до тех пор, пока считывающий процесс его не заполнит) будем использовать два семафора. Почти во всех программах, использующих разделяемую память, требуется дополнительная синхронизация, так как механизм разделяемой памяти не содержит собственных средств синхронизации.

Программа shmcopy использует следующий заголовочный файл share_ex.inc:

(* Заголовочный файл для примера работы с разделяемой памятью *)

const

  SHMKEY1:tkey=$10; (* ключ разделяемой памяти *)

  SHMKEY2:tkey=$15; (* ключ разделяемой памяти *)

  SEMKEY :tkey=$20; (* ключ семафора *)

  

  (* Размер буфера для чтения и записи *)

  BUFSIZ=8192;

  SIZ=5*BUFSIZ;

  

(* В этой структуре будут находиться данные и счетчик чтения *)

type

  databuf=record

    d_nread:integer;

    d_buf:array [0..SIZ-1] of char;

  end;

  pdatabuf=^databuf;

Напомним, что постоянная BUFSIZ определена в файле stdio и задает оптимальный размер порций данных при работе с файловой системой. Шаблон databuf показывает структуру, которая связывается с каждым сегментом разделяемой памяти. В частности, элемент d_nread позволит процессу, выполняющему чтение, передавать другому, осуществляющему запись, через участок разделяемой памяти число считанных символов.




Следующий файл содержит процедуры для инициализации двух участков разделяемой памяти и набора семафоров. Он также содержит процедуру remobj, которая удаляет различные объекты межпроцессного взаимодействия в конце работы программы. Обратите внимание на способ вызова shmat для подключения участков разделяемой памяти к адресному пространству процесса.
(* Процедуры инициализации *)
{$i share_ex.inc}
const
  IFLAGS=IPC_CREAT or IPC_EXCL;
  ERR:pdatabuf=pdatabuf(-1);
var
  shmid1, shmid2, semid : longint;
procedure getseg (var p1,p2:pdatabuf);
begin
  (* Создать участок разделяемой памяти *)
  shmid1 := shmget (SHMKEY1, sizeof (databuf), octal(0600) or IFLAGS);
  if shmid1 = -1 then
    fatal ('shmget');
  shmid2 := shmget (SHMKEY2, sizeof (databuf), octal(0600) or IFLAGS);
  if shmid2 = -1 then
    fatal ('shmget');
  (* Подключить участки разделяемой памяти. *)
  p1 := pdatabuf( shmat (shmid1, 0, 0));
  if p1 = ERR then
    fatal ('shmat');
  p2 := pdatabuf( shmat (shmid2, 0, 0));
  if p2 = ERR then
    fatal ('shmat');
end;
function getsem:longint;     (* Получить набор семафоров *)
var
  x:tsemun;
begin
  x.val := 0;
  (* Создать два набора семафоров *)
  semid := semget (SEMKEY, 2, octal(0600) or IFLAGS);
  if semid = -1 then
    fatal ('semget');
  (* Задать начальные значения *)
  if semctl (semid, 0, SETVAL, x) = -1 then
    fatal ('semctl');
  if semctl (semid, 1, SETVAL, x) = -1 then
    fatal ('semctl');
  getsem:=semid;
end;
(* Удаляет идентификаторы разделяемой памяти
  * и идентификатор набора семафоров
  *)
procedure remobj;
var
  x:tsemun;
begin
  if not shmctl (shmid1, IPC_RMID, nil) then
    fatal ('shmctl');
  if not shmctl (shmid2, IPC_RMID, nil) then
    fatal ('shmctl');
  if semctl (semid, 0, IPC_RMID, x) = -1 then
    fatal ('semctl');
end;
Ошибки в этих процедурах обрабатываются при помощи процедуры fatal, которая использовалась в предыдущих примерах. Она просто вызывает perror, а затем halt.


Ниже следует главная функция для программы shmcopy. Она вызывает процедуры инициализации, а затем создает процесс для чтения (родительский) и для записи (дочерний). Обратите внимание на то, что именно выполняющий запись процесс вызывает процедуру remobj при завершении программы.
(* Программа shmcopy - главная функция *)
uses ipc,stdio,linux;
{$i share_ex.inc}
var
  pid : longint;
  buf1, buf2 : pdatabuf;
begin
  (* Инициализация набора семафоров. *)
  semid := getsem;
  (* Создать и подключить участки разделяемой памяти. *)
  getseg (buf1, buf2);
  pid := fork;
  case pid of
    -1:
      fatal ('fork');
    0:                (* дочерний процесс *)
    begin
      writer (semid, buf1, buf2);
      remobj;
    end;
    else                (* родительский процесс *)
      reader (semid, buf1, buf2);
  end;
  halt (0);
end.
Программа создает объекты межпроцессного взаимодействия до вызова fork. Обратите внимание на то, что адреса, определяющие сегменты разделяемой памяти (которые находятся в переменных buf1 и buf2), будут заданы в обоих процессах.
Процедура reader принимает данные со стандартного ввода, то есть из дескриптора файла 0, и является первой функцией, представляющей интерес. Ей передается идентификатор набора семафоров в параметре semid и адреса двух участков разделяемой памяти в переменных buf1 и buf2.
{$i share_ex.inc}
(* Определения процедур p() и v() для двух семафоров *)
const
  p1:tsembuf=(sem_num:0;sem_op:-1;sem_flg:0);
  p2:tsembuf=(sem_num:1;sem_op:-1;sem_flg:0);
  v1:tsembuf=(sem_num:0;sem_op:1;sem_flg:0);
  v2:tsembuf=(sem_num:1;sem_op:1;sem_flg:0);
(* Процедура reader - выполняет чтение из файла *)
procedure reader(semid:longint;buf1,buf2:pdatabuf);
begin
  while true do
  begin
    (* Считать в буфер buf1 *)
    buf1^.d_nread := fdread (0, buf1^.d_buf, SIZ);
    (* Точка синхронизации *)
    semop (semid, @v1, 1);
    semop (semid, @p2, 1);


    (* Чтобы процедура writer не была приостановлена. *)
    if buf1^.d_nread <= 0 then
      exit;
    buf2^.d_nread := fdread (0, buf2^.d_buf, SIZ);
    semop (semid, @v2, 1);
    semop (semid, @p1, 1);
    if buf2^.d_nread <= 0 then
      exit;
  end;
end;
Структуры sembuf просто определяют операции р() и v() для набора из
двух семафоров. Но на этот раз они используются не для блокировки критических участков кода, а для синхронизации процедур, выполняющих чтение и запись. Процедура reader использует операцию v2 для сообщения о том, что она завершила чтение и ожидает, вызвав semop с параметром р1, пока процедура writer сообщит о завершении записи. Это станет более очевидным при описании процедуры writer. Возможны другие подходы, включающие или четыре бинарных семафора, или семафоры, имеющие более двух значений.
Последней процедурой, вызываемой программой
shmcopy, является процедура writer:
{$i share_ex.inc}
(* Процедура writer - выполняет запись *)
procedure writer(semid:longint;buf1,buf2:pdatabuf);
begin
  while true do
  begin
    semop (semid, @p1, 1);
    semop (semid, @v2, 1);
    if buf1^.d_nread <= 0 then
      exit;
    fdwrite (1, buf1^.d_buf, buf1^.d_nread);
    semop (semid, @p2, 1);
    semop (semid, @v1, 1);
    if buf2^.d_nread <= 0 then
      exit;
    fdwrite (1, buf2^.d_buf, buf2^.d_nread);
  end;
end;
И снова следует обратить внимание на использование набора семафоров для согласования работы процедур reader и writer. На этот раз процедура writer использует операцию v2
для сигнализации и ждет р1. Важно также отметить, что значения buf1^.d_nread и buf2^.d_nread устанавливаются процессом, выполняющим чтение.
После компиляции можно использовать программу
shmcopy при помощи подобной команды:
$ shmcopy < big > /tmp/big
Упражнение 8.6. Усовершенствуйте обработку ошибок и вывод сообщений в программе shmcopy (в особенности для вызовов fdread и fdwrite). Сделайте так, чтобы программа shmcopy принимала в качестве аргументов имена файлов в форме команды cat. Какие последствия возникнут при прерывании программы shmcopy? Можете ли вы улучшить поведение программы?
Упражнение 8.7. Придумайте систему передачи сообщений, использующую разделяемую память. Измерьте ее производительность и сравните с производительностью стандартных процедур передачи сообщений.

Пример работы с семафорами


Теперь продолжим пример, который начали с процедуры initsem. Он содержит две процедуры р() и v(), реализующие традиционные операции над семафорами. Сначала рассмотрим р():

{$i pv.inc}

(* Процедура p.pas - операция p для семафора *)

function p (semid:longint):longint;

var

  p_buf:tsembuf;

begin

  p_buf.sem_num := 0;

  p_buf.sem_op := -1;

  p_buf.sem_flg := SEM_UNDO;

  if not semop (semid, @p_buf, 1) then

  begin

    perror ('ошибка операции p(semid)');

    halt (1);

  end;

  p:=0;

end;

Обратите внимание на то, что здесь использован флаг SEM_UNDO. Теперь рассмотрим текст процедуры v().

{$i pv.inc}

(* Процедура v.pas - операция v для семафора *)

function v (semid:longint):longint;

var

  v_buf:tsembuf;

begin

  v_buf.sem_num := 0;

  v_buf.sem_op := 1;

  v_buf.sem_flg := SEM_UNDO;

  if not semop (semid, @v_buf, 1) then

  begin

    perror ('Ошибка операции v(semid)');

    halt (1);

  end;

  v:=0;

end;

Можно продемонстрировать использование этих довольно простых процедур для реализации взаимного исключения. Рассмотрим следующую программу:

(* Программа testsem - проверка процедур работы с семафорами *)

uses ipc,stdio,linux;

{$i pv.inc}

procedure handlesem (skey:tkey);

var

  semid, pid:longint;

begin

  pid := getpid;

  semid := initsem (skey);

  if semid < 0 then

    halt (1);

  writeln (#$a'Процесс ',pid,' перед критическим участком');

  p (semid);

  writeln ('Процесс ',pid,' выполняет критический участок');

  (* В реальной программе здесь выполняется нечто осмысленное *)

  sleep (10);

  writeln ('Процесс ',pid,' покинул критический участок');

  v (semid);

  writeln ('Процесс ',pid,' завершает работу');

  halt (0);

end;

const

  semkey:tkey = $200;

var

  i:integer;

begin

  for i := 1 to 3 do

    if fork = 0 then

      handlesem (semkey);

end.

Программа testsem порождает три дочерних процесса, которые используют вызовы р() и v() для того, чтобы в каждый момент времени только один из них выполнял критический участок. Запуск программы testsem может дать следующий результат:

Процесс 799 перед критическим участком

Процесс 799 выполняет критический участок

Процесс 800 перед критическим участком

Процесс 801 перед критическим участком

Процесс 799 покинул критический участок

Процесс 801 выполняет критический участок

Процесс 799 завершает работу

Процесс 801 покинул критический участок

Процесс 801 завершает работу

Процесс 800 выполняет критический участок

Процесс 800 покинул критический участок

Процесс 800 завершает работу



Пример управления терминалом: программа tscript


Программа tscript устроена следующим образом: при старте она выполняет вызовы fork и ехес для запуска пользовательской оболочки. Далее все данные, записываемые на терминал оболочкой, сохраняются в файле, при этом оболочка ничего об этом не знает и продолжает вести себя так, как будто она полностью управляет дисциплиной линии связи и, следовательно, терминалом. Логическая структура программы tscript показана на рис. 9.6.

Рис. 9.6. Использование псевдотерминала в программе tscript

Основные элементы схемы:

tscript

Первый запускаемый процесс. После инициализации псевдотерминала и дисциплины линии связи этот процесс использует вызовы fork и ехес для создания оболочки shell. Теперь программа tscript играет две роли. Первая состоит в чтении из настоящего терминала и записи всех данных в порт ведущего устройства псевдотерминала. (Все данные, записываемые в ведущее устройство псевдотерминала, непосредственно передаются на ведомое устройство псевдотерминала.) Вторая роль состоит в чтении вывода программы оболочки shell при помощи псевдотерминала и копировании этих данных на настоящий терминал и в выходной файл

shell

Пользовательская оболочка. Перед запуском процесса shell модули дисциплины линии связи STREAM вставляются в ведомое устройство. Стандартный ввод, стандартный вывод и стандартный вывод диагностики оболочки перенаправляются в ведомое устройство псевдотерминала

Первая задача программы tscript состоит в установке обработчика сигнала SIGCHLD и в открытии псевдотерминала. Затем программа создает процесс shell. И, наконец, вызывается процедура script. Эта процедура отслеживает два потока данных: ввод с клавиатуры (стандартный ввод), который она передает ведущему устройству псевдотерминала, и ввод с ведущего устройства псевдотерминала, передаваемый на стандартный вывод и записываемый в выходной файл.

(* Программа tscript управление терминалом *)

(* Хотя в Linux этот пример и не работает... *)

uses linux,stdio;

var

  dattr:termios;




var

  act:sigactionrec;

  mfd, sfd:longint;

  err:integer;

  buf:array [0..511] of char;

  mask:sigset_t;

begin

  (* Сохранить текущие установки терминала *)

  tcgetattr (0, dattr);

  (* Открыть псевдотерминал *)

  err := pttyopen (mfd, sfd);

  if err <> 1 then

  begin

    writeln (stderr, 'pttyopen: ', err);

    perror ('Ошибка при открытии псевдотерминала');

    halt (1);

  end;

  (* Установить обработчик сигнала SIGCHLD *)

  act.handler.sh := @catch_child;

  sigfillset (@mask);

  act.sa_mask:=mask.__val[0];

  sigaction (SIGCHLD, @act, nil);

  (* Создать процесс оболочки *)

  case fork of

    -1:               (* ошибка *)

    begin

      perror ('Ошибка вызова оболочки');

      halt (2);

    end;

    0:                (* дочерний процесс *)

    begin

      fdclose (mfd);

      runshell (sfd);

    end;

    else         (* родительский процесс *)

    begin

      fdclose (sfd);

      script (mfd);

    end;

  end;

end.

Основная программа использует четыре процедуры. Первая из них называется catch_child. Это обработчик сигнала SIGCHLD. При получении сигнала SIGCHLD процедура catch_child восстанавливает атрибуты терминала и завершает работу.

procedure catch_child (signo:integer);cdecl;

begin

  tcsetattr (0, TCSAFLUSH, dattr);

  halt (0);

end;

Вторая процедура, pttyopen, открывает псевдотерминал.

function pttyopen (var masterfd, slavefd:longint):integer;

var

  slavenm:pchar;

begin

  (* Открыть псевдотерминал -

   * получить дескриптор файла главного устройства *)

  masterfd := fdopen ('/dev/ptmx', Open_RDWR);

  if masterfd = -1 then

  begin

    pttyopen:=-1;

    exit;

  end;

  (* Изменить права доступа для ведомого устройства *)

  if grantpt (masterfd) = -1 then

  begin

    fdclose (masterfd);

    pttyopen:=-2;

    exit;

  end;

  (* Разблокировать ведомое устройство, связанное с mfd *)

  if unlockpt (masterfd) = -1 then

  begin

    fdclose (masterfd);

    pttyopen:=-3;

    exit;

  end;



  (* Получить имя ведомого устройства и затем открыть его *)

  slavenm := ptsname (masterfd);

  if slavenm = nil then

  begin

    fdclose (masterfd);

    pttyopen:=-4;

    exit;

  end;

  slavefd := fdopen (slavenm, Open_RDWR);

  if slavefd = -1 then

  begin

    fdclose (masterfd);

    pttyopen:=-5;

    exit;

  end;

  (* Создать дисциплину линии связи *)

  ioctl (slavefd, I_PUSH, pchar('ptem'));

  if linuxerror>0 then

  begin

    fdclose (masterfd);

    fdclose (slavefd);

    pttyopen:=-6;

    exit;

  end;

  ioctl (slavefd, I_PUSH, pchar('ldterm'));

  if linuxerror>0 then

  begin

    fdclose (masterfd);

    fdclose (slavefd);

    pttyopen:=-7;

    exit;

  end;

  pttyopen:=1;

end;

Третья процедура – процедура runshell. Она выполняет следующие задачи:

–        вызывает setpgrp, чтобы оболочка выполнялась в своей группе процессов. Это позволяет оболочке полностью управлять обработкой сигналов, в особенности в отношении управления заданиями;

–        вызывает системный вызов dup2 для перенаправления дескрипторов stdin, stdout и stderr на дескриптор файла ведомого устройства. Это особенно важный шаг;

–        запускает оболочку при помощи вызова ехес, которая выполняется до тех пор, пока не будет прервана пользователем.

procedure runshell (sfd:longint);

begin

  setpgrp;

  dup2 (sfd, 0);

  dup2 (sfd, 1);

  dup2 (sfd, 2);

  execl ('/bin/sh -i');

end;

Теперь рассмотрим саму процедуру script. Первым действием процедуры script является изменение дисциплины линии связи так, чтобы она работала в режиме прямого доступа. Это достигается получением текущих атрибутов терминала и изменением их при помощи вызова tcsetattr. Затем процедура script открывает файл output и использует системный вызов select (обсуждавшийся в главе 7) для обеспечения одновременного ввода со своего стандартного ввода и ведущего устройства псевдотерминала. Если данные поступают со стандартного ввода, то процедура script передает их без изменений ведущему устройству псевдотерминала. При поступлении же данных с ведущего устройства псевдотерминала процедура script записывает эти данные в терминал пользователя и в файл



output.

procedure script(mfd:longint);

var

  nread, ofile:longint;

  _set, master:fdset;

  attr:termios;

  buf:array [0..511] of char;

begin

  (* Перевести дисциплину линии связи в режим прямого доступа *)

  tcgetattr (0, attr);

  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);

  (* Открыть выходной файл *)

  ofile := fdopen ('output', Open_CREAT or Open_WRONLY or Open_TRUNC, octal(0666));

  (* Задать битовые маски для системного вызова select *)

  FD_ZERO (master);

  FD_SET (0, master);

  FD_SET (mfd, master);

  (* Вызов select осуществляется без таймаута,

   * и будет заблокирован до наступления события. *)

  _set := master;

  while select (mfd + 1, @_set, nil, nil, nil) > 0 do

  begin

    (* Проверить стандартный ввод *)

    if FD_ISSET (0, _set) then

    begin

      nread := fdread (0, buf, 512);

      fdwrite (mfd, buf, nread);

    end;

    (* Проверить главное устройство *)

    if FD_ISSET (mfd, _set) then

    begin

      nread := fdread (mfd, buf, 512);

      write (ofile, buf, nread);

      write (1, buf, nread);

    end;

    _set := master;

  end;

end;

Следующий сеанс демонстрирует работу программы tscript. Комментарии, обозначенные символом #, показывают, какая из оболочек выполняется в данный момент.

$ ./tscript

$ ls -l tscript     # работает новая оболочка

-rwxr-xr-x   1 spate    fcf 6984 Jan 22 21:57 tscript

$ head -2 /etc/passwd # выполняется в новой оболочке

root:х:0:1:0000-Admin(0000):/:/bin/ksh

daemon:x:1:1:0000-Admin(0000):/:

$ exit         # выход из новой оболочки

$ cat output       # работает исходная оболочка

-rwxr-xr-x   1 spate     fcf 6984 Jan 22 21:57 tscript

root:х:0:1:0000-Admin(0000):/:/bin/ksh

daemon:x:1:1:0000-Admin(0000):/:

Упражнение 9.5. Добавьте к программе обработку ошибок и возможность задания в качестве параметра имени выходного файла. Если имя не задано, используйте по умолчанию имя output.

Упражнение 9.6. Эквивалентная стандартная программа UNIX script позволяет задать параметр -а, который указывает на необходимость дополнения файла output (содержимое файла не уничтожается). Реализуйте аналогичную возможность в программе tscript.


Пример восстановление прежнего действия


Как упоминалось выше, в структуре sigaction может быть заполнен третий параметр oact. Это позволяет сохранять и восстанавливать прежнее состояние обработчика сигнала, как показано в следующем примере:

uses linux;

var

  act, oact: sigactionrec;

(* Сохранить старый обработчик сигнала SIGTERM *)

sigaction(SIGTERM, nil, @oact);

(* Определить новый обработчик сигнала SIGTERM *)

act.handler.sh := SIG_IGN;

sigaction(SIGTERM, @act, nil);

(* Выполнить какие-либо действия *)

(* Восстановить старый обработчик *)

sigaction(SIGTERM, @oact, nil);



. Приоритеты процессов


Система приближенно вычисляет долю процессорного времени, отводимую для работы процесса, руководствуясь значением

nice (буквально «дружелюбный»). Значения nice находятся в диапазоне от нуля до максимального значения, зависящего от конкретной системы. Чем больше это число, тем ниже приоритет процесса. Процессы, «дружелюбно настроенные», могут понижать свой приоритет, используя системный вызов nice. Этот вызов имеет один аргумент, положительное число, на которое увеличивается текущее значение nice, например:

nice(5);

Процессы суперпользователя (и только суперпользователя) могут увеличивав свой приоритет, используя отрицательное значение параметра вызова nice. Вызов nice может пригодиться, если есть необходимость, например, вычислить число ? с точностью до ста миллионов знаков, не изменяя существенно время реакции системы для остальных пользователей. Вызов nice был введен давно. Современные факультативные расширения реального времени стандарта POSIX

определяют гораздо более точное управление планированием параллельной работы процессов.



Процедура sprintf


Прежде всего нужно отметить еще один момент, касающийся процедуры sprintf. Дело в том, что не следует думать о процедуре sprintf как о процедуре вывода. На самом деле она представляет собой наиболее гибкую из библиотечных процедур, работающих со строками и преобразующих форматы данных. Следующий текст показывает использование этой функции:

(* Процедура genkey - генерация ключа базы данных *)

uses stdio;

(* Длина ключа всегда будет равна 20 символам *)

function genkey (buf:pchar; const suppcode:pchar; orderno:longint):pchar;

begin

  (* Проверка размера ключа *)

  if strlen (suppcode) <> 10 then

  begin

    genkey:=nil;

    exit;

  end;

  sprintf (buf, '%s_%.9d', [suppcode, orderno]);

  genkey:=buf;

end;

var

  buf:array [0..99] of char;

begin

  if genkey (buf, 'supplement', 12)<>nil then

    printf ('Key: %s'#$a, [pchar(buf)]);

end.

Тогда вызов процедуры genkey

printf('%s'#$a, [genkey(buf, 'abcdefghij', 12)]);

выведет такую строку ключа:

abcdefghij_000000012



Процедуры sigsetjmp и siglongjmp


Иногда при получении сигнала необходимо вернуться на предыдущую позицию в программе. Например, может потребоваться, чтобы пользователь мог вернуться в основное меню программы при нажатии клавиши прерывания. Это можно выполнить, используя две специальные функции sigsetjmp и siglongjmp. (Существуют альтернативные функции setjmp и longjmp, но их нельзя использовать совместно с обработкой сигналов.) Процедура sigsetjmp «сохраняет» текущие значения счетчика команд, позиции стека, регистров процессора и маски сигналов, а процедура siglongjmp передает управление назад в сохраненное таким образом положение. В этом смысле процедура siglongjmp аналогична оператору goto. Важно понимать, что возврат из процедуры siglongjmp не происходит, так как стек возвращается в сохраненное состояние. Как будет показано ниже, при этом происходит выход из соответствующего вызова sigsetjmp.



Процесс


В терминологии UNIX термин процесс (process) обычно обозначает экземпляр выполняющейся программы. Простейший способ создания процесса - передать команду для исполнения оболочке

(shell), которая также называется командным интерпретатором (command processor). Например, если пользователь напечатает:

$ ls

то процесс оболочки создаст другой процесс, в котором будет выполняться программа ls, выводящая список файлов в каталоге. UNIX – многозадачная система, поэтому несколько процессов могут выполняться одновременно. Фактически для каждого пользователя, работающего в данный момент с системой UNIX, выполняется хотя бы один процесс.



Программа etest


(* Программа etest - ввод имен объектов в очередь. *)

{$mode objfpc}

uses ipc,linux,stdio,sysutils;

{$i q.inc}

var

  priority:longint;

begin

  if paramcount <> 2 then

  begin

    writeln(stderr, 'Применение: ',paramstr(0),' имя приоритет');

    halt (1);

  end;

  try

    priority:=strtoint(paramstr(2));

  except

    on e:econverterror do

    begin

      warn ('Нечисловой приоритет');

      halt (2);

    end;

  end;

  if (priority <= 0) or (priority > MAXPRIOR) then

  begin

    warn ('Недопустимый приоритет');

    halt (2);

  end;

  if not enter (paramstr(1), priority) then

  begin

    warn ('Ошибка в процедуре enter');

    halt (3);

  end;

  halt (0);

end.



Программа io


В качестве очень простого примера использования стандартных дескрипторов файлов приведем программу io, применяющую системные вызовы fdread и fdwrite и дескрипторы файлов со значениями 0 и 1 для копирования стандартного ввода в стандартный вывод. В сущности, это усеченная версия программы UNIX cat. Обратите внимание на отсутствие вызовов fdopen и fdcreat.

(* Программа io копирует стандартный ввод *)

(* в стандартный вывод *)

uses linux;

const

  SIZE=512;

var

  nread:longint;

  buf:array [0..SIZE-1] of byte;

begin

  nread := fdread (0, buf, SIZE);

  while nread > 0 do

  begin

    fdwrite (1, buf, nread);

    nread := fdread (0, buf, SIZE);

  end;

  halt(0);

end.

Предположим, что исходный код этой программы находится в файле iо.pas, который компилируется для получения исполняемого файла io:

$ fpc io.pas

Если теперь запустить программу io на выполнение, просто набрав имя файла программы, то она будет ожидать ввода с терминала. Если пользователь напечатает строку и затем нажмет клавишу Return

или Enter на клавиатуре, то программа iо просто выведет на дисплей напечатанную строку, то есть запишет строку в стандартный вывод. При этом диалог с системой в действительности будет выглядеть примерно так:

$ io          Пользователь печатает io и нажимает Return

Это строка 1  Пользователь печатает строку и нажимает Return

Это строка 1  Программа io

выводит строку на дисплей

.

.

.

После вывода строки на экран программа io будет ожидать дальнейшего ввода. Пользователь может продолжать печатать, и программа io

будет послушно выводить каждую строку на экран при нажатии на клавишу Return или Enter.

Для завершения программы пользователь может напечатать строку из единственного символа конца файла. Обычно это символ ^D, то есть Ctrl+D, который набирается одновременным нажатием клавиш Ctrl и D. При этом вызов fdread вернет 0, указывая на то, что достигнут конец файла. Весь диалог с системой мог бы выглядеть примерно так:

$ io

Это строка 1

Это строка 1




Это строка 2

Это строка 2

<Ctrl-D>          Пользователь печатает Ctrl+D

$

Обратите внимание, что программа io ведет себя не совсем так, как можно было бы ожидать. Вместо того чтобы считать все 512 символов до начала вывода на экран, как, казалось бы, следует делать, она выводит строку на экран при каждом нажатии клавиши Return. Это происходит из-за того, что вызов fdread, который использовался для ввода данных с терминала, обычно возвращает значение после каждого символа перевода строки для облегчения взаимодействия с пользователем. Если быть еще более точным, это будет иметь место только для обычных настроек терминала. Терминалы могут быть настроены в другом режиме, позволяя осуществлять, например, посимвольный ввод. Дополнительные соображения по этому поводу изложены в главе 9.

Поскольку программа io

использует стандартные дескрипторы файлов, к ней можно применить стандартные средства оболочки для перенаправления и организации конвейеров. Например, выполнение команды

$ io < /etc/motd > message

приведет к копированию при помощи программы io сообщения с цитатой дня команды /etc/motd в файл message, а выполнение команды

$ io < /etc/motd | wc

направит стандартный вывод программы io в утилиту UNIX для подсчета числа слов wc. Так как стандартный вывод программы io

будет фактически идентичен содержимому /etc/motd, это просто еще один (более громоздкий) способ подсчета слов, строк и символов в файле.

Упражнение 2.15. Напишите версию программы io, которая проверяет наличие аргументов командной строки. Если существует хотя бы один из них, то программа должна рассматривать каждый из аргументов как имя файла и копировать содержимое каждого файла в стандартный вывод. Если аргументы командной строки отсутствуют, то ввод должен осуществляться из стандартного ввода. Как должна действовать программа io, если она не может открыть файл?

Упражнение 2.16. Иногда данные в файле могут медленно накапливаться в течение продолжительного промежутка времени. Напишите версию программы io с именем watch, которая будет выполнять чтение из стандартного ввода до тех пор, пока не встретится символ конца файла, выводя данные на стандартный вывод. После достижения конца файла программа watch должна сделать паузу на пять секунд, а затем снова начать чтение стандартного ввода, чтобы проверить, не поступили ли новые данные, не открывая при этом файл заново и не изменяя положение указателя чтения-записи. Для прекращения работы процесса на заданное время вымажете использовать стандартную библиотечную процедуру delay, которая имеет единственный аргумент – целое число, задающее продолжительность ожидания в миллисекундах. Например, вызов

sleep(5000);

заставляет процесс прекратить работу на 5 секунд. Программа watch аналогична программе readslow, существующей в некоторых версиях UNIX. Посмотрите также в руководстве системы описание ключа -f команды tail.