Системные вызовы для работы с файлами в Linux 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Системные вызовы для работы с файлами в Linux



28.12.2011IT тематикаКомментарии: 3

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

Начнем с open. Этот системный вызов нужен для открытия файла. Для его использования необходимо подключить заголовочный файл <fcntl.h>. Прототипы:

 

1 2 int open(char * filename, int flags) int open(char * filename, int flags, int mode)

Возвращает файловый дескриптор в случае успешного выполнения, и значение меньше 0, если выполнить не удалось. Аргумент filename — это путь к файлу. Может быть как полным, так и относительным. Аргумент flags устанавливает режим открытия файла. Для удобства можно использовать следующие макроопределения:

O_RDONLY — только для чтения;
O_WRONLY — только для записи;
O_RDWR — для чтения и записи;
O_NONBLOCK — не блокирующее открытие, только для чтения;
O_APPEND — открыть файл и переместить указатель курсора в конец;
O_CREAT — создать файл, если такой не существует;
O_TRUNC — удалить содержимое файла, при открытии;
O_EXCL — открытие потерпит ошибку, если файл уже существует;
O_DIRECT — пробовать не использовать кэширующие операции;
O_NOFOLLOW — если файл символьная ссылка, открывает именно файл-ссылку;

Все макроопределения можно совмещать, путем использования побитового или «|». Например, следующий код откроет файл для чтения, записи и удалит все содержимое:

 

1 myFile = open("myFile.txt", O_RDWR | O_TRUNC);

Аргумент mode определяет права файла, если используется флаг O_CREAT, иначе просто игнорируется. Если подключить <sys/stat.h>, то можно использовать макроопределения вместо битовой маски, для задания прав:

S_IRUSR — чтения для владельца;
S_IWUSR — запись для владельца;
S_IXUSR — исполнение для владельца;
S_IRGRP — чтение для группы;
S_IWGRP — запись для группы;
S_IXGRP — исполнение для группы;
S_IROTH — чтения для остальных;
S_IWOTH — запись для остальных;
S_IXOTH — исполнение для остальных;

Так же, как и для аргумента flags, можно использовать битовое «или» для «сложения» прав. Например следующий код создаст файл, который доступен для изменения владельцем, группа может только читать, а остальные — только исполнение, и откроет его в режиме чтения/записи.

 

1 myFile = open("myFile.txt", O_RDWR | O_CREAT, S_IWUSR | S_IRGRP | S_IXOTH);

Теперь рассмотрим системный вызов close. Он закрывает открытый дескриптор. Прототип очень прост:

 

1 int close(int fd)

Возвращает 0 — при успешном закрытии, и значение меньше 0 — при ошибке. Принимает конечно же файловый дескриптор.

Думаю с open и close вопросов нет. Идем дальше. Системный вызов read выполняет чтение из файла в буфер указанное количество байт. Прототип следующий.

 

1 int read(int fd, void * ptr, int numbytes)

Аргумент fd — файловый дескриптор, ptr — указатель на область памяти, куда записывать данные и numbytes — количество байт, которые необходимо прочитать из файла и записать в ptr. Возвращает число реально прочитанных байт при успешном чтении, 0 — если достигнут конец файла и отрицательное число, если произошла ошибка чтения. Напишем маленькую программу, которая открывает файл, читает первые 64 байта, выводит их на экран. Добавим еще вывод количества прочитанных байт, вдруг в файле их будет меньше! Код вышел следующий:

 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include &lt;stdio.h&gt; #include &lt;fcntl.h&gt; #include &lt;string.h&gt;   int main(int argc, char *argv[]) { int input_file = 0; //Файловая переменная input_file = open("input.txt", O_RDONLY); //Открываю файл для чтения char buff[64]; //Буфер для чтения memset(buff, 0, 64); //Заполняю буфер нулями int count = 0; //Переменная для хранения количества прочитанных байт count = read(input_file, buff, 64); //Читаю из файла close(input_file); //Закрываю файловый дескриптор /* Вывожу содержимое и количество прочитанных байт */ printf("Содержимое файла:rn%srnВсего прочитано: %irn", buff, count); return 0; }

Программа просто открывает файл и пытаеться прочесть первые 64 байта. На экран выводиться содержимое файла, которое удалось прочитать и количество успешно прочитанных байт. Я создал файл input.txt, и написал в нем «This is test file!». Запустил программу, он вывела на экран «This is test file!» и количество прочитанных байт — 19. Поскольку 1 символ занимает 1 байт, а в предложении «This is test file!» 19 символов, значит все правильно!

Читать из файла научились. Теперь научимся записывать. Системный вызов записывающий в файл информацию очень похож на read:

 

1 int write(int fd, void * ptr, int numbytes)

Принцип работы аналогичный. Функция пытается записать в файл с файловым дискриптором fd, данный из буффера ptr, в количестве байт numbytes.

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

 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include &lt;stdio.h&gt; #include &lt;fcntl.h&gt; #include &lt;string.h&gt;   int main(int argc, char *argv[]) { //Открытие файла int file = open("output.txt", O_RDWR | O_CREAT, S_IRWXU | S_IRWXO | S_IRWXG); printf("Введите строку...rn"); char buff[2048]; //Считывание строки scanf("%s", buff); //Запись в файл write(file, buff, strlen(buff)); close(file); }

Ничего сложного. Поскольку в функцию write необходимо передать количество записываемых байт из буфера, я использую функцию strlen. Она определяет длину строки, то есть от первого символа и до символа конца строки(‘‘).

С чтением и записью ничего сложного. Следующий системный вызов смещает текущую позицию в файле. Прототип lseek:

 

1 off_t lseek(int fd, off_t offset, int whence);

Аргумент fd — дескриптор файла, offset — смещение, whence — тип смещения. Возвращает количество байтов, на которое сместился указатель от начала файла, если не было ошибки, иначе -1. В качестве whence можно ипспользовать следующие макросы:

· SEEK_SET — смещение в файле устанавливается в значение offset;

· SEEK_CUR — смещение в файле устанавливается в текущее значение + offset;

· SEEK_END — смещение в файле устанавливается в конец файла + offset.

Пример работы далее. Напишем простую программу, которая выводит содержимое файла, а после предлагает дописать строку в него.

 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include &lt;stdio.h&gt; #include &lt;fcntl.h&gt; #include &lt;string.h&gt;   int main(int argc, char *argv[]) { char buff[2048]; //Открытие файла int file = open("1.txt", O_RDWR); //Вывод содержимого read(file, buff, 2048); printf("Содержимое файла:rn%srn", buff); //Обнуляем буфер memset(buff, 0, 2048); printf("Введите строку, которую следует дописать в файлrn"); //Считывание строки scanf("%s", buff); //Запись в файл write(file, buff, strlen(buff)); close(file); }

Я создал файл 1.txt и написал в него «This is file!». После этого, запустил программу, она вывела «This is file!» и спросила, что дописать. Я написал «Okay!». После чего в файле оказалось:

This is file!
Okay!

Теперь немного исправим нашу программу:

 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include &lt;stdio.h&gt; #include &lt;fcntl.h&gt; #include &lt;string.h&gt;   int main(int argc, char *argv[]) { char buff[2048]; //Открытие файла int file = open("1.txt", O_RDWR); //Вывод содержимого read(file, buff, 2048); printf("Содержимое файла:rn%srn", buff); //Обнуляем буфер memset(buff, 0, 2048); printf("Введите строку, которую следует дописать в файлrn"); //Считывание строки scanf("%s", buff); //Смещение lseek(file, 0, SEEK_SET); //Запись в файл write(file, buff, strlen(buff)); close(file); }

Проделываю тоже самую операцию с файлом, и получаю:

Okay!is file!

Как видно, указатель на текущую позицию в файле сместился, в результате чего «Okay!» записалось в начале.

Кроме смещения текущей позиции, системный вызов lseek может послужить в других целях. Например для определения текущей позиции:

 

1 int pos = lseek(file, 0, SEEK_CUR);

Или например для определения размера файла:

 

1 int size = lseek(file, 0, SEEK_END);

Для записи или чтения байт в определенное место файла, кроме комбинации read/write + lseek есть еще две функции. Это pread и pwrite, прототипы:

 

1 2 ssize_t pread(int fd, void *buff, size_t count, off_t offset); ssize_t pwrite(int fd, const void *buff, size_t count, off_t offset);

Функция pread считывает из файла, с дескриптором fd, смещением offset, количество байт count в буфер buff. Функция pwrite работает аналогично, но для записи.

Приведу примеры. Первая мини-программа открывает файл для чтения/записи и считывает с начала файла 10 байт. Выводит их на экран.

 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include &lt;stdio.h&gt; #include &lt;fcntl.h&gt; #include &lt;string.h&gt;   int main(int argc, char *argv[]) { char buff[2048]; //Открытие файла int file = open("1.txt", O_RDWR); //Вывод содержимого через pread pread(file, buff, 10, 0); printf("Первые десять байт файла:rn%srn", buff); close(file); }

Содержимое файла 1.txt:

This is line! This is Sparta!

Вывод на экран:

This is li

Примечание: пробелы тоже учитываются, так как они тоже символы.

Теперь изменим программу. Добавим запись в файл.

 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include &lt;stdio.h&gt; #include &lt;fcntl.h&gt; #include &lt;string.h&gt;   int main(int argc, char *argv[]) { char buff[2048]; //Открытие файла int file = open("1.txt", O_RDWR); //Вывод содержимого через pread pread(file, buff, 10, 0); printf("Первые десять байт файла:rn%srn", buff); //Считаем новую строку с экрана memset(buff, 0, 2048); printf("Введите новую строку:rn"); scanf("%s", buff); //Запишем ее в файл pwrite(file, buff, strlen(buff), 0); close(file); }

Данная программа затрет начало файла введенной строкой с экрана.

Далее рассмотрим 2 системных вызова для изменения прав файла. Прототипы:

 

1 2 int chmod(const char *path, mode_t mode); int fchmod(int fildes, mode_t mode);

Как видно из определения, функция chmod изменяет права файла по его пути, а функция fchmod — по его файловому дескриптору. В случае успешного изменения прав, возвращают 0, в случае ошибки -1. Для параметра mode определены макросы, описанные мною выше вместе с функцией open. Создадим 2 файла с правами 111:

 

1 2 touch 1.txt 2.txt chmod 111 1.txt 2.txt

Проверим их права командой ls *.txt -l:

 

1 2 ---x--x--x 1 jakeroid users 0 Dec 28 12:12 1.txt ---x--x--x 1 jakeroid users 0 Dec 28 12:12 2.txt

Теперь напишем маленькую программу, откомпилируем и запустим ее на выполнение. Текст программы:

 

1 2 3 4 5 6 7 8 9 10 11 12 13 #include &lt;stdio.h&gt; #include &lt;fcntl.h&gt; #include &lt;string.h&gt;   int main(int argc, char *argv[]) { //Изменяю права для файла 1.txt по его пути chmod("1.txt", S_IRUSR | S_IWUSR | S_IXUSR); int file = open("2.txt", O_RDWR); //Изменяю права для файла 2.txt по его дескриптору fchmod(file, S_IRGRP | S_IWGRP | S_IXGRP); close(file); }

После выполнения, права файла 1.txt изменились:

 

1 2 -rwx------ 1 jakeroid users 0 Dec 28 12:12 1.txt ---x--x--x 1 jakeroid users 0 Dec 28 12:12 2.txt

А почему не изменились у 2.txt? Все просто — его права запрещают чтение содержимого, поэтому наша программа не смогла его открыть. Если изменить ему права командой chmod 711 2.txt, и запустить нашу программу еще раз, вывод ls *.txt -l даст следующий результат:

 

1 2 -rwx------ 1 jakeroid users 0 Dec 28 12:12 1.txt ----rwx--- 1 jakeroid users 0 Dec 28 12:12 2.txt

Все заработало! Рассмотрим еще один системный вызов, для удаления файлов:

 

1 int remove (const char * filename);

Из его прототипа видно, что он удаляет файл по указанному пути. Возвращает 0 — в случае успеха, и -1 — в случае ошибки. Ничего сложного в использовании нет. Пример программы:

 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include &lt;stdio.h&gt; #include &lt;fcntl.h&gt; #include &lt;string.h&gt;   int main(int argc, char *argv[]) { char path[255]; printf("Введите название файла: "); scanf("%s", path); if (!remove(path)) printf("Файл удален!rn"); else printf("Ошибка при удалении!rn"); return 0; }

Программа спрашивает у пользователя имя файла, после чего удаляет его. Если произошла ошибка при удалении — сообщает об этом.

 

========================================================

 



Поделиться:


Последнее изменение этой страницы: 2021-06-14; просмотров: 66; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.149.26.246 (0.024 с.)