Creeaza.com - informatii profesionale despre


Simplitatea lucrurilor complicate - Referate profesionale unice
Acasa » scoala » informatica
Apeluri sistem de baza pentru lucrul cu fisiere

Apeluri sistem de baza pentru lucrul cu fisiere


Apeluri sistem de baza pentru lucrul cu fisiere

1. Scopul lucrarii

Lucrarea de fata prezinta apelurile sistem uzuale folosite in operatiile de intrare - iesire cu fisiere. Operatiile de I/E pe fisiere pot fi realizate folosind cinci functii: open, read, write, lseek si close. Aceste functii sunt referite ca I/E fara tampon, deoarece ele determina un apel sistem in nucleu (kernel).

Partajarea resurselor intre procese tine cont de conceptul de operatie atomica. Se evidentiaza acest concept prin folosirea adecvata a argumentelor apelului sistem open.

2. Consideratii teoretice

2.1. Descriptori de fisier

Pentru kernel toate fisierele deschise sunt referite de un descriptor de fisier = un intreg pozitiv. La deschiderea unui fisier sau la crearea unui fisier nou, kernel-ul returneaza un descriptor de fisier procesului care a executat apelul.



Fiecare proces Unix are la dispozitie 20 (in versiuni mai noi numarul a fost extins la 64) de descriptori fisier. Prin conventie, primii trei sunt deschisi automat la inceputul unui proces. Descriptorul de fisier 0 identifica intrarea standard, 1 identifica iesirea standard, iar 2 iesirea standard de erori. Cei 17 descriptori ramasi pot fi folositi de proces pentru deschiderea de fisiere ordinare, pipe, speciale sau directoare.

Exista cinci apeluri sistem care genereaza descriptori de fisiere: creat, open, fcntl, dup si pipe. În lucrare vor fi descrise primele doua apeluri sistem. Cele ramase, vor fi prezentate in lucrarile urmatoare.

2.2. Apeluri sistem

Fiecare apel sistem este prezentat folosind definirea sau prototipul functiei. Aceasta abordare permite precizarea semnificatiei si tipului fiecarui argument si tipul valorii returnate.

2.2.1. Apelul sistem OPEN

Deschiderea sau crearea unui fisier se poate face prin apelul sistem open. Sintaxa acestui apel este:

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

int open( const char *path, int flags, /* mode_t mod */);

Returneaza descriptorul de fisier sau -1 in caz de eroare.

Numarul de argumente al acestei functii poate fi doi sau trei. Argumentul al treilea este folosit doar la crearea de fisiere noi. Apelul cu doua argumente este folosit pentru citirea si scrierea fisierelor existente. Functia returneaza cel mai mic descriptor de fisier disponibil. Acesta poate fi utilizat in apelurile sistem: read, write, lseek si close. IDU efectiv sau GIDU efectiv al procesului care executa apelul trebuie sa aiba drepturi de citire/scriere, functie de valoarea argumentului flags. Pointerul din fisier este pozitionat pe primul octet din fisier.

Argumentul flags se formeaza printr-un SAU pe biti intre constantele:

O_RDONLY Fisierul este deschis in citire.

O_WRONLY Fisierul este deschis in scriere.

O_RDWR Fisierul este deschis in adaugare (citire+scriere).

Acestea sunt definite in fisierul antet fcntl.h.

Urmatoarele constante simbolice sunt optionale:

O_APPEND Scrierile succesive se produc la sfarsitul fisierului.

O_CREAT Daca fisierul nu exista el este creat.

O_EXCL Daca fisierul exista si O_CREAT este pozitionat, apelul open esueaza.

O_NDELAY La fisiere pipe si cele speciale pe bloc sau caracter cauzeaza trecerea in modul fara blocare atat pentru apelul open cat si pentru operatiile viitoare de I/E. In versiunile noi System V inlocuit cu O_NONBLOCK.

O_TRUNC Daca fisierul exista ii sterge continutul.

O_SYNC Forteaza scrierea efectiva pe disc prin write. Întarzie mult intregul sistem, dar e eficace in cazuri critice.

Argumentul al treilea, mod, poate fi o combinatie de SAU pe biti intre constantele

simbolice:

S_IRUSR, S_IWUSR, S_IXUSR User: read, write, execute

S_IRGRP, S_IWGRP, S_IXGRP Group: read, write, execute

S_IROTH, S_IWOTH, S_IXOTH Other: read, write, execute

Acestea definesc drepturile de acces asupra unui fisier si sunt definite in fisierul antet

sys/stat.h.

2.2.2. Apelul sistem CREAT

Un fisier nou este creat prin:

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

int creat( const char *path, mode_t mod);

Returneaza descriptorul de fisier sau -1 in caz de eroare.

Apelul este echivalent cu:

open( path, O_WRONLY | O_CREAT | O_TRUNC, mod);

Argumentul path specifica numele fisierului, iar mod drepturile de acces.

Daca fisierul creat nu exista, este alocat un nou i-node si o legatura spre el este plasata in directorul unde acesta a fost creat. Proprietarul (IDU efectiv si GIDU efectiv) procesului care executa acest apel trebuie sa aiba permisiunea de scriere in director. Fisierul deschis va avea drepturile de acces specificate de argumentul al doilea din apel (vezi umask). Apelul intoarce cel mai mic descriptor de fisier disponibil (vezi pipe). Fisierul este deschis in scriere iar dimensiunea sa initiala este 0. Timpii de acces si modificare din i-node sunt actualizate.


Daca fisierul exista (este nevoie de permisiunea de cautare in director) continutul lui este pierdut si el este deschis in scriere. Nu se modifica proprietarul sau drepturile de acces ale fisierului. Argumentul al doilea este ignorat.

2.2.3. Apelul sistem READ

Pentru a citi un numar de octeti dintr-un fisier, de la pozitia curenta, se foloseste apelul read. Sintaxa este:

#include <unistd.h>

ssize_t read( int fd, void *buf, size_t noct);

Returneaza numarul de octeti cititi efectiv, 0 la EOF, -1 in caz de eroare.

Citeste noct octeti din fisierul deschis referit de fd si ii depune in tamponul referit de buf. Pointerul in fisier este incrementat automat dupa o operatie de citire cu numarul de octeti cititi. Procesul care executa o operatie de citire asteapta pana cand kernel-ul depune datele de pe disc in bufferele cache. În mod uzual, kernel-ul incearca sa iuteasca operatiile de citire citind in avans blocuri de disc consecutive pentru a anticipa eventualele cereri viitoare.

2.2.4. Apelul sistem WRITE

Pentru a scrie un numar de octeti intr-un fisier, de la pozitia curenta, se foloseste apelul write. Sintaxa este:

#include <unistd.h>

ssize_t write( int fd, const void *buf, size_t noct);

Returneaza numarul de octeti scrisi si -1 in caz de eroare.

Apelul scrie noct octeti din tamponul buf in fisierul a carui descriptor este fd.

Interesant de semnalat la acest apel este faptul ca scrierea fizica pe disc este intarziata. Ea se efectueaza la initiativa nucleului fara ca utilizatorul sa fie informat. Daca procesul care a efectuat apelul, sau un alt proces, citeste datele care inca nu au fost scrise pe disc, kernel-ul le citeste inapoi din bufferele cache. Scrierea intarziata este mai rapida, dar are trei dezavantaje: a) o eroare disc sau caderea kernelului produce pierderea datelor; b) un proces care a initiat o operatie de scriere nu poate informat in cazul aparitiei unei erori de scriere; c) ordinea scrierilor fizice nu poate fi controlata.

Pentru a elimina aceste dezavantaje, in anumite cazuri, se foloseste fanionul O_SYNC. Dar cum aceasta scade viteza sistemului si avand in vedere fiabilitatea sistemelor UNIX de astazi se prefera mecanismul de lucru cu tampoane cache.

2.2.5. Apelul sistem CLOSE

Pentru a disponibiliza descriptorul atasat unui fisier in vederea unei noi utilizari se foloseste apelul close.

#include <unistd.h>

int close( int fd);

Returneaza 0 in caz de succes si -1 in caz de eroare.

Apelul nu goleste bufferele kernel-ului si deoarece fisierul este oricum inchis la terminarea procesului apelul se considera a fi redundant.

2.2.6. Apelul sistem LSEEK

Pentru pozitionarea absoluta sau relativa a pointerul de fisier pentru un apel read sau write se foloseste apelul lseek.

#include <sys/types.h>

#include <unistd.h>

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

Returneaza un deplasament in fisier sau -1 in caz de eroare.

Nu se efectueaza nici o operatie de I/O si nu se trimite nici o comanda controlerului de disc. Daca interp este SEEK_SET pozitionarea este fata de inceputul fisierului (primul octet din fisier este la deplasament zero). Daca interp este SEEK_CUR pozitionarea este relativa la pozitia curenta. Daca interp este SEEK_END pozitionarea este fata de sfarsitul fisierului.

Apelurile open, creat, write si read executa implicit lseek. Daca un fisier este deschis folosind constanta simbolica O_APPEND se efectueaza un apel lseek la sfarsitul fisierului inaintea unei operatii de scriere.

2.2.7. Apelul sistem LINK

Pentru a adauga o noua legatura la un director se foloseste apelul:

#include <unistd.h>

int link(const char *oldpath, const char newpath);

Returneaza 0 in caz de reusita si -1 in caz contrar.

Argumentul oldpath trebuie sa fie o legatura existenta, ea furnizeaza numarul i-node-ului.

Daca legaturile . si .. din fiecare director sunt ignorate structura sistemului de fisiere este arborescenta. Programe care parcurg structura ierarhica (de exemplu, comanda find) pot fi usor implementate fara probleme de traversare multipla sau bucla infinita. Pentru a respecta aceasta cerinta doar superuser-ul are dreptul sa stabileasca o noua legatura la un director. Crearea celei de a doua legaturi la un director este utila pentru a-l putea muta in alta parte a arborelui sau pentru a-l putea redenumi.

2.2.8. Apelul sistem UNLINK

Pentru a sterge o legatura (cale) dintr-un director se foloseste apelul:

#include <unistd.h>

int unlink( const char *path);

Returneaza 0 in caz de reusita si -1 in caz contrar.

Apelul decrementeaza contorul de legaturi din i-node si sterge intrarea director. Daca acesta devine 0 spatiul ocupat de fisierul in cauza devine disponibil pentru o alta utilizare, la fel si i-node-ul. Doar superuser-ul poate sa stearga un director.

2.3. Implementarea semafoarelor prin fisiere

Apelul sistem creat esueaza daca fisierul exista si daca nu exista permisiunea de scriere. Acest lucru permite utilizarea unui fisier pe post de semafor. Se poate astfel crea un mecanism de acces la o resursa partajabila. Doua procese cu acces exclusiv la resursa vor executa urmatoarea secventa:

a)   Înainte de a accesa resursa, procesele incearca sa creeze un fisier (cu nume cunoscut) dar fara permisiunea de scriere.

b)   Ambele procese vor executa creat, dar doar unul din ele va reusi. Procesul care nu reuseste ramane in starea de asteptare.

c)   Procesul care a reusit apelul creat executa codul propriu de lucru cu resursa dupa care elibereaza resursa prin stergerea fisierului semafor, printr-un apel unlink.

in mod paradoxal, facilitatea aceasta poate fi exploatata numai de utilizatorii obisnuiti (fara drept de superuser), deoarece un apel creat executat de superuser truncheaza fisierul indiferent de drepturile sale de acces.

Protocolul de semafor poate fi descris prin doua functii, lock si unlock, care respecta secventa:

if ( lock('semaf')) else

n-a reusit lock

Numele semaf este arbitrar, dar este de dorit ca el sa fie unic. Functia lock selecteaza dintre procesele care executa concurent aceasta secventa de cod, un singur proces care va executa secventa protejata unul singur.

Codul functiei lock si unlock:

#define LOCKDIR '/tmp/'

#define MAXINCERC 3

#define WAITTIME 5

BOOLEAN lock( char *name)

if ( fd < 0 || close(fd) < 0)

err_sys('lock');

return(TRUE);

unlock( char *name)

static char *lockpath( char *name)

Functia lockpath genereaza un nume de fisier care se utilizeaza ca semafor. Acest fisier este creat in directorul /tmp, deoarece acest director exista in orice sistem UNIX si oricine are permisiuni de scriere acolo.

Daca apelul creat nu reuseste se testeaza variabila errno, deoarece singurul caz acceptat este absenta dreptului de scriere ('permision denied'). Constanta simbolica EACCES este definita in fisierul errno.h.

Numarul de incercari de a crea fisierul semafor nu depaseste MAXINCERC si timpul scurs intre incercari succesive e WAITTIME.

Functia unlock trebuie doar sa stearga fisierul.

Constanta simbolica O_EXCL ofera o cale generala (atat pentru superuser) de folosire a unui fisier ca semafor. Ca atare, in functia lock in locul liniei:

while (( fd=creat( path, 0)) < 0 && errno == EACCES)

Structura DIR este o structura interna folosita de aceste patru functii pentru a pastra

informatii despre directorul citit. Primul apel readdir citeste prima intrare dintr-un director. Ordinea intrarilor dintr-un director este dependenta de implementare. De regula nu este alfabetica.

3. Aplicatii

3.1. Sa se scrie un program care creeza un fisier cu o zona libera de 20 de octeti. Nu

conteaza continutul fisierului.

#include <sys/types.h>

#include <sys/stat.h>

#include 'fcntl.h'

#include 'hdr.h'

char buf1[]='Laborator ';

char buf2[]='SO Unix an IV+V';

int main( void)

Efectul executiei programului se poate vedea prin comanda:

$od c file.gol

Fisierul antet hdr.h, va fi inclus de multe din programele din lucrarile urmatoare. Continutul sau este urmatorul:

#ifndef __hdr_h

#define __hdr_h

#include <sys/types.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#define MAXLINE 1024

#define FILE_MODE ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

#define DIR_MODE ( FILE_MODE | S_IXUSR | S_IXGRP | S_IXOTH)

#define ever (;;)

typedef void Sigfunc( int); /* tipul rutinei de tratare */

void print_exit( int); /* lucrarea 10 */

void print_mask( const char*); /* lucrarea 10 */

Sigfunc *signal_intr( int, Sigfunc *); /* lucararea 10,11 */

void err_sys( const char *, );

void err_quit( const char *, );

void err_dump( const char *, );

void err_ret( const char *, );

void err_msg( const char *, );

#endif /* __hdr_h */

3.2. Sa se scrie un program care copiaza continutul unui fisier existent intr-un alt fisier. Numele celor doua fisiere se citesc ca argumente din linia de comanda. Se presupune ca oricare dintre apelurile sistem read sau write poate genera erori.

#define BUFSIZE 512

void copy( depe, pe)

char *depe, *pe;

while ( nw < nr);

}

close( depefd); close(pefd);

3.3. Apelurile sistem intorc de regula o valoare. În general, daca un apel sistem intoarce valoarea (-1), apelul respectiv nu a reusit. În acest caz variabila errno contine un cod de eroare. Aflarea erorii propriu-zise se poate face prin apelul functiei strerror cu argumentul valoarea variabilei errno. Netratarea erorilor in cazul operatiilor de I/E poate conduce la erori greu de depistat. Cunoscand aceste informatii sa se scrie o familie de functii utile in tratarea erorilor Acestea sa fie cuprinse in modulul err.c. Modulul poate fi compilat la err.o si inclus in linia de comanda la compilarea oricarui program ce foloseste aceste functii.

/* A se compila cu: gcc c err.o err.c */

#include <errno.h>

#include <stdarg.h>

#include 'hdr.h'

static void err_do( int, const char *, va_list);

char *pname=NULL;

/* Eroare fatala datorata unui apel Eroare fatala independenta de apelul sistem. Afiseaza mesaj si termina sistem. Afiseaza mesaj si termina procesul. procesul. */

void  void

err_sys(const char *frmt,) err_quit(const char *frmt,)

}

/* Eroare nefatala datorata unui apel Eroare nefatala independenta de un

sistem. Afiseaza mesaj si revine. apel sistem. Afiseaza mesaj si revine */

void  void

err_ret(const char *frmt, ) err_msg(const char *frmt, )

}

/* Eroare fatala relativa la un apel sistem.

Afiseaza mesaj, dump core si termina procesul. */

void

err_dump( const char *frmt, )

static void

err_do( int errfan, const char *frmt, va_list ap)

4. Probleme propuse spre rezolvare

4.1. Ce se poate spune despre deplasamentul in cazul apelului lseek ? Cum se poate afla dimensiunea unui fisier ?

4.2. Folosind apelurile de sistem prezentate scrieti un program C care sa afiseze in ordine inversa liniile unui fisier.

4.3. Sa se scrie un program care citeste si tipareste caracterele 0, 20, 40 , dintr-un fisier creat anterior.

4.4. Un fisier deschis in citire si scriere cu O_APPEND, poate fi citit si scris de oriunde folosind lseek ? Sa se scrie un program care lansat in background de N ori scrie intr-un fisier ID procesului curent. Nici unul dintre programe nu poate sa-si continue executia pana ce toate procesele nu si-au scris ID propriu in fisier. În final fiecare proces afiseaza ID procesului urmator.

4.5 Sa se scrie un program care sa permita scrierea unor siruri de caractere din intrarea standard intr-un fisier, incepand cu o anumita pozitie. Apelul programului se face sub forma:

rw poz fis

4.6. Se considera programul:

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include 'hdr.h'

void main( void)

Considerand ca fisierul temp exista si programul este lansat in background sa se explice rezultatul comenzilor:

$ ls -l temp; df /home; a.out &

$ ls -l temp; df /home; # se asteapta 10s

$ df /home

Ce concluzie se poate trage legat de apelul sistem unlink ?

4.7. Sa se rescrie functia lock astfel incat ea sa expandeze numele fisierului semafor cu numele de login (se va folosi functia getlogin). Sa se adauge un argument functiei, care in caz ca functia returneaza FALSE, sa permita afisarea numelui utilizatorului care incearca lock.

4.8. Cunoscand structura unui director sa se scrie un program care sa afiseze continutul tuturor intrarilor director (numele fisierului si i-node-ul asociat) folosind apelul sistem open. Calea completa spre director se specifica in linia de comanda.

4.9. Sa se rescrie programul anterior folosind functii ce lucreaza cu directoare.

4.10. Sa se scrie un program care elimina tot al cincilea octet dintr-un fisier. Observatie: nu se va folosi un fisier temporar.

4.11. Sa se scrie un pachet B-tree. Sa se implementeze functii pentru crearea fisierelor

B-tree, pentru deschiderea si inchiderea unui fisier; functii pentru memorarea, selectarea si stergerea articolelor. Avantajul major al acestui pachet este faptul ca articolele sunt in ordine. Sa se scrie si o functie de parcurgere.





Politica de confidentialitate


creeaza logo.com Copyright © 2024 - Toate drepturile rezervate.
Toate documentele au caracter informativ cu scop educational.