Порядок выполнения работы




Создадим текстовый файл hello.c со следующим программным кодом на языке Си:

main() {printf (“Hello World!\n”);}

Далее откомпилируем его коммандой:

cc hello.c

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

cc -o hello.exe hello.c

После этого будет создан двоичный файл hello.exe, который может быть запущен на выполнение.

Рассмотрим основные системные вызовы, связанные с проведением операций ввода-вывода, которые наиболее часто используются в операционной системе UNIX. Ввод-вывод в ОС UNIX является крайне простой операцией и налагает на пользователя минимум ограничений.

Системный вызов ореn (открыть файл) имеет следующий формат:

#include <sys/file.h>

open(name, flags, mode)

char *name;

int flags, mode;

Системный вызов open открывает файл с именем name для чтения и/или записи. Режим открытия файла определяется значением параметра flags. Это значение может быть задано как результат логического сложения следующих признаков (в любой комбинации):

0_RDONLY - открыть только для чтения.

0_WRONLY -открыть только для записи.

0_RDWR - открыть для чтения и записи.

0_NDELAY - не блокировать при открытии. Если операция открытия задерживается по каким-либо причинам, например, при отсутствии готовности линии связи, процесс не приостанавливается. Возвращается код ошибки.

0_АРРЕND - открыть для дозаписи. Обычно, при открытии файла, указатель текущей позиции чтения/записи устанавливается на его начало, но, если задан режим 0_АРРЕND, этот указатель устанавливается на конец файла.

0_СRЕАТ - создать файл, если он не существует.

0_TRUNC - сократить размер файла. В режиме 0_ТRUNC, если указанный файл существует, его размер усекается до нуля.

0_ЕХСL - выдавать ошибку при попытке создания существующего файла. (Этот признак используется в сочетании с признаком 0_СRЕАТ). Режим может применяться для организации простого механизма блокировки.

В случае успешного завершения вызова Ореn, возвращается дескриптор открытого файла, иначе - значение -1 и в переменную еrrnо записывается код ошибки.

Системный вызов сrеаt (создать файл) имеет следующий формат:

сreat(name,mode)

сhar *name;

int mode;

Функция сгеаt создает новый (или подготавливает к повторной записи уже существующий) файл с именем name. Если файл еще не существует, значение параметра mode используется для формирования режимов доступа создаваемого файла, при этом учитывается значение маски режимов процесса. Значение параметра mode составляется по правилам, приведенным в описание системного вызова chmod. Если указанный файл существует, то его владелец и режим доступа остаются прежними, а файл усекается до нулевой длины. Кроме того, файл открывается для записи и возвращается его дескриптор.

При неудачном завершении, операции сrеаt возвращается значение -1, в остальных случаях возвращается дескриптор открытого файла (целое положительное число).

Параметр mode задается произвольно, в нем не обязательно должно быть разрешение на запись. Эта возможность используется программами, которые работают с временными файлами с фиксированными именами. Создание производится с режимом, запрещающим запись. Затем, если другая программа пытается выполнить вызов сrеаt, возвращается ошибка и программе становится известно, что в данный момент это имя использовать нельзя. Все действия, перечисленные для вызова сrеаtе, можно выполнить с помощью системного вызова ореn. Вызов сrеаtе оставлен для совместимости с ранними версиями ОС Unix.

Системный вызов rеаd ( чтение файла) имеет следующий формат:

read (fd, buf, nbytes)

char *buf;

int fd, nbytes;

Системный вызов read обеспечивает считывание nbytes данных в массив buf из файла с дескриптором fd. Информация читается из файла по текущему указателю позиции чтения/записи. После завершения пересылки данных значение указателя увеличивается на число считанных байт. Для некоторых файлов значение указателя позиции чтения/записи не имеет смысла (например, для терминала), тем не менее, данные передаются. При успешном завершении вызова, возвращается число считанных байт, в случае ошибки - значение -1, при достижении конца файла в процессе чтения - число 0.

Системный вызов write (запись в файл) имеет следующий формат:

write (fd, buf, nbytes)

char *buf;

int fd, nbytes;

