Файл: Учебнометодическое пособие Томск 2016 2 удк 004. 451(075. 8) Ббк 32. 973. 2018. 2я73 к 754 Рецензенты.pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 07.11.2023
Просмотров: 340
Скачиваний: 2
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
Выполните приведенные выше программы.
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
8.3.5 Создание файла
Результатом выполнения операции создания файла является или логиче- ское имя файла или признак ошибки.
Системные вызовы. Для создания файла может быть использован один из двух системных вызовов: open и creat. Вызов creatпредставляет собой традиционный способ создания файла. Его описание: int creat(const char *pathname, mode_t mode);
Первый параметр (pathname) является указателем на символьную стро- ку, содержащую имя физического файла. Параметр mode задает права доступа к файлу. Обычно это 3-значное восьмеричное число, старшая цифра которого определяет права доступа владельца файла, средняя цифра – владельца-группы, а младшая цифра задает права доступа всех остальных пользователей. Напри- мер, восьмеричное число (в Си восьмеричное число начинается с нуля) 0764 за- дает право доступа rwx для владельца, rw – для владельца-группы, r – для всех остальных пользователей. Пример использования вызова creat: fil = creat(“/tmp/newfile”, 0764);
227
В случае успешного завершения системный вызов creat возвращает но- мер нового файла, открытого на запись. А в случае ошибки возвращает значе- ние –1. Для того чтобы только что созданный файл был открыт для чтения, его необходимо сначала закрыть, а затем открыть вновь.
Если файл, заданный в качестве первого параметра creat уже существу- ет, то, во-первых, второй параметр вызова creat игнорируется. А во-вторых, данный файл усекается до нулевой длины и, следовательно, прежняя информа- ция файла уничтожается.
Рассмотрим применение для создания файла системного вызова open, который предоставляет больше возможностей по сравнению с вызовом creat.
Для этого, во-первых, в состав второго операнда вызова open обязательно должна быть включена константа O_CREAT. Во-вторых, данный вызов должен иметь третий операнд – mode, аналогичный операнду вызова creat.
Одним из отличий создания файла при помощи open является то, что но- вый файл сразу же оказывается открытым заданным способом. Для этого в со- став второго операнда кроме O_CREAT включается константа O_RDONLY,
O_WRONLY или O_RDWR. Естественно, что запрашиваемый доступ к файлу не должен выходить за рамки прав доступа, задаваемых параметром mode.
Иначе вызов open возвратит признак ошибки. Например, следующий вызов предназначен для создания файла, а также для его открытия на чтение и запись: fil = open(“/tmp/newfile”, O_RDWR| O_CREAT, 0760);
Третья константа, включаемая в состав второго операнда вызова open, определяет поведение этого вызова, если файл с заданным именем уже суще- ствует. В частности, если данная константа опущена, то существующий файл будет открыт на запись (если позволяют права доступа к нему), а параметр mode игнорируется.
Если в состав второго операнда включена константа O_EXCL (exclusive – исключительный), то в случае существования файла вызов open завершится с ошибкой. Пример такого вызова: fil = open(“abc”, O_WRONLY | O_CREAT| O_EXCL, 0760);
Если в состав второго операнда включена константа O_TRUNC, то в слу- чае существования файла он будет усечен до нулевого размера (если позволяют права доступа). Пример такого вызова: fil = open(“abc”, O_WRONLY | O_CREAT| O_TRUNC, 0760);
228
Функции стандартной библиотеки. Для создания файла используется описанная ранее функция fopen, в качестве второго параметра которой задает- ся строка w или a. Каждая из этих строк приводит к созданию нового файла, от- крытого на запись. Различие между ними проявляется в том случае, если уже существует файл с заданным именем. Строка w приводит к усечению этого файла до нулевой длины, а строка a ограничивается открытием существующего файла.
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
Разработайте программу создания файла, получающую имя файла из командной строки. После выполнения программы про- верьте наличие созданного файла.
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
8.3.6 Указатель файла
После того как файл открыт любым из изложенных выше способов, про- граммный процесс может выполнять информационный обмен с ним, то есть выполнять или чтение данных из файла, или запись данных в файл. Для каждо- го открытого файла ядро содержит отдельную переменную, называемую указа- телем файла, которая содержит номер того байта файла, начиная с которого бу- дет выполняться последующее чтение файла или запись в него. Поэтому прежде чем выполнять операцию чтения (записи) файла, необходимо четко представлять себе, номер какого байта находится в указателе файла. Например, в результате обычного открытия файла его указатель содержит 0. Если же во второй операнд вызова open добавить константу O_APPEND, то указатель фай- ла будет установлен на его конец.
Системные вызовы. Системный вызов lseek позволяет установить указатель файла на любую позицию. Его описание:
#include
#include off_t lseek(int fil, off_t offset, int whence);
При успешном завершении вызов lseek возвращает новое значение ука- зателя файла. В случае ошибки: –1. Первый параметр (fil) – номер файла, от- крытого ранее. Второй параметр (offset) задает требуемое смещение относи- тельно той позиции указателя файла, которая задается третьим параметром
229
(whence), который можно задать в виде одной из следующих трех констант, определенных в файле <unistd.h>:
SEEK_SET – смещение прибавляется к началу файла;
SEEK_CUR – смещение прибавляется к текущему значению указателя;
SEEK_END – смещение прибавляется к концу файла.
Несмотря на то что тип смещения и тип значения указателя файла один и тот же – off_t (он определен в файле <sys/types.h>), область допусти- мых значений для каждого из них различна. В отличие от указателя файла, принимающего лишь неотрицательные целые значения, смещение может быть и отрицательным.
Вообще говоря, вызов lseekвыдаст признак ошибки для существующе- го файла только в том случае, если мы попытаемся установить указатель файла на позицию, предшествующую началу файла. И наоборот, операция установки указателя на позицию, расположенную далее конца файла, приведет к появле- нию в файле «дыры», заполненной нулями.
Пример использования вызова lseek: off_t newpos; newpos = lseek(fil, (off_t)-16, SEEK_END);
В данном примере новое значение указателя файла будет на 16 меньше, чем значение, соответствующее концу файла. Обратите внимание, что для чис- ла (–16), задающего смещение, используется явное задание типа (off_t).
Интересно отметить, что вызов lseek можно использовать для опреде- ления длины файла. Например, пусть переменная filsize содержит длину файла, тогда: off_t filsize; int fil; filsize = lseek(fil, (off_t) 0, SEEK_END);
Функции стандартной библиотеки. Установку указателя файла выпол- няет стандартная функция fseek. Ее описание:
#include int fseek(FILE *stream, long offset, int whence);
230
Функция возвращает ненулевое значение только в случае ошибки. Пер- вый параметр (stream) идентифицирует файл. Назначение двух других пара- метров аналогично вызову seek.
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
Создайте с помощью текстового редактора небольшой файл, а затем с помощью своей программы образуйте в этом файле «ды- ру», наличие которой проверьте с помощью текстового редактора
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
8.3.7 Чтение из файла
После того как указатель файла установлен в требуемое место файла, можно выполнить чтение из этого файла требуемого числа байтов.
Системные вызовы. Чтение из файла выполняет системный вызов read.
Его описание:
#include ssize_t read(int fil, void *buffer, size_t n);
Данный вызов выполняет чтение из файла, номер которого задается пер- вым параметром (fil), такого числа байтов, которое задается третьим пара- метром (n). Считанные байты помещаются в буфер, указатель на который зада- ется вторым параметром (buffer). Буфер представляет собой массив, элементы которого имеют произвольный тип (void). (Чаще всего буфер оформляется как массив символьного типа.)
Если выполнение readзавершилось успешно, то он возвращает число байтов, считанных из файла. Это число равно n или меньше его. Второе выпол- няется тогда, когда мы выполняем чтение из конечной части файла, содержа- щей меньше символов, чем запрашивается. Если после этого опять выполнить read
, то он возвратит число 0, означающее, что достигнут конец файла.
В случае ошибки read возвращает –1.
Одним из результатов выполнения read является перемещение указателя файла на первый, несчитанный байт файла или на его конец.
· · · · · · · · · · · · · · · · · · · · · · · · ·
Пример
· · · · · · · · · · · · · · · · · · · · · · · · ·
Следующая программа выполняет подсчет байтов в файле, имя которого передается из командной строки:
#include /* Для вызова exit */
231
#include
#include
#define BUFSIZE 512 main(int argc, char *argv[])
{ int fil; char buffer[BUFSIZE]; ssize_t nread; long total = 0; if (argc !=2)
{ printf(“Need 1 argument for open\n”); exit(1); /* Выход по ошибке */
}
/* Открыть файл для чтения */ if ((fil = open(argv[1], O_RDONLY)) = = -1)
{ printf(“Cannot open file %s\n”, argv[1]); exit(1); /* Выход по ошибке */
}
/* Повторять до конца файла */ while((nread = read(fil, buffer, BUFSIZE)) >0) total=total+nread; printf(“total = %ld\n”, total); exit(0);
}
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
Поясним, почему длина буфера взята равной 512 байтам. Дело в том, что каждый системный вызов readинициирует считывание с диска блока байтов.
Величина этого блока есть число 512, умноженное на степень двойки, и зависит от системы. Если считывать данные с диска небольшими порциями (по не- сколько байтов), то никаких изменений в результатах работы программы не бу- дет. Единственное – резко возрастет время выполнения программы, так как, во- первых, чтение каждой порции байтов требует считывания в лучшем случае из дискового кэша, а в худшем – с диска целого блока. А во-вторых, большое ко- личество системных вызовов приводит к такому же числу переключений ЦП из
232 режима «задача» в режим «ядро» и обратно, что требует заметных затрат вре- мени ЦП.
Функции стандартной библиотеки. Применение для чтения из файла стандартных библиотечных функций позволяет не заботиться об эффективно- сти информационного обмена. Так как, во-первых, эти функции занимаются буферизацией обмена с файлом. Поэтому запрос из программы очередной пор- ции байтов не приводит неизбежно к считыванию блока файла. Если эта порция находится в том блоке, который был считан последним с диска, то требуемые байты стандартная функция находит в своем буфере, откуда она и копирует их в буфер программы. Во-вторых, функции стандартной библиотеки выполняют- ся в режиме «задача» и поэтому их вызов не приводит к переключению процес- сора.
Примером стандартной функции чтения файла является функция чтения символа getc. Ее описание:
#include int getc(FILE *istream);
Данная функция возвращает или код символа или число –1, которое означает «конец файла» (EOF). Единственный параметр функции (istream) представляет собой указатель на структуру FILE. Этот параметр должен быть получен в результате открытия файла в программе (до вызова функции getc).
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
Выполните приведенную выше программу подсчета симво- лов.
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
8.3.8 Запись в файл
После того как указатель файла установлен в требуемое место файла, можно выполнить запись в файл требуемого числа байтов. Если указатель фай- ла был установлен на конец файла, то запись будет выполняться на свободное место за счет увеличения длины файла. Иначе – запись выполняется «поверх» прежнего содержимого файла.
Системные вызовы. Запись в файл выполняет системный вызов write.
Его описание:
#include ssize_t write(int fil, void *buffer, size_t n);
233
Данный вызов выполняет запись в файл, номер которого задается первым параметром (fil), такого числа байтов, которое задается третьим параметром
(n). Запись в файл выполняется из буфера, указатель на который задается вто- рым параметром (buffer). Буфер представляет собой массив, элементы кото- рого имеют произвольный тип (void).
Если выполнение write завершилось успешно, то он возвращает число байтов, записанных в файл. Почти всегда это число равно n. В случае ошибки write возвращает –1.
Одним из результатов выполнения write является перемещение указа- теля файла на первый байт после записанного участка файла.
В качестве примера приведем фрагмент программы, выполняющий за- пись содержимого буфера outbuf, имеющего длину OBSIZE, в файл с именем abc: fil = open(“abc”, O_RDWR| O_APPEND); write(fil, outbuf, OBSIZE);
Функции стандартной библиотеки. Примером стандартной функции записи в файл является функция записи символа putc. Ее описание:
#include int putc(int c, FILE *ostream);
При успешном завершении данная функция возвращает код символа
(совпадает с параметром c), а в случае ошибки – число –1. Параметр ostream аналогичен параметру istreamдля функции getc.
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
Создайте программу, выполняющую добавление символов в конец текстового файла.
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
8.3.9 Закрытие и уничтожение файла
После того как программа завершила работу с файлом, этот файл жела- тельно закрыть, а если файл в будущем не понадобится, то его желательно уни- чтожить. Каждая из этих операций (особенно уничтожение) освобождает ре- сурсы, использовавшиеся для работы с файлом. При завершении процесса все файлы, открытые им, закрываются автоматически.
Системные вызовы. Закрытие файла выполняет системный вызов close. Его описание:
234
#include int close(int fil);
В случае успешного завершения вызов close возвращает 0. В случае ошибки: –1. Единственный параметр вызова – номер закрываемого файла.
Для уничтожения файла может быть использован вызов unlink. Его описание:
#include int unlink(const char *pathname);
В случае успешного завершения вызов возвращает 0, иначе –1. Един- ственный параметр (pathname) есть указатель на имя файла.
Функции стандартной библиотеки. Закрытие файлавыполняет стан- дартная функция fclose. Ее описание:
#include int fclose(FILE *stream);
Функция возвращает значение 0 при успешном завершении. В случае ошибки: –1.
Для уничтожения файла вполне достаточно системного вызова unlink, и соответствующая стандартная функция не существует.
8.3.10 Задание
Требуется разработать одну из следующих программ:
1) копирование файла (имя старого и нового файла передаются в ко- мандной строке), используя системные вызовы;
2) копирование файла (имя старого и нового файла передаются в ко- мандной строке), используя стандартные функции;
3) вывод на экран содержимого текстового файла, имя которого задается в командной строке, используя системные вызовы;
4) вывод на экран содержимого текстового файла, имя которого задается в командной строке, используя стандартные функции;
5) ввод с клавиатуры содержимого текстового файла, имя которого зада- ется в командной строке, используя системные вызовы;
6) ввод с клавиатуры содержимого текстового файла, имя которого зада- ется в командной строке, используя стандартные функции;
235 7) ввод с клавиатуры содержимого текстового файла, имя которого зада- ется в командной строке, используя системные вызовы и вывод его на экран, используя системные вызовы;
8) ввод с клавиатуры содержимого текстового файла, имя которого зада- ется в командной строке, используя стандартные функции и вывод его на экран, используя системные вызовы;
9) ввод с клавиатуры содержимого текстового файла, имя которого зада- ется в командной строке, используя системные вызовы и вывод его на экран, используя стандартные функции;
10) ввод с клавиатуры содержимого текстового файла, имя которого зада- ется в командной строке, используя стандартные функции и вывод его на экран, используя стандартные функции.
8.4 Лабораторная работа № 4. Обработка сигналов
8.4.1 Цель работы
Целью выполнения настоящей лабораторной работы является получение навыков программного управления процессами с помощью сигналов.
8.4.2 Подготовка к выполнению работы
В начале выполнения данной работы следует ознакомиться со следующи- ми вопросами из теоретической части пособия:
1) синхронизация процессов с помощью сигналов (пп. 3.4.1) [1, 2] – по- нятие сигнала; типы сигналов; выдача сигнала процессом;
2) терминальное управление процессами (пп. 3.4.2) [1, 2] – управляющий терминал; сеанс; группа процессов; получение информации о процес- сах с помощью команды shell – psс флагом-j;применение ко- манды shell- kill с целью посылки сигнала процессу;
3) обработка сигналов (п. 5.3) [1, 2] – варианты обработки сигналов; диспозиция сигналов;
4) применение таймера для управления процессами (п. 5.5) [1, 2] – тай- мер; аларм; таймер интервала.
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
8.3.5 Создание файла
Результатом выполнения операции создания файла является или логиче- ское имя файла или признак ошибки.
Системные вызовы. Для создания файла может быть использован один из двух системных вызовов: open и creat. Вызов creatпредставляет собой традиционный способ создания файла. Его описание: int creat(const char *pathname, mode_t mode);
Первый параметр (pathname) является указателем на символьную стро- ку, содержащую имя физического файла. Параметр mode задает права доступа к файлу. Обычно это 3-значное восьмеричное число, старшая цифра которого определяет права доступа владельца файла, средняя цифра – владельца-группы, а младшая цифра задает права доступа всех остальных пользователей. Напри- мер, восьмеричное число (в Си восьмеричное число начинается с нуля) 0764 за- дает право доступа rwx для владельца, rw – для владельца-группы, r – для всех остальных пользователей. Пример использования вызова creat: fil = creat(“/tmp/newfile”, 0764);
227
В случае успешного завершения системный вызов creat возвращает но- мер нового файла, открытого на запись. А в случае ошибки возвращает значе- ние –1. Для того чтобы только что созданный файл был открыт для чтения, его необходимо сначала закрыть, а затем открыть вновь.
Если файл, заданный в качестве первого параметра creat уже существу- ет, то, во-первых, второй параметр вызова creat игнорируется. А во-вторых, данный файл усекается до нулевой длины и, следовательно, прежняя информа- ция файла уничтожается.
Рассмотрим применение для создания файла системного вызова open, который предоставляет больше возможностей по сравнению с вызовом creat.
Для этого, во-первых, в состав второго операнда вызова open обязательно должна быть включена константа O_CREAT. Во-вторых, данный вызов должен иметь третий операнд – mode, аналогичный операнду вызова creat.
Одним из отличий создания файла при помощи open является то, что но- вый файл сразу же оказывается открытым заданным способом. Для этого в со- став второго операнда кроме O_CREAT включается константа O_RDONLY,
O_WRONLY или O_RDWR. Естественно, что запрашиваемый доступ к файлу не должен выходить за рамки прав доступа, задаваемых параметром mode.
Иначе вызов open возвратит признак ошибки. Например, следующий вызов предназначен для создания файла, а также для его открытия на чтение и запись: fil = open(“/tmp/newfile”, O_RDWR| O_CREAT, 0760);
Третья константа, включаемая в состав второго операнда вызова open, определяет поведение этого вызова, если файл с заданным именем уже суще- ствует. В частности, если данная константа опущена, то существующий файл будет открыт на запись (если позволяют права доступа к нему), а параметр mode игнорируется.
Если в состав второго операнда включена константа O_EXCL (exclusive – исключительный), то в случае существования файла вызов open завершится с ошибкой. Пример такого вызова: fil = open(“abc”, O_WRONLY | O_CREAT| O_EXCL, 0760);
Если в состав второго операнда включена константа O_TRUNC, то в слу- чае существования файла он будет усечен до нулевого размера (если позволяют права доступа). Пример такого вызова: fil = open(“abc”, O_WRONLY | O_CREAT| O_TRUNC, 0760);
228
Функции стандартной библиотеки. Для создания файла используется описанная ранее функция fopen, в качестве второго параметра которой задает- ся строка w или a. Каждая из этих строк приводит к созданию нового файла, от- крытого на запись. Различие между ними проявляется в том случае, если уже существует файл с заданным именем. Строка w приводит к усечению этого файла до нулевой длины, а строка a ограничивается открытием существующего файла.
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
Разработайте программу создания файла, получающую имя файла из командной строки. После выполнения программы про- верьте наличие созданного файла.
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
8.3.6 Указатель файла
После того как файл открыт любым из изложенных выше способов, про- граммный процесс может выполнять информационный обмен с ним, то есть выполнять или чтение данных из файла, или запись данных в файл. Для каждо- го открытого файла ядро содержит отдельную переменную, называемую указа- телем файла, которая содержит номер того байта файла, начиная с которого бу- дет выполняться последующее чтение файла или запись в него. Поэтому прежде чем выполнять операцию чтения (записи) файла, необходимо четко представлять себе, номер какого байта находится в указателе файла. Например, в результате обычного открытия файла его указатель содержит 0. Если же во второй операнд вызова open добавить константу O_APPEND, то указатель фай- ла будет установлен на его конец.
Системные вызовы. Системный вызов lseek позволяет установить указатель файла на любую позицию. Его описание:
#include
#include
При успешном завершении вызов lseek возвращает новое значение ука- зателя файла. В случае ошибки: –1. Первый параметр (fil) – номер файла, от- крытого ранее. Второй параметр (offset) задает требуемое смещение относи- тельно той позиции указателя файла, которая задается третьим параметром
229
(whence), который можно задать в виде одной из следующих трех констант, определенных в файле <unistd.h>:
SEEK_SET – смещение прибавляется к началу файла;
SEEK_CUR – смещение прибавляется к текущему значению указателя;
SEEK_END – смещение прибавляется к концу файла.
Несмотря на то что тип смещения и тип значения указателя файла один и тот же – off_t (он определен в файле <sys/types.h>), область допусти- мых значений для каждого из них различна. В отличие от указателя файла, принимающего лишь неотрицательные целые значения, смещение может быть и отрицательным.
Вообще говоря, вызов lseekвыдаст признак ошибки для существующе- го файла только в том случае, если мы попытаемся установить указатель файла на позицию, предшествующую началу файла. И наоборот, операция установки указателя на позицию, расположенную далее конца файла, приведет к появле- нию в файле «дыры», заполненной нулями.
Пример использования вызова lseek: off_t newpos; newpos = lseek(fil, (off_t)-16, SEEK_END);
В данном примере новое значение указателя файла будет на 16 меньше, чем значение, соответствующее концу файла. Обратите внимание, что для чис- ла (–16), задающего смещение, используется явное задание типа (off_t).
Интересно отметить, что вызов lseek можно использовать для опреде- ления длины файла. Например, пусть переменная filsize содержит длину файла, тогда: off_t filsize; int fil; filsize = lseek(fil, (off_t) 0, SEEK_END);
Функции стандартной библиотеки. Установку указателя файла выпол- няет стандартная функция fseek. Ее описание:
#include
230
Функция возвращает ненулевое значение только в случае ошибки. Пер- вый параметр (stream) идентифицирует файл. Назначение двух других пара- метров аналогично вызову seek.
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
Создайте с помощью текстового редактора небольшой файл, а затем с помощью своей программы образуйте в этом файле «ды- ру», наличие которой проверьте с помощью текстового редактора
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
8.3.7 Чтение из файла
После того как указатель файла установлен в требуемое место файла, можно выполнить чтение из этого файла требуемого числа байтов.
Системные вызовы. Чтение из файла выполняет системный вызов read.
Его описание:
#include
Данный вызов выполняет чтение из файла, номер которого задается пер- вым параметром (fil), такого числа байтов, которое задается третьим пара- метром (n). Считанные байты помещаются в буфер, указатель на который зада- ется вторым параметром (buffer). Буфер представляет собой массив, элементы которого имеют произвольный тип (void). (Чаще всего буфер оформляется как массив символьного типа.)
Если выполнение readзавершилось успешно, то он возвращает число байтов, считанных из файла. Это число равно n или меньше его. Второе выпол- няется тогда, когда мы выполняем чтение из конечной части файла, содержа- щей меньше символов, чем запрашивается. Если после этого опять выполнить read
, то он возвратит число 0, означающее, что достигнут конец файла.
В случае ошибки read возвращает –1.
Одним из результатов выполнения read является перемещение указателя файла на первый, несчитанный байт файла или на его конец.
· · · · · · · · · · · · · · · · · · · · · · · · ·
Пример
· · · · · · · · · · · · · · · · · · · · · · · · ·
Следующая программа выполняет подсчет байтов в файле, имя которого передается из командной строки:
#include
231
#include
#include
#define BUFSIZE 512 main(int argc, char *argv[])
{ int fil; char buffer[BUFSIZE]; ssize_t nread; long total = 0; if (argc !=2)
{ printf(“Need 1 argument for open\n”); exit(1); /* Выход по ошибке */
}
/* Открыть файл для чтения */ if ((fil = open(argv[1], O_RDONLY)) = = -1)
{ printf(“Cannot open file %s\n”, argv[1]); exit(1); /* Выход по ошибке */
}
/* Повторять до конца файла */ while((nread = read(fil, buffer, BUFSIZE)) >0) total=total+nread; printf(“total = %ld\n”, total); exit(0);
}
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
Поясним, почему длина буфера взята равной 512 байтам. Дело в том, что каждый системный вызов readинициирует считывание с диска блока байтов.
Величина этого блока есть число 512, умноженное на степень двойки, и зависит от системы. Если считывать данные с диска небольшими порциями (по не- сколько байтов), то никаких изменений в результатах работы программы не бу- дет. Единственное – резко возрастет время выполнения программы, так как, во- первых, чтение каждой порции байтов требует считывания в лучшем случае из дискового кэша, а в худшем – с диска целого блока. А во-вторых, большое ко- личество системных вызовов приводит к такому же числу переключений ЦП из
232 режима «задача» в режим «ядро» и обратно, что требует заметных затрат вре- мени ЦП.
Функции стандартной библиотеки. Применение для чтения из файла стандартных библиотечных функций позволяет не заботиться об эффективно- сти информационного обмена. Так как, во-первых, эти функции занимаются буферизацией обмена с файлом. Поэтому запрос из программы очередной пор- ции байтов не приводит неизбежно к считыванию блока файла. Если эта порция находится в том блоке, который был считан последним с диска, то требуемые байты стандартная функция находит в своем буфере, откуда она и копирует их в буфер программы. Во-вторых, функции стандартной библиотеки выполняют- ся в режиме «задача» и поэтому их вызов не приводит к переключению процес- сора.
Примером стандартной функции чтения файла является функция чтения символа getc. Ее описание:
#include
Данная функция возвращает или код символа или число –1, которое означает «конец файла» (EOF). Единственный параметр функции (istream) представляет собой указатель на структуру FILE. Этот параметр должен быть получен в результате открытия файла в программе (до вызова функции getc).
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
Выполните приведенную выше программу подсчета симво- лов.
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
8.3.8 Запись в файл
После того как указатель файла установлен в требуемое место файла, можно выполнить запись в файл требуемого числа байтов. Если указатель фай- ла был установлен на конец файла, то запись будет выполняться на свободное место за счет увеличения длины файла. Иначе – запись выполняется «поверх» прежнего содержимого файла.
Системные вызовы. Запись в файл выполняет системный вызов write.
Его описание:
#include
233
Данный вызов выполняет запись в файл, номер которого задается первым параметром (fil), такого числа байтов, которое задается третьим параметром
(n). Запись в файл выполняется из буфера, указатель на который задается вто- рым параметром (buffer). Буфер представляет собой массив, элементы кото- рого имеют произвольный тип (void).
Если выполнение write завершилось успешно, то он возвращает число байтов, записанных в файл. Почти всегда это число равно n. В случае ошибки write возвращает –1.
Одним из результатов выполнения write является перемещение указа- теля файла на первый байт после записанного участка файла.
В качестве примера приведем фрагмент программы, выполняющий за- пись содержимого буфера outbuf, имеющего длину OBSIZE, в файл с именем abc: fil = open(“abc”, O_RDWR| O_APPEND); write(fil, outbuf, OBSIZE);
Функции стандартной библиотеки. Примером стандартной функции записи в файл является функция записи символа putc. Ее описание:
#include
При успешном завершении данная функция возвращает код символа
(совпадает с параметром c), а в случае ошибки – число –1. Параметр ostream аналогичен параметру istreamдля функции getc.
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
Создайте программу, выполняющую добавление символов в конец текстового файла.
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
8.3.9 Закрытие и уничтожение файла
После того как программа завершила работу с файлом, этот файл жела- тельно закрыть, а если файл в будущем не понадобится, то его желательно уни- чтожить. Каждая из этих операций (особенно уничтожение) освобождает ре- сурсы, использовавшиеся для работы с файлом. При завершении процесса все файлы, открытые им, закрываются автоматически.
Системные вызовы. Закрытие файла выполняет системный вызов close. Его описание:
234
#include
В случае успешного завершения вызов close возвращает 0. В случае ошибки: –1. Единственный параметр вызова – номер закрываемого файла.
Для уничтожения файла может быть использован вызов unlink. Его описание:
#include
В случае успешного завершения вызов возвращает 0, иначе –1. Един- ственный параметр (pathname) есть указатель на имя файла.
Функции стандартной библиотеки. Закрытие файлавыполняет стан- дартная функция fclose. Ее описание:
#include
Функция возвращает значение 0 при успешном завершении. В случае ошибки: –1.
Для уничтожения файла вполне достаточно системного вызова unlink, и соответствующая стандартная функция не существует.
8.3.10 Задание
Требуется разработать одну из следующих программ:
1) копирование файла (имя старого и нового файла передаются в ко- мандной строке), используя системные вызовы;
2) копирование файла (имя старого и нового файла передаются в ко- мандной строке), используя стандартные функции;
3) вывод на экран содержимого текстового файла, имя которого задается в командной строке, используя системные вызовы;
4) вывод на экран содержимого текстового файла, имя которого задается в командной строке, используя стандартные функции;
5) ввод с клавиатуры содержимого текстового файла, имя которого зада- ется в командной строке, используя системные вызовы;
6) ввод с клавиатуры содержимого текстового файла, имя которого зада- ется в командной строке, используя стандартные функции;
235 7) ввод с клавиатуры содержимого текстового файла, имя которого зада- ется в командной строке, используя системные вызовы и вывод его на экран, используя системные вызовы;
8) ввод с клавиатуры содержимого текстового файла, имя которого зада- ется в командной строке, используя стандартные функции и вывод его на экран, используя системные вызовы;
9) ввод с клавиатуры содержимого текстового файла, имя которого зада- ется в командной строке, используя системные вызовы и вывод его на экран, используя стандартные функции;
10) ввод с клавиатуры содержимого текстового файла, имя которого зада- ется в командной строке, используя стандартные функции и вывод его на экран, используя стандартные функции.
8.4 Лабораторная работа № 4. Обработка сигналов
8.4.1 Цель работы
Целью выполнения настоящей лабораторной работы является получение навыков программного управления процессами с помощью сигналов.
8.4.2 Подготовка к выполнению работы
В начале выполнения данной работы следует ознакомиться со следующи- ми вопросами из теоретической части пособия:
1) синхронизация процессов с помощью сигналов (пп. 3.4.1) [1, 2] – по- нятие сигнала; типы сигналов; выдача сигнала процессом;
2) терминальное управление процессами (пп. 3.4.2) [1, 2] – управляющий терминал; сеанс; группа процессов; получение информации о процес- сах с помощью команды shell – psс флагом-j;применение ко- манды shell- kill с целью посылки сигнала процессу;
3) обработка сигналов (п. 5.3) [1, 2] – варианты обработки сигналов; диспозиция сигналов;
4) применение таймера для управления процессами (п. 5.5) [1, 2] – тай- мер; аларм; таймер интервала.