Операционни системи, тема 5

Системни примитиви за работа с файлове.


страницата се нуждае от дописване/преглеждане, щото някои работи не са ми съвсем ясни


Презентацията на преподавателя

http://newkis.fmi.uni-sofia.bg/~moni/osKN_ppt/fsys2_SC.ppt


Важна част от работата на всяка операционна система е работата с файлове, и затова всяка ОС има средства за това. Тези средства са системни примитиви (системни извиквания (system calls), системни функции) на съответната система.

В стандарта POSIX (Portable Operating System Interface for Unix) са дефинирани системните примитиви за достъп до файлове за тези операционни системи, които се придържат към него1 - например MINIX, UNIX, Linux, Solaris, FreeBSD и др. . Ще разгледаме някои основни POSIX понятия и команди.

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

  • Файлов дескриптор - Неотрицателно цяло число, което служи за уникален идентификатор на отворен файл. При отваряне на файл, ядрото извършва необходимите действия за отваряне на файла, зарежда информация за него. След това връща файловия дескриптор, който се свързва с отворения файл, ползва се в програмата, а след затваряне се освобождава. Дескрипторът има локално значение - връзката между дескриптор (число) и отворен файл важи само за текущия процес. Всеки процес стандартно има отворени три дескриптора:
    • 0 (стандартен вход);
    • 1 (стандартен изход);
    • 2 (стандартен изход за грешки).
  • Текуща позиция във файл (file offset, file pointer) - Определя позицията на файла за четене и писане (един указател за двете действия). От гледна точка на ядрото файлът представлява масив от байтове. Отместването указва броя байтове от началото на файла до текущата позиция. Всеки файлов дескриптор има собствена позиция, която се променя от извършваните върху него процедури за четене и писане.
  • Режим на отваряне - Указва режима, в който е отворен даден дескриптор. При опит за четене / писане, в зависимост от режима, операцията се позволява или забранява. Основните режими са четене, писане, четене и писане.

Системни примитиви за работа с обикновен файл

Ще разгледаме някои функции във вариант за C/C++.

Създаване на файл

int creat(const char *filename, mode_t mode);

Създава обикновен файл (не е специален - устройство, или връзка - link) с име filename в режим mode. Името на файла може да е абсолютно или относително. Режимът представлява съкратен код на защитата (permissions на потребителите за този файл) и се указва с 12-битово двоично число, което може да бъде записано като 4-цифрено осмично. mode_t е тип, дефиниран в хедъра <sys/types.h> - така се осигурява по-голяма междуплатформена съвместимост.
Връща дескриптора на файла, за който се назначава най-ниското свободно число, или -1 (невалиден дескриптор) при грешка.

Отваряне на файл

#include <fcntl.h>
int open(const char *filename, int oflag [, mode_t mode]);

Отваря файла filename в режим на отваряне oflag, а ако не съществува, го създава в режим mode. Създаването е позволено в по-нови версии на UNIX/Linux, в които creat() е запазена само за съвместимост. Така следните две са еквивалентни:
creat(path, mode)
open(path, O_WRONLY|O_CREAT|O_TRUNC, mode)

oflag указва режима на отваряне и/или действия, които да се извършат след отваряне. Може да се зададе със символични константи, дефинирани в хедъра fcntl.h. Част от тях:
  • O_RDONLY - само за четене;
  • O_WRONLY - само за писане;
  • O_RDWR - за четене и писане;

Едно от тези е задължително. Към него чрез побитово или може да се добавят някои от следните:

  • O_CREAT - създава файла;
  • O_EXCL - заедно с O_CREAT: ако файлът не съществува, той се създава, иначе се връща грешка;
  • O_TRUNC - старото съдържание на файла се изтрива след отваряне;
  • O_APPEND - старото съдържание се запазва, а новото се вписва в края на файла (при писане, текущата позиция автоматично отива в края на файла);
  • O_SYNC - всяко писане е синхронно (за обикновени файлове, по подразбиране се използва delayed write - данните се записват в буферния кеш, а ядрото се грижи за тяхното действително записване).

Връща дескриптора на файла или -1 при грешка.

Затваряне на файл

int close(int fd);

Затваря файла и освобождава файловия дескриптор. Връща 0 при успех и -1 при грешка.

Четене и писане във файл