Системный вызов write записывает nbytes данных из массива buf в файл с дескриптором fd. Информация записывается в файл по текущему указателю позиции чтения/записи. После завершения пересылки данных, значение указателя увеличивается на число записанных байт. Для некоторых файлов значение указателя позиции чтения/записи не имеет смысла, (например, для терминала), тем не менее, данные передаются.

Если статус файла, в который записывается информация, содержит признак set-UID и процесс работает в непривилегированном режиме, данный признак удаляется (в целях защиты информации). При успешном завершении вызова возвращается число записанных байт, в случае ошибки - значение -1.

Системный вызов сlose (закрыть файл) имеет следующий формат:

аns = сlоsе (fd)

int fd;

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

При порождении нового процесса все его дескрипторы указывают на те же объекты, что и дескрипторы процесса-предка. После выполнения вызова ехесvе в порожденном процессе, новая программа также наследует все активные дескрипторы. Для того, чтобы сделать недоступными новой программе уже открытые файлы, соответствующие дескрипторы можно переопределить с помощью dup2 или удалить с помощью системного вызова unlink. Однако бывают ситуации, в которых уже открытые файлы могут потребоваться при неудачном завершении системного вызова execvе. В таких случаях, применение вызова fcntl обеспечивает закрытие определенных файлов после успешного старта новой программы. В случае успешного завершения, системный вызов с1оsе возвращает значение 0, иначе - значение -1 и код ошибки в переменной еrrnо. Код ошибки:

[ЕВАDF] - указанный дескриптор не является дескриптором открытого файла.

Системный вызов lsееk (установка указателя чтения/записи) имеет следующий формат:

#define L_SЕТ 0 /* установка * /

# define L_INCR 1 /* смещение */

# define L_ХТND 2 /* увеличение размера файла */

long lseek (fd, offset, whence)

int fd, whence;

long offset;

Системный вызов lsееk изменяет значение указателя позиции чтения/записи дескриптора fd следующим образом:

- если значение параметра whence равно L_SЕТ, то указателю присваивается значение параметра;

- если значение параметра whence равно L_INCR, значение указателя увеличивается на значение offset;

- если значение параметра whenc e равно L_XTND, то указателю присваивается значение (offset + fsize), где fsize- размер файла.

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

Системный вызов fork (порождение нового процесса) имеет следующий формат:

pid = fork()

int pid;

Системный вызов fork служит для создания нового процесса. Новый процесс (процесс-потомок) является полной копией процесса-предка, за исключением того, что процесс-потомок имеет свой уникальный идентификатор процесса (РID). Поскольку новый процесс сам может выступать в качестве порождающего, его идентификатор процесса-предка (РРID), естественно, отличается от соответствующего идентификатора породившего его процесса.

Новый процесс имеет собственную копию таблицы дескрипторов открытых файлов, но эти дескрипторы ссылаются на те же самые файлы, что и дескрипторы процесса-предка, и имеют с ним общие указатели позиций чтения/записи. Так, при выполнении операции 1sееk в порожденном процессе, может измениться и позиция чтения/записи файла в процессе-предке. Это свойство, в частности, используется командными языками SН СSН для переопределения файлов стандартного ввода/вывода и организации конвейеров команд. Счетчики использованных ресурсов порожденного процесса инициализируются нулевыми значениями. В случае успешного завершения, системный вызов fork возвращает значение 0 порожденному процессу и идентификатор нового процесса - породившему. При неудачном завершении вызова возвращается значение -1.

Системный вызов wait (ожидание завершения процесса-потомка) имеет следующий формат:

pid = wait (status)

int pid;

int *status;

pid = wait(0)

int = pid;

Системный вызов wait задерживает вызвавший его процесс до поступления сигнала или завершения одного из порожденных им процессов. Если какой-нибудь порожденный процесс завершился в период времени с момента последней выдачи производится немедленный возврат. Если порожденные процессы отсутствуют, производится немедленный возврат с установкой бита ошибки (соответственно, с возвратом -1). При нормальном возврате, системный вызов wait получает идентификатор завершившегося порожденного процесса. В случае нескольких порожденных процессов нужно произвести несколько вызовов wait, чтобы отследить признаки завершения всех порожденных процессов. При успешном завершении вызова wai, значение status всегда не равно нулю и содержит код возврата и статус завершения порождённого процесса. Если порождающий процесс завершается, не ожидая своих потомков, их наследует стартовый процесс (РID=1). Вызов автоматически перезапускается, если процесс получает сигнал во время ожидания своих потомков. При нормальном завершении выдается идентификатор завершившегося процесса. В случае ошибки - значение -1 и код ошибки в переменной errnо.

