PytPajB0Au
.pdfПроцесс-потомок является точной копией процесса-родителя. Но у них отличаются номера процесса и значения, возвращаемые вызовом fork. При этом потомок получает к ранее открытым файлам доступ того же типа, что и предок. Родственные процессы общаются с общим файлом через один указатель чтения/записи, и если один из процессов прочитал или записал данные в файл, то значение указателя чтения/записи изменится для всех родственных процессов, имеющих доступ к этому файлу.
Рассмотрим для примера такую последовательность системных вызовов:
fd1 = open("/etc/passwd",O_RDONLY); pid = fork();
fd2 = open("private",O_RDWR);
Первый вызов open выполняется до вызова fork, он создает записи, относящиеся к файлу /etc/passwd, во всех файловых таблицах. При выполнении вызова fork процесс-потомок получает копию таблицы открытых файлов процесса, а в записях таблиц файлов и описателей файлов счетчики ссылок на файл /etc/passwd увеличиваются и становятся равным 2. Дополнительная запись в таблицу файлов не добавляется, оба процесса имеют доступ к файлу через один указатель чтения/записи. Второй вызов open выполняется после вызова fork, то есть тогда, когда существуют уже два процесса - родитель и потомок. Каждый из них выполняет открытие файла независимо от другого. В связи с этим, новые записи добавляются во все таблицы. Закрытие файла уменьшает число ссылок на файл, и когда оно становится равным 0, происходит удаление соответствующих записей из таблиц.
9.1. ПРОГРАММИРОВАНИЕ ОПЕРАЦИЙ ВВОДА-ВЫВОДА
Системные вызовы представляют собой единственное средство, реализующее интерфейс между пользовательскими программами и ядром ОС UNIX. Всякая операция ввода/вывода для пользователя - это операция ввода/вывода в файл. Рассмотрим наиболее часто используемые из системных вызовов.
OPEN
Открывает или создает файл:
int open( |
|
const char *path, |
/* полное имя файла */ |
int flags, |
/* флаги */ |
mode_t perms |
/* права доступа (если создается новый |
файл) */ |
|
); |
|
70
Возвращает положительное целое число, так называемый пользовательский дескриптор файла, который в дальнейшем используется для обращения к этому файлу. Если нет возможности открыть файл, open возвращает -1.
flags определяет режим открытия файла: O_RDONLY – открыть только для чтения O_WRONLY — открыть только для записи O_RDWR — открыть для чтения и для записи
O_APPEND — открыть для дополнения в конец файла O_CREAT — создать новый, если не существует
O_TRUNC — удалить содержимое файла — усечь его размер
до нуля
O_EXCL — не открывать, если существует (используется в комбинации с O_CREAT)
perms определеяет права доступа к создаваемому файлу.
Принцип именования прав доступа следует шаблону S_Ipwww, где p определяет режим доступа (R, W или Х), а www – кому выдается право на этот режим доступа (USR, GRP или OTH). Например, пусть вывод команды ls следующий:
-rwxr-xr-x 1 tar_stud tar_stud 29880 Aug 4 13:45 hello
Для этого файла вместо восьмеричного числа 755 можно записать:
S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
Существуют отдельные идентификаторы, описывающие полные права доступа. Именование этих идентификаторов следует форме S_IRWXw, где w определяет, кому выдается полное право доступа к файлу
— U, G или O.
Таким образом, предыдущий пример может быть записан в следующем виде:
S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
CLOSE
Закрывает файл, уничтожает связь между пользовательским дескриптором файла и самим файлом:
void close( |
|
int fd |
/* дескриптор файла */ |
);
Возвращает 0 в случае успеха, -1 в случае ошибки (код ошибки в переменной errno).
STAT и FSTAT
Эти системные вызовы позволяют получить информацию о файле, не осуществляя явного доступа к нему:
71
int stat(char *path, struct stat *statbuf); int fstat(int fd, struct stat *statbuf);
Вызов stat предоставляет информацию по имени файла, а fstat - по номеру дескриптора открытого файла. Информация помещается в структуру, описанную ниже:
struct stat
{
dev_t st_dev; |
/* идентификатор устройства файловой |
|||
системы */ |
|
|
|
|
ino_t st_ino; |
/* номер индексного узла */ |
|
||
ushort st_mode; |
/* режим доступа и тип файла */ |
|
||
short st_nlink; |
/* счетчик числа ссылок на файл */ |
|
||
ushort st_uid; |
/* идентификатор его владельца */ |
|
||
ushort st_gid; |
/* идентификатор группы */ |
|
||
dev_t st_rdev; |
/* тип устройства */ |
|
||
off_t st_size; |
/* размер файла в байтах */ |
|
||
time_t st_atime; |
/* дата последнего доступа */ |
|
||
time_t st_mtime; |
/* дата последней модификации */ |
|
||
time_t st_ctime; |
/* дата создания */ |
|
||
} |
|
|
|
|
Для детализации информации в поле st_mode используются следую- |
||||
щие макросы: |
|
|
|
|
#define S_IFMT |
0170000 |
/* тип файла */ |
|
|
#define S_IFDIR |
0040000 |
/* каталог */ |
|
|
#define S_IFCHR |
0020000 |
/* |
байт-ориентированный |
спец. |
файл */ |
|
|
|
|
#define S_IFBLK |
0060000 |
/* |
блок-ориентированный |
спец. |
файл */ |
|
|
|
|
#define S_IFREG |
0100000 |
/* обычный файл */ |
|
|
#define S_IFIFO |
0010000 |
/* дисциплина FIFO */ |
|
|
#define S_ISUID |
04000 |
/* идентификатор владельца */ |
||
#define S_ISGID |
02000 |
/* идентификатор группы */ |
|
|
#define S_ISVTX |
01000 |
/* |
сохранить свопируемый текст |
|
*/ |
|
|
|
|
#define S_IREAD |
00400 |
/* владельцу разрешено чтение */ |
||
#define S_WRITE |
00200 |
/* владельцу разрешена запись */ |
||
#define S_IEXEC |
00100 |
/* владельцу разрешено исполне- |
||
ние */ |
|
|
|
|
Пример использования вызова stat: struct stat stbuf;
char *filename = ”myfile”; stat(filename, &stbuf);
72
if ((stbuf.st_mode & S_IFMT) == S_IFDIR) printf("%s является каталогом", filename);
READ
Осуществляет чтение из открытого файла count символов в буфер buffer:
int read(int fd, void *buffer, unsigned count);
Возвращает количество прочитанных байт, 0 (если достигнут конец файла) или -1 (в случае ошибки).
Чтение начинается с текущей позиции в файле и по окончании текущая позиция смещается на количество прочитанных байт.
WRITE
Осуществляет запись в открытый файл (fd) указанного количества
(count) символов из буфера (buffer):
int write(int fd, void *buffer, unsigned count);
Возвращает количество реально записанных байт или -1 в случае ошибки.
Запись начинается с текущей позиции в файле, а по ее окончании текущая позиция смещается на число записанных байт.
LSEEK
Перемещает указатель файла с пользовательским дескриптором fd на offset байт:
long lseek(int fd, long offset, int fromwhere);
Возвращает новую позицию в файле или -1 в случае ошибки. Параметр fromwhere определяет положение указателя файла перед
началом перемещения:
SEEK_SET — от начала файла (=0);
SEEK_CUR — от текущей позиции указателя (=1); SEEK_END — от конца файла (=2).
Результатом работы вызова может быть любое неотрицательное число, даже превышающее размер файла. Если новая текущая позиция оказалась за пределами файла, ближайший вызов write вставит «недостающий» кусок в конец файла и заполнит его байтами со значением 0. Вызов read, с текущей позицией установленной в конец файла или за его пределами, вернет признак конца файла – 0. Попытка чтения из интервала, который заполнил вызов write при вставке «недостающего» куска, увенчается успехом и в вызывающую программу будет возвращен буфер, заполненный нулями, как того и следовало ожидать.
DUP и DUP2
73
Эти системные вызовы дублируют пользовательский дескриптор файла:
int dup(int handle);
int dup2(int oldhandle, int newhandle); fd1 = dup(handle);
fd2 = dup2(oldhandle, newhandle);
Копия пользовательского дескриптора позволяет осуществлять к файлу доступ того же типа и с использованием того же указателя чтения/записи, что и с помощью оригинального дескриптора.
Вызов dup возвращает первый свободный номер дескриптора fd1 или -1, если указанный дескриптор handle не соответствует открытому файлу или нет свободных номеров.
Вызов dup2 возвращает дескриптор newhandle как копию дескриптора oldhandle или -1, если указанный дескриптор oldhandle не соответствует открытому файлу. Если newhandle до этого указывал на открытый файл, этот файл в результате вызова dup2 будет закрыт.
9.2. КОМПИЛИРОВАНИЕ ИСХОДНОГО КОДА ПРОГРАММЫ НА ЯЗЫКЕ C++
Для того, чтобы ввести код программы и отредактировать его подойдет любой текстовый редактор, доступный в вашей UNIX-подобной операционной системе. При этом, программа на языке C++ может иметь различные расширения (.С; .cxx; .cc)
Чтобы откомпилировать уже набранный и отредактированный в текстовом редакторе код программы можно воспользоваться командой вида:
c++ file_name.cxx.
Если текст программы содержал ошибки, то в результате выполнения команды, указанной выше, вы получите сообщение об ошибках. В противном случае, вы получите исполняемый файл («a.out»). При этом, указание расширения является обязательным условием.
Для запуска на выполнение полученного исполняемого файла с программой следует использовать команду вида:
./a.out [в квадратных скобках через пробел указываются параметры, при необходимости]
Для того, чтобы в результате компиляции получить исполняемый файл с определенным именем, необходимо использовать при компиляции опцию «–o»:
c++ file_name.cxx. –o executable_file_name
Тогда, для запуска программы необходимо использовать команду
вида:
./ executable_file_name [в квадратных скобках через пробел указываются параметры, при необходимости]
74
9.3. ПРИМЕРЫ РАБОТЫ С ФАЙЛАМИ
Пример 1. Запись в файл / чтение из файла.
Обратите внимание на обработку параметров командной строки.
/* Фрагмент программы rw.cxx записи в файл / чтения из файла. Программа воспринимает в качестве параметра командной строки имя рабочего файла. Если файл не существует, он будет создан, если существует, его содержимое будет изменено */
#include <stdio.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h>
int fd;
int f1()
{
static int j = 1;
if (j > 10) return 0; write(fd, &j, sizeof(int));
printf("write %d -- %d\n", fd, j++); return 1;
}
void f2()
{
int i; lseek(fd,-sizeof(int), 1); read(fd, &i, sizeof(int));
printf("read %d -- %d\n", fd, i);
}
int main(int argc, char *argv[])
{
if (argc < 2) puts("Format: rw filename"); else
{
fd = open(argv[1], O_CREAT | O_RDWR); while (f1()) f2();
close(fd);
}
return 0;
75
}
Пример 2. Дублирование дескриптора файла.
/* Фрагмент программы dup.cxx - перенаправление стандартного вывода в файл. */
#include <unistd.h> #include <stdio.h> #include <string.h> #include <fcntl.h>
int main(void)
{
int outf, std_out;
char *str1 = "Вывод строки в файл\n", *str2 = "Вывод строки на экран\n", *str3 = "Почему эта строка в файле?\n"; std_out = dup(1);
/* закрытие стандартного вывода */ close(1);
outf = open("1.dat", O_CREAT|O_WRONLY, S_IRWXU); write(outf,str1,strlen(str1));
write(std_out,str2,strlen(str2));
write(1,str3,strlen(str3));
/* восстановление предыдущих значений */ close(outf);
outf = open("dev\tty", O_WRONLY); close(std_out);
return 0;
}
9.4. ВАРИАНТЫ ЗАДАНИЙ ДЛЯ ЗАКРЕПЛЕНИЯ ЗНАНИЙ НА ПРАКТИКЕ
1.Создайте программу, которая будет считывать две последовательности байтов из файла a1 и записывать их в файл a2. Длины последовательностей [х1, х2] и [y1, y2], имена файлов задаются в качестве параметров.
2.Создайте программу, в качестве параметров которой задаются длины последовательностей байтов [х1, х2] и [y1, y2], и имя файла, в котором эти последовательности будут меняться местами.
76
3.Создайте программу, которая в качестве параметров получает имена входного и выходного файлов и натуральной число - х. Программа должна считывать каждый х-й байт из входного файла и записывать его в выходной файл.
4.Создайте программу, которая в качестве параметров получает имена входного и выходного файлов и параметр - x. Если параметр x нечетный, то программа должна считывать каждый x-й байт из входного файла и записывать его в выходной файл, а если параметр x четный, то программа должна переписать побайтово содержимое входного файла в выходной файл.
5.Создайте программу, которая в качестве параметров получает имена входного файла и некоторую подстроку. Программа должна осуществлять поиск этой подстроки в файле. В случае обнаружения этой подстроки в файле, необходимо заменить её на другую подстроку, также задаваемую в качестве параметра.
6.Создайте программу, которая в качестве параметров получает имена входного файла и некоторую подстроку. Программа должна осуществлять поиск этой подстроки в файле. В случае обнаружения этой подстроки в файле, необходимо заменить её на другую подстроку, запрашиваемую у пользователя.
7.Создайте программу, которая в качестве параметров получает имя входного файла, производит в нем поиск пробелов и, в случае, если обнаруживает подстроку, состоящую из более чем одного пробела, идущего подряд, заменяет её на один пробел.
8.Используя алгоритм сложения по модулю два, создайте программу, которая в качестве параметров получает имя входного файла и кодовое слово и может осуществлять кодирование и декодирование этого файла.
9.Создайте программу, которая в качестве параметров получает имя входного текстового файла, подсчитывает общее количество строк в нем, и модифицирует его, подставляя число строк в последнюю
строку этого файла.
10.Создайте программу, которая в качестве параметров получает имя входного текстового файла, подсчитывает общее количество слов в нем, и модифицирует его, подставляя число слов в последнюю строку этого файла.
11.Создайте программу, которая в качестве параметров получает имя входного текстового файла, подсчитывает общее количество букв в нем, и модифицирует его, подставляя число букв в последнюю строку этого файла.
12.Создайте программу, которая в качестве параметров получает имя входного текстового файла, подсчитывает общее количество цифр в
77
нем, и модифицирует его, подставляя число цифр в последнюю строку этого файла.
13.Создайте программу, которая в качестве параметров получает имя входного текстового файла, подсчитывает общее количество символов в нем, и модифицирует его, подставляя число символов в последнюю строку этого файла.
14.Создайте программу, которая в качестве параметров получает имя входного файла и диапазон кодов символов [х1, х2], находит в файле все символы, попадающие в указанный диапазон и заменяет их символом, код которого также указывается в качестве параметра.
15.Создайте программу, которая в качестве параметров получает имя входного файла и диапазон кодов символов [х1, х2], находит в файле все символы, попадающие в указанный диапазон и заменяет их пробелами.
16.Создайте программу, которая в качестве параметров получает имя входного текстового файла и параметр x, указывающий желаемое количество строк на странице. Программа должна разбить текстовый фай на страницы, с количеством строк, соответствующим x (см. код
12).
17.Создайте программу, которая в качестве параметров получает имя входного текстового файла и меняет его формат с формата UNIX на формат DOS. Суть замены заключается в добавлении/удалении специальных символов, используемых для обозначения перевод строки и возврата каретки (10 и 13, соответственно).
18.Создайте программу, которая в качестве параметров получает имя файла a1 и директории d1 и записывает список файлов директории d1 и их параметров в файл a1.
19.Создайте программу, которая в качестве параметров получает имя файла a1 и директории d1, проводит поиск всех файлов в поддереве d1 и записывает количество найденных файлов в файл a1.
20.Создайте программу, которая в качестве параметров получает имя файла a1 и директории d1, проводит поиск всех файлов в поддереве d1 и записывает имена найденных файлов в файл a1.
21.Создайте программу, которая в качестве параметров получает имя файла a1 и директории d1, проводит поиск всех файлов в поддереве d1 и записывает имена и параметры найденных файлов в файл a1.
22.Создайте программу, которая в качестве параметров получает имя директории d1, проводит поиск всех файлов в поддереве d1 и, в случае, если для какого-либо файла разрешено выполнение для какой – либо группы пользователей, устанавливает для этого файла биты разрешения доступа на выполнение.
78
23.Создайте программу, которая в качестве параметров получает имя файла a1, директории d1, и дату t1. Программа должна записать список всех файлов директории d1, которые были созданы в дату t1.
24.Создайте программу, которая в качестве параметров получает имя файла a1, директории d1, и дату t1. Программа должна записать список всех файлов директории d1, которые были модифицированы в дату t1.
25.Создайте программу, которая в качестве параметров получает имя файла a1 и директории d1. Программа должна записать список всех файлов директории d1, которые были созданы в текущую дату.
26.Создайте программу, которая в качестве параметров получает имя файла a1, директории d1, и дату t1. Программа должна записать список всех файлов директории d1, которые были модифицированы в текущую дату.
27.Создайте программу, которая в качестве параметров получает имя файла a1, директории d1 осуществляет определение имен всех владельцев всех файлов в директории d1 и выводит список всех владельцев всех файлов в файл a1.
28.Создайте программу, которая в качестве параметров получает имя директории d1, осуществляет определение имен всех владельцев всех файлов в директории d1 и выводит список всех владельцев всех файлов на экран.
29.Создайте программу, которая в качестве параметров получает имя файла a1, директории d1, искомое количество ссылок файла l1, осуществляет поиск всех файлов из директории d1, которые имеют более чем l1 ссылок и выводит список имен этих файлов в файл a1.
30.Создайте программу, которая в качестве параметров получает имя файла a1, директории d1, искомое количество ссылок файла l1, осуществляет поиск всех файлов из директории d1, которые имеют менее чем l1 ссылок и выводит список имен этих файлов в файл a1.
31.Создайте программу, которая в качестве параметров получает имя файла a1, директории d1, искомое количество ссылок файла l1, осуществляет поиск всех файлов из директории d1, которые имеют количество ссылок равное l1 и выводит список имен этих файлов в файл a1.
32.Создайте программу, которая в качестве параметров получает имя директории d1, искомое количество ссылок файла l1, осуществляет поиск всех файлов из директории d1, которые имеют более чем l1 ссылок и выводит список имен этих файлов на экран.
33.Создайте программу, которая в качестве параметров получает имя директории d1, искомое количество ссылок файла l1, осуществляет поиск всех файлов из директории d1, которые имеют менее чем l1 ссылок и выводит список имен этих файлов на экран.
79