ssize_t read(int fd, void *buffer, size_t nbytes);
ssize_t write(int fd, void *buffer, size_t nbytes);
  • ssize_t - разновидност на познатия ни size_t, но със знак;
  • fd - номер на файлов дескриптор;
  • buffer - указател към програмния буфер - областта от програмата, където се записват данните (void* е сложено за универсалност);
  • nbytes - указва броя байтове за четене / писане. Четенето / писането започва от текущата позиция във файла. Може тя да е след края на файла, което не се счита за грешка.
    • При четене след края на файла, връща се 0.
    • При писане след края на файла: ако е указан режим O_APPEND, писането се извършва в края на файла. В противен случай, байтовете се пишат на текущата позиция, а размерът на файла се увеличава с броя записани байтове плюс размера на "дупката" между тях и EOF - т. нар. файлове с дупки

Файл с дупки:

x x x x x
^fp ^EOF дупка ^нов fp

При указан режим O_SYNC write() връща управление чак след действителното дисково записване; иначе, ползва се delayed write (виж по-горе). При O_SYNC, целият файл се презаписва на диска, независимо каква част от него е променена, което не е ефективно. Режимът се ползва за сигурен запис.
Връща се броят действително прочетени / записани байтове (може да е по-малък от nbytes), 0 при EOF, -1 при грешка.

Позициониране във файл

off_t lseek(int fd, off_t offset, int flag);

off_t обикновено указва long. offset е самото отместване. flag указва как се интерпретира отместването, за да се получи новият file pointer:
  • SEEK_SET (стойност 0): fp = offset
  • SEEK_CUR (стойност 1): fp = fp + offset
  • SEEK_END (стойност 2): fp = file_size + offset (да, "+" е - внимавайте!)

Най-бърз system call - не се извършват дискови операции и не се променя размерът на файла.
lseek(file, 0, 1) връща текущата позиция във file.
Връща новата позиция при успех.

Информация за файл

#include <sys/stat.h>
int stat(const char * filename,struct stat* sbuf); 
int fstat(int fd, struct stat* sbuf);

filename е знаков низ - име на файла. fd е файлов дескриптор. stat е структура, дефинирана в хедъра. Елементите `и придобиват и връщат атрибутите на файла.
Връща 0 при успех, както и стойностите в структурата, -1 при неуспех (например несъществуващ файл).

Примерни процедури

Създава ново копие на съществуващ файл, чието име е зададено като първи аргумент, а името на създавания файл, като втори.

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#define BUFS 4096

main(int argc, char *argv[])
{
int fdr, fdw, n;
struct stat sbuf;
char buff[BUFS];

if (argc != 3) {
    fprintf(stderr, "usage: copy from_file to_file\n");
    exit(1); }

if ( stat(argv[1], &sbuf) == -1) {
    fprintf(stderr, "copy: %s: No such file\n", argv[1]);
    exit(1); }

if ( ! S_ISREG(sbuf.st_mode) ) {
    fprintf(stderr, "copy: %s: Not a regular file\n", argv[1]);
    exit(1); }

if (( fdr = open(argv[1], O_RDONLY)) == -1) {
    fprintf(stderr, "copy: %s: can't open\n", argv[1]);
    exit(1); }

if (( fdw = creat(argv[2], 0640)) == -1) {
    fprintf(stderr, "copy: %s:can't create\n", argv[2]);
    exit(1); }

while (( n = read(fdr, buff, BUFS)) > 0)
        write(fdw, buff, n);
exit(0);
}

Извежда на стандартния изход съдържанието на файлове, чиито имена са зададени като аргументи.

#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
main(int argc, char *argv[])
{
int fdr, i;
struct stat sbuf;
char buff;

if (argc < 2) {
   fprintf(stderr,"usage: typef file...\n");
   exit(1); }

 for ( i=1; i<argc; i++ ) {
    if ( stat(argv[i], &sbuf) ==-1) {
      fprintf(stderr,"typef: %s: No such file\n", argv[i]);
      continue; }

    if ( ! S_ISREG(sbuf.st_mode) )  {
      fprintf(stderr,"typef: %s: is not a regular file\n", argv[i]); 
      continue; }

    if (( fdr = open(argv[i], O_RDONLY)) == -1) {
      fprintf(stderr, "typef: %s: can't open\n", argv[i]);
      continue; }

    while ( read(fdr, &buf, 1) ) 
             write(1, &buf, 1);
    close(fdr);
 }
}

Допълнителни неща

SUS (Single UNIX Specification), "наследникът" на POSIX.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License