Системный вызов exit (завершить процесс) имеет следующий формат:

exit (arg)

int arg;

Параметр arg имеет смысл, определенный в описании вызова wait.

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

Пример использования системных вызовов fork, wait, exit

main ()

{

int pid, status, died;

switch (pid=fork()) /* порождаем новый процесс */

{

саsе -1: /* аварийное завершение*/

printf (“Can’t fork\n”);

ехit(-1);

саsе 0: /* процесс-потомок */

рrintf (“I am the child\n”);

exit (3);

default: /* процесс предок ожидает окончание процесса-потомка */

died = wait (&status);

}

printf (“Child was %d\n”, pid);

printf (“ %d died \n”, died);

printf (“Exit value %d\n”, status>>8);

printf (“Exit status %d\n”, status &0377);

exit (0);

}

Системные вызовы ехес1, ехес1е, ехесv и ехесvе (выполнить файл) имеют форматы:

execl (name, arg0, arg1, arg2, …, 0)

char *name;

char *arg0, *arg1, *arg2, …

execle (name, arg0, arg1, arg2, …, 0, envp)

char *name;

char *arg0, *arg1, *arg2, …

charn *envp[ ];

execv (name, argv)

char *name, *arg[ ];

execve (name, argv, envp)

char *name, *arg[ ], *envp[ ];

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

Пример использования системных вызовов fork, wait, exit, execl.

main()

{

int pid, status;

switch(pid=fork())/* порождаем новый процесс */

{

case –1: /* аварийное завершение */

printf (“Can’ t fork\n”);

exit(1);

case 0: /* процесс-потомок */

printf (“I am the child\n”);

execl (“/bin/echo”, “echo”, “Hello mike!”, 0);

/* запускаем команду echo “Hello mike!” */

printf (“Exec error\n”); /* процесс предок */

wait (&status);

/* ожидаем завершение процесса-потомка */

}

/* Процесс-отец выводит на экран код завершения и статус завершения

процесса-потомка */

printf (“Exit value %d\n”, status>>8);

printf (“Exit status %d\n, status & 0377);

exit(0);

}

 

Системный вызов nice (установить процессу приоритет) имеет следующий формат:

nice (incr)

Системный вызов nice устанавливается текущему процессу новый приоритет, равный текущему приоритету плюс значение аргумента incr. Значение аргумента incr должно лежать в определенном диапазоне, конкретные границы которого зависят от версии ОС UNIX.

Пример использования системного вызова nice:

#inc1ude <stdio.h>

#include<ctype.h>

main (argc, argv) int argc; char *argv [];

{

int nicarg = 10; /* приращение приоритета по умолчанию */

extern errno;

/* Разбираем аргументы команды */

if(argc > 1 && argv[l][0] = ‘-‘) { /* если задано приращение приоритета */

register char *p = argv[l];

if(*++p!= ‘-‘) --р;

whi1e(*++p)

if(!isdigit(*p)) { /* указано не числовое значение */

fprintf(stderr, "nice: argument must be numeric.\n");

exit(2);

}

nicarg == atoi (&argv[l] [1]);

/* в переменной nicarg записываем значение приращения приоритета */

argc--;

argv++;

}

if (argc < 2) { /* если не задана команда */

fprintf(stderr, "nice: usage: nice [-num] command\n");

exit(2);

}

nice(nicarg); /* изменяем приоритет процесса */

execvp(argv [1], &argv[l]); /* выполняем команду */

fprintf(stderr. "%s: %d\n", argv[l], errno);

/* если ошибка при запуске команды, то печатаем переменную errno */

exit(2);

}



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2017-11-19 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: