Nejdrive popiseme funkce pro souborovy vstup a vystup. Vetsina vstupne/vystupnich operaci muze byt provedena pomoci pouze peti funkci: open, read, write, lseek a close. Tyto se take nazyvaji nebufferovane I/O funkce (na rozdil napr. od tzv. standardnich vstupne/vystupnich funkci, ktere jsou bufferovane).
Soubor je otevren jadrem po volani funkce open.
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> |
int open (const char *pathname, int oflag, ... /* mode_t mode */ ); |
Vraci: deskriptor nebo -1 pri chybe |
Nekdy se pouziva i treti argument. pathname je jmeno souboru, ktery chceme vytvorit nebo otevrit. oflag je zpusob otevreni souboru; tento argument muzeme spojovat pomoci bitoveho nebo (|).
O_RDONLY | Otevreni pouze pro cteni. |
O_WRONLY | Otevreni pouze pro zapis. |
O_RDWR | Otevreni pro cteni i zapis. |
O_APPEND | Pripojeni na konec souboru. |
O_CREAT | Vytvoreni neexistujiciho souboru. Vyzaduje treti argument pro urceni prav. |
O_EXCL | Vygeneruje chybu, kdyz je pouzito zaroven s O_CREAT. Funkce jen otestuje existenci souboru. |
O_SYNC | Kazdy write ceka na fyzicke ukonceni I/O operace. |
Tento vycet neni uplny, je jen orientacni.
Deskriptor, ktery funkce vrati je nejmensi volny deskriptor systemu. Tato vlastnost je dulezita pro dalsi praci se soubory.
V systemu je definovana promenna udavajici maximalni delku jmena souboru NAME_MAX. Jestlize prekrocite delku, System V jmeno tise zkrati na odpovidajici pocet znaku. Naproti tomu BSD systemy vrati chybu ENAMETOOLONG. Tento problem neni jen u vytvareni novych souboru.
Novy soubor muzeme vytvorit take volanim funkce create.
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> |
int creat (const char *pathname, mode_t mode); |
Vraci: deskriptor otevreny jen pro zapis nebo -1 pri chybe |
Tato funkce je ekvivalentni volani
open (pathname, O_WRONLY | O_CREAT | O_TRUNC | mode);
pathname je jmeno souboru, ktery chceme vytvorit nebo otevrit.
Parametr mode se tyka prav -- vysvetlime pozdeji
(v
Tato funkce zavre otevreny soubor.
Uzavreni souboru zpusobi take odemceni vsech zamku na soubor.
(Se zamky pracuji funkce fcntl, lockf, flock.
Zde plati: ruzny system -- ruzny zpusob zamykani.).
Kazdy otevreny soubor ma svuj current file offset. Toto
nezaporne cislo ukazuje pocet bajtu od pocatku souboru. Normalni I/O
operace zacinaji od zacatku, tj. offset 0 (kdyz neni specifikovana
podminka O_APPEND). Pozice ukazovatka v souboru muze byt
explicitne nastavena pomoci lseek.
Interpretace offset zavisi na parametru whence, ktery muze
byt:
Muzeme tedy snadno previnout soubor na zacatek. Tato technika se
pouziva pro urceni, zda dane zarizeni umoznuje seek.
Data ze souboru obdrzime volanim funkce read.
Existuje nekolik pripadu, ve kterych je pocet nactenych bajtu mensi, nez
kolik pozadujeme:
Data zapiseme do souboru pomoci funkce write.
Data se zapisuji na aktualni pozici (offset).
Jako ilustraci dulezitosti nastaveni spravne velikosti bufferu pro I/O
operace si ukazeme program a tabulku vysledku s timto programem
dosazenych.
Program kopiruje soubor za pouziti funkci read a write a
nasledujicich predpokladu.
Timto programem autori knihy [7] zkouseli kopirovat soubor
o velikosti 1,468.802 bajtu, pri pouziti nekolika ruznych velikosti
bufferu. Poznamenejme, ze standardni vystup byl presmerovan do
/dev/null (do tzv. cerne diry). Soubor byl ulozen na
Berkeley filesystemu s velikosti bloku 8192 bajtu. Proto zvetsovani
bufferu nad tuto hodnotu nemelo jiz vyznam pro zrychleni. Vysledky
shrnuje tabulka 7.
Unix podporuje sdileni souboru mezi ruznymi procesy. Pred popisem funkce
dup musime toto sdileni blize popsat. Jadro pouziva celkem tri
datove struktury:
Obrazek 1 ukazuje usporadani techto tri tabulek pro jeden
proces, ktery ma otevrene dva ruzne soubory -- jeden soubor je otevreny
na standardnim vstupu (deskriptor cislo 0) a druhy je otevreny na
standardnim vystupu (deskriptor 1).
Obrazek 1:
Struktury v jadre pro otevrene soubory
Pokud si dva nezavisle procesy otevrou ten samy soubor, nastane situace,
ktera je zobrazena na obr. 2.
Obrazek 2:
Dva procesy maji otevren stejny soubor
V pripade, ze unix nepodporuje priznak O_APPEND, lze ho
nahradit:
Tento scenar pracuje dobre pro jeden proces, ale ve viceulohovem
prostredi muze byt zdrojem potizi. Potize vznikaji prave proto, ze
takovato operace neni atomicka, tj. neni dale nedelitelna.
Proto je nutne zde pouzit atomickou operaci open s priznakem
O_APPEND.
Obdobne potize mohou vzniknout pri vytvareni souboru.
Problem muze nastat mezi funkcemi open a create.
Existujici deskriptor lze duplikovat pomoci nasledujicich funkci:
Novy deskriptor je nejnizsi volny deskriptor (ve stejne tabulce souboru
jako stary).
S dup2 specifikujeme novy deskriptor hodnotou filedes2.
Jestlize je deskriptor filedes2 otevren, pak se nejdriv uzavre.
Na obrazku 3 je ilustrovana situace po provedeni:
Obrazek 3:
Souborove struktury v jadre po dup(1)
Tato funkce muze zmenit charakter otevreneho souboru.
Funkce fcntl se da pouzit pro tyto ucely:
Funkce ioctl je urcena k provadeni ridicich operaci nad I/O
zarizenim.
Funkce se pouziva ke specialnim operacim s disky, paskou, terminalem
atd.
Funkce close
#include <unistd.h> int close (int filedes); Vraci: 0 kdyz OK, -1 pri chybe
Funkce lseek
#include <sys/types.h>
#include <unistd.h>off_t lseek (int filedes, off_t offset, int whence);
Vraci: novy offset kdyz je vse OK, -1 pri chybe
#include <sys/types.h>
int
main(void)
{
if (lseek(STDIN_FILENO, 0, SEEK_CUR) == -1)
printf("cannot seek\n");
else
printf("seek OK\n");
exit(0);
}
Funkce read
#include <unistd.h> ssize_t read (int filedes, void *buff, size_t nbytes);
Vraci: pocet nactenych bajtu, 0 kdyz konec, -1 pri chybe
Funkce write
#include <unistd.h> ssize_t write (int filedes, void *buff, size_t nbytes);
Vraci: pocet zapsanych bajtu, -1 pri chybe
Ucinnost I/O
#define BUFFSIZE 8192
int
main(void)
{
int n;
char buf[BUFFSIZE];
while ( (n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0)
if (write(STDOUT_FILENO, buf, n) != n)
err_sys("write error");
if (n < 0)
err_sys("read error");
exit(0);
}
BUFFSIZE
User CPU
System CPU
(sekundy) Clock time
(sekundy) pocet cyklu
(sekundy) 1
23.8
397.9
423.4
1468802 2
12.3
202.0
215.2
734401 4
6.1
100.6
107.2
367201 8
3.0
50.7
54.0
183601 16
1.5
25.3
27.0
91801 32
0.7
12.8
13.7
45901 64
0.3
6.6
7.0
22950 128
0.2
3.3
3.6
11475 256
0.1
1.8
1.9
5738 512
0.0
1.0
1.1
2869 1024
0.0
0.6
0.6
1435 2048
0.0
0.4
0.4
718 4096
0.0
0.4
0.4
359 8192
0.0
0.3
0.3
180 16384
0.0
0.3
0.3
90 32768
0.0
0.3
0.3
45 65536
0.0
0.3
0.3
23 131072
0.0
0.3
0.3
12
Sdileni souboru
Atomicke operace
Pripojeni na konec souboru
if (lseek(fd, 0L, 2) < 0 )
err_sys("lseek error");
if (write(fd, buff, 100) != 100)
err_sys("write error");
Vytvareni souboru
if ( (fd = open(pathname, O_WRONLY)) < 0)
if (errno == ENOENT) {
if (fd = creat(pathname, mode)) < 0)
err_sys("creat error");
}
else err_sys("open error");
Funkce dup a dup2
#include <unistd.h> int dup (int *filedes);
int dup2 (int *filedes, int filedes2);
Vraci: novy deskriptor nebo -1 pri chybe
newfd = dup(1);
Funkce fcntl
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>int fcntl (int *filedes, int cmd, ...
/* int arg */ );
Vraci: kdyz OK, zalezi na cmd nebo -1 pri chybe
Funkce ioctl
#include <unistd.h> /* SVR4 */
#include <sys/ioctl.h> /* BSD 4.3+ */int ioctl (int *filedes, int request, ...);
Vraci: -1 pri chybe, neco jineho kdyz je OK
Cviceni
Dalsi: Soubory a adresare
Predchozi: Programatorske zaklady
Sat Nov 1 15:38:32 MET 1997