-
Notifications
You must be signed in to change notification settings - Fork 19
Sisteme de Operare
1. Fișiere
2. Fișiere
3. Procese
4. Semnale
5. Shared Memory
6. Semafoare
7. Procese
8. Fișiere
9. Directoare
10. Fișiere
UPDATE 04.04
Adăugat un "Cuprins" pentru navigarea mai ușoară a paginii
și două exerciții rezolvate date la grupa noastră în sesiunea trecută.
UPDATE 27.01 16:00 - 22:00
Adăugate încă două probleme cu procese și fișiere cu tot cu comentarii în sursă.
UPDATE 27.01 13:55
Adăugată o problemă cu Semafoare (Problema 6).
UPDATE 26.01 22:00
Adăugată o problemă cu Shared Memory (Memorie Partajată) (Problema 5).
UPDATE 26.01 18:15
M-am uitat puțin peste semafoare și am ajuns la concluzia că o să dureze ceva până o sa le înțeleg. Sper să reușesc să le bag la cap și voi posta (sper) ceva probleme cu ele mâine.
UPDATE 26.01 17:35
Am mai adăugat o problemă cu semnale (Problema 4).
UPDATE 26.01 17:00
Am scos tot ce ținea de funcția perror() că se pare că nu-i convine d-lui Baranga să folosim funcția asta la examen. (Mulțumesc, Eugen!).
Recomand să vedeți și pagina despre Pointeri.
int myFunct(char *buf,int fd, int *poz)
fd este ID-ul unui fișier din descriptorul de fișiere al programului. Se citește din fișier un caracter și se pune în buf pe poziția *poz, după care se incrementează circular *poz. Returnează 0, respectiv -1 dacă a terminat cu success, respectiv a dat de o eroare.
Rezolvare:
#include <unistd.h>
// Includ biblioteca ce conține majoritatea apelurilor de sistem.
int myFunct(char *buf, int fd, int *poz)
{
char temp;
// Citesc '1' caracter din fișierul menționat de
// descriptorul 'fd' și îl pun în 'temp'.
if( read(fd, &temp, 1) >= 0 )
{
buf[*poz] = temp;
(*poz)++;
return 0;
}
return -1;
}Linkuri ajutătoare:
int myFunct(char *f1, char *f2, int m, int n)
Se iau caracterele din fișierul f1 dintre pozițiile m și n și se pun la sfârșitul lui f2 (dacă nu există se crează). Se pun numai litere și cifre, restul caracterelor se sar. Returnează -1 în caz de eroare sau numărul de caractere puse în f2 în caz de succes.
Rezolvare:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//fcntl.h conține constantele O_RDWR, O_RDONLY, O_CREAT, etc. și funcția open()
int myFunct(char *f1, char *f2, int m, int n)
{
int fd_1, fd_2;
//Cum în cerință scrie că trebuie să iau elementele dintre 'm' și 'n',
//atunci trebuie să fiu sigur că 'm' este strict mai mic ca 'n'.
//Altfel, funcția nu ar avea sens.
if( m > n )
return -1;
//Deschid fișierul 'f1' doar în citire
//și memorez descriptorul său în fd_1
if( (fd_1 = open(f1, O_RDONLY)) < 0)
{
return -1;
}
//Deschid fișierul 'f2' în citire și scriere, având niște opțiuni
//suplimentare:
//
//O_RDWR = deschid fișierul în citire și scriere
//O_CREAT = crează fișierul dacă nu a fost creat
//O_APPEND = mută cursorul de citire/scriere la sfârșitul fișierului
//0644 = masca de drepturi pentru fișierul nou creat (dacă nu a fost
//creat deja)
if( (fd_2 = open(f2, O_RDWR | O_CREAT | O_APPEND, 0644)) < 0)
{
//nu ai putut deschide f2, dar f1 e deschis
close(fd_1);
return -1;
}
//Mut cursorul de citire la poziția 'm' a fișierului
if( lseek(fd_1, m, SEEK_SET) < 0)
{
//nu ai putut face lseek, dar ele sunt tot deschise, si asta e ideea peste tot
close(fd_1);
close(fd_2);
return -1;
}
int nr_de_char_scrisi = 0;
while(m <= n)
{
//Declar un char in care citesc din fișier.
char temp;
//Citesc un char.
if( read(fd_1, &temp, 1) < 0 )
{
close(fd_1);
close(fd_2);
return -1;
}
//Verific dacă char-ul citit este alfanumeric.
//Dacă este, atunci îl scriu în 'f2'.
if(isalnum(temp))
{
if( write(fd_2, &temp, 1) < 0 )
{
return -1;
}
nr_de_char_scrisi++;
}
m++;
}
close(fd_1);
close(fd_2);
return nr_de_char_scrisi;
}Linkuri ajutătoare:
-
open() A se observa că există două funcții având numele de
open. - close()
- lseek()
- write()
- Articol Wikipedia despre masca de drepturi
- isalnum() și altele
int myFunct(int *tab, int n)
tab este o listă de identificatori a n procese fiu. Funcția așteaptă terminarea tuturor proceselor din tabelă. Dacă cel puțin un proces nu s-a terminat, atunci se returnează -1. Dacă toate procesele s-au terminat, se returnează media aritmetică a codurilor de retur.
Rezolvare:
#include <stdlib.h>
#include <unistd.h>
int myFunct(int *tab, int n)
{
int i;
int suma_exit_statusurilor = 0;
//Luăm pe rând toate pid-urile din vector
for(i=0; i<n; i++)
{
//wait_ret = reține valoarea returnată de un apel al funcției waitpid()
int wait_ret;
//status = variabila în care se rețin informații legate de apelul waitpid()
int status;
//Facem un waitpid() neblocant (WNOHANG) asupra PID-ului curent (tab[i])
if( (wait_ret = waitpid(tab[i], &status, WNOHANG)) < 0)
{
//Deși în cerință nu scria ce să facem în cazul în care waitpid()
//eșuează am presupus că funcția ar returna, și în acest caz, -1.
return -1;
}
//În cazul în care, waitpid() returnează 0, atunci
//înseamnă că procesul curent (tab[i]) nu și-a terminat execuția.
//Deci, putem returna -1 conform cerinței.
if(wait_ret == 0)
{
return -1;
}
//Dacă waitpid() a returnat o valoare strict pozitivă,
//atunci luăm valoare de retur a procesului fiu și o adunăm
//la sumă.
suma_exit_statusurilor += WIFEXITED(status);
}
//Returnăm media aritmetică a valorilor de retur a proceselor fiu,
//având PID-urile în vectorul 'tab'.
return suma_exit_statusurilor / n;
}Linkuri ajutătoare:
-
waitpid(). Aveți aici informații și despre
WNOHANGșiWIFEXITED.
Scrieți o funcție C int myFunct(int *tab, int n, (void *)funct( int x ), int val) (sic) care lansează un proces fiu care mai întâi maschează (cum?) semnalele din vectorul tab, ce are lungime n și apoi va executa funcția desemnată de funct cu argumentul val. Se va returna -1 în caz de eroare sau dacă procesul fiu nu s-a terminat normal și 0 altfel.
NOTĂ: Nu este menționat clar cum se face mascarea (sau nu înțeleg eu), așa că am presupus că toate acele semnale din vectorul tab sunt mascate ca fiind ignorate. Totodată, header-ul dat în cerință este greșit.
Rezolvare:
int myFunct(int *tab, int n, void (funct) (int x), int val)
{
//Se creează un proces fiu. Din acest moment există două procese, ambele executând
//aceleași instrucțiuni până la al doilea 'if'.
pid_t child = fork();
//Se verifică dacă fork-ul s-a realizat cu succes. În cazul în care a eșuat, înseamnă
//că un proces nou nu a mai fost creat, iar funcția returnează -1
if(child < 0)
{
return -1;
}
//Aici se verifică dacă procesul este fiul.
//Dacă da, se execută instrucțiunile explicate în cerință.
//(dacă child este 0, atunci înseamnă că ne aflăm în fiu)
if(child == 0)
{
//Declarăm o "mulțime de semnale".
//Aceasta este un fel de vector în care sunt reținute semnalele.
//Peste un 'sigset_t' se execută operații folosind anumite funcții
//(vezi mai jos la Linkuri ajutătoare).
sigset_t sset;
//Golim mulțimea de semnale.
if( sigemptyset(&sset) < 0)
exit(1);
int i;
//Adăugăm pe rând toate elementele din `tab` în această mulțime
for(i=0; i<n; i++)
if( sigaddset(&sset, tab[i]) < 0)
exit(1);
//Acum, peste toată mulțimea, punem o mască de ignorare.
//Altfel spus, toate semnalele din 'sset' vor fi ignorate de acum în colo.
if( sigprocmask(SIG_SETMASK, &sset, NULL) < 0)
exit(1);
//Apelăm funcția din cerință.
funct(val);
exit(0);
}
//Aici suntem înapoi în procesul tată.
//Ultimul exit(0) ne asigură că procesul fiu nu v-a mai ajunge
//vreodată să execute instrucțiunile care urmează.
//Facem un 'wait' până când procesul fiu se termină de executat, după care îi vom lua
//codul de retur și, într-un final vom returna cum spune cerința.
int status;
wait(&status);
if(WIFEXITED(status) == 0)
return 0;
return -1;
}Linkuri ajutătoare:
Problemă rezolvată de Eugen.
Să se scrie o funcție int myFunc(key_t cheie) care să transforme literele mici în litere mari și invers, din stringul de la începutul segmentului de memorie desemnat de cheie.
Rezolvare:
#include <ctype.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
int myFunct(key_t cheie)
{
//Presupunem dimensiunea memoriei partajate ca fiind de 100,
//întrucât în cerință nu se zice cât este.
int dim = 100;
//Accesăm prin funcția corespunzătoare cu ajutorul cheii.
int get;
get = shmget(cheie, dim, 0666);
if(get < 0)
{
return -1;
}
//Atașăm segmentul de memorie partajat la variabila 'str'.
char *str;
str = (char *) shmat(get, NULL, 0);
if(str == (void *) -1)
{
return -1;
}
//Odată atașat, peste acest segment se pot desfășura operații
//ca în orice alt vector alocat cu 'malloc'
char *ss;
for(ss = str; (*ss) != '\\0'; ss++)
{
if(islower(*ss)) //caz literă mică
(*ss) = toupper(*ss);
else
if(isupper(*ss)) //caz literă mare
(*ss) = tolower(*ss);
}
//Detașăm segmentul de memorie.
if(shmdt(str) < 0)
{
return -1;
}
return 0;
}Linkuri ajutătoare:
Scrieți o funcție C sub forma int myFunct(int id, (int *)f(), int *ret) (sic) unde id e identificatorul unui semafor, care apelează semaforizat functia desemnată de f (înainte de apel semaforul se decrementează și după apel semaforul se incrementează). La adresa desemnată de ret se va depune codul de retur al funcției f. Se lucrează doar cu primul semafor din vector. myFunct returnează 0 în caz de succes sau -1 în caz de eroare.
Rezolvare:
#include <sys/sem.h>
//Din nou, header-ul funcției dat în cerință este greșit.
//Dacă aș fi pus '(int *) f()' în loc de 'int (f)()' mi-ar fi dat eroare la compilare.
//Presupun că header-ul e așa întrucât am dedus în același mod header-ul de la problema 4.
int myFunct(int id, int (f)(), int *ret)
{
//Pentru a executa operațiunile de incrementare / decrementare
//trebuie să definim o structură de tip 'sembuf' în care
//să detaliem ceea ce vrem să facem.
struct sembuf sb;
//Alegem primul (și, în cazul nostru, singurul) semafor
//din array-ul de semafoare.
sb.sem_num = 0;
//Selectăm tipul operațiunii
//(Număr negativ = scade valoarea semaforului cu acea valoare)
//(Număr pozitiv = crește valoarea semaforului cu acea valoare)
//(Zero = funcția va intra într-o stare de așteptare până când valoarea
//semaforului va fi zero).
sb.sem_op = -1;
//Setăm lista de flaguri, în cazul nostru rămânând vidă.
sb.sem_flg = 0;
//Executăm operația de decrementare.
if(semop(id, &sb, 1) < 0)
return -1;
//Apelăm funcția dată ca parametru și memorăm valoarea în
//locul indicat de 'ret'.
*ret = f();
//Acum va trebui să facem o incrementare. Cum am definit adineaori care semafor și ce
//flaguri avem nevoie, nu va mai trebui decât să modificăm 'sem_op' să incrementeze.
sb.sem_op = 1;
//Executarea operației de incrementare.
if(semop(id, &sb, 1) < 0)
return -1;
return 0;
}Linkuri ajutătoare:
Scrieți o funcție int *myFunct(int *n) care asteaptă terminarea tuturor proceselor fiu, pune într-un vector pid-urile acestora și returnează adresa acestuia. De asemenea *n va reține numărul de procese fiu.
Rezolvare:
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
int *myFunct(int *n)
{
//Nu știu câți fii are procesul, așa că o să presupun că are maxim 256.
int *copii = (int *) malloc(sizeof(int) * 256);
//În 'wait_stat' memorez valoarea returnată de 'wait()'
int wait_stat;
//Aici număr câți copii avea procesul în total.
int nr_de_copii = 0;
while(1)
{
//Aștept un fiu să termine.
wait_stat = wait(NULL);
//Verific dacă wait-ul s-a desfășurat cu succes.
if(wait_stat < 0)
{
//În cazul în care 'wait()' eșuează și setează errno cu
//valoarea ECHILD, înseamnă că toate procesele fiu s-au terminat.
if(errno = ECHILD)
{
//Setez *n-ul și returnez vectorul de piduri conform cerinței.
*n = nr_de_copii;
return copii;
}
//Returnez NULL dacă wait eșuează cu oricare errno în
//afară de ECHILD.
return NULL;
}
//Adaug pid-ul copilului "wait-at" în vector.
copii[nr_de_copii] = wait_stat;
nr_de_copii++;
}
}Linkuri ajutătoare:
Scrieți o funcție C int myFunct (char *f1, char *f2) care întoarce 0 dacă fișierele cu numele dat de f1 și f2 coincid, și 1 altfel. Se întoarce -1 în caz de eroare.
Rezolvare:
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
int myFunct(char *f1, char *f2)
{
//Descriptorii fișierelor 'f1', respectiv 'f2'.
int fd_1, fd_2;
//Deschid 'f1' in mod read-only.
fd_1 = open(f1, O_RDONLY);
if(fd_1 < 0)
return -1;
//Analog 'f2'.
fd_2 = open(f2, O_RDONLY);
if(fd_2 < 0)
return -1;
//Bufferii în care citesc caracterele din fișiere.
char buf1, buf2;
//Rețin aici numărul de bytes citiți la fiecare 'read'.
int bytes_cititi;
//Variabile care le setez cu '1' atunci când am ajuns
//la sfârșitul fișierului respectiv.
int end1 = 0, end2 = 0;
while(1)
{
//Citesc un byte din 'f1'.
if( (bytes_cititi = read(fd_1, &buf1, 1)) == 0 )
{
//În cazul în care am ajuns la sfârșitul fișierului, intru în acest
//'if' și îi spun programului că am ajuns la sfârșitul fișierului.
end1 = 1;
}
else
{
//Intru aici când apelul read 'eșuează'.
if(bytes_cititi < 0)
return -1;
}
//Analog ca în 'if'-ul precedent, dar pentru 'f2'.
if( (bytes_cititi = read(fd_2, &buf2, 1)) == 0)
{
end2 = 1;
}
else
{
if(bytes_cititi < 0)
return -1;
}
//Verific dacă ultimul char citit din 'f1' este identic cu
//ultimul char citit din 'f2'. Dacă da, continui. Dacă nu mă opresc.
if(buf1 != buf2)
return 1;
//Verific dacă fișierul 'f1' și 'f2' au ajuns la End-Of-File
//în același timp. Dacă da, înseamnă că fișierele sunt identice.
if(end1 == 1 && end2 == 1)
return 0;
//Dacă unul din fișiere a terminat înaintea celuilalt,
//e clar că fișierele nu sunt identice.
if(end1 != end2)
return 1;
}
}Se consideră definiția
typedef struct
{
char name[NAME_MAX];
struct stat st;
} MYSTR;Scrieți o funcție C
MYSTR *myFunct(char *dir, int *n)care construiește un vector de structuri MYSTR ce conțin fiecare numele fișierelor și caracteristicile i-nodurilor din directorul cu numele dir. n (va) comtinre (sic) numărul de elemente din vec. Se întoarce NULL în caz de eroare.
Rezolvare:
#include <dirent.h>
// Pentru operații pe directoare cum ar fi:
// * opendir()
// * readdir()
// * closedir()
#include <limits.h>
// Pentru constantele NAME_MAX și PATH_MAX
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
// Pentru funcția și structura 'stat'.
typedef struct
{
char name[NAME_MAX];
struct stat st;
} MYSTR;
MYSTR *myFunct(const char *dir, int *n)
{
// Deschidem directorul aflat la calea specificată
// de 'dir'.
DIR *dirp = opendir(dir);
if(dirp == NULL)
{
(*n) = 0;
return NULL;
}
// Citim primul fișier din director pentru a verifica
// dacă directorul a fost deschis cum trebuie și se pot
// executa operațiile de citire.
struct dirent *dirs = readdir(dirp);
if(dirs == NULL)
{
(*n) = 0;
closedir(dirp);
return NULL;
}
// Setăm numărul de fișiere cu 1 pentru că citisem
// adineaori primul fișier, apoi citim restul de fișiere
// și incrementăm n-ul de fiecare dată.
// Acest pas este important pentru a ști lungimea
// finală a vectorului de structuri 'MYSTR'.
(*n) = 1;
while( (dirs = readdir(dirp)) != NULL)
{
(*n)++;
}
closedir(dirp);
// Închidem și redeschidem directorul dat pentru a
// o lua de la capăt cu citirea datelor. De data
// asta vom citi și numele fișierelor + informațiile
// oferite de 'stat'.
dirp = opendir(dir);
if(dirp == NULL)
{
return NULL;
}
// Alocăm vectorul de structuri mai sus menționat.
MYSTR *files = (MYSTR *) malloc(sizeof(MYSTR) * (*n));
if(files == NULL)
{
closedir(dirp);
return NULL;
}
// Pentru a obține informații la nivel de sistem
// despre un anumit fișier, avem nevoie de calea lui exactă.
// Astfel, vom aloca un vector de caractere (aka string)
// de lungime PATH_MAX (lungimea maximă a unei căi oarecare
// acceptată pe un sistem de operare POSIX).
char *current_file_path = malloc(sizeof(char) * PATH_MAX);
int i = 0;
// Începem citirea riguroasă a fișierelor.
// Reamintesc că la fiecare readdir(dirp) se obține o
// referință către un alt fișier din directorul referit
// de 'dirp'. Când nu mai sunt fișiere, 'readdir' returnează 'NULL'.
while( (dirs = readdir(dirp)) != NULL)
{
// Luăm de la fiecare fișier numele și îl reținem
// în vectorul nostru de structuri.
strcpy(files[i].name, dirs->d_name);
// Aici creăm calea la fișierul curent, relativă
// la directorul 'dir'.
// Spre exemplu, dacă 'dir' = "NewFolder" și denumirea
// fișierului curent ar fi "NewFile", calea relativă
// ar fi "NewFolder/NewFile".
strcpy(current_file_path, dir);
strcat(current_file_path, "/");
strcat(current_file_path, files[i].name);
// Executăm 'stat()' și punem informațiile
// în vectorul nostru.
if( stat(current_file_path, &files[i].st) < 0)
{
free(current_file_path);
free(files);
closedir(dirp);
return NULL;
}
i++;
}
// Eliberăm memoria alocată de noi care nu mai
// folosește programului și închidem directoarele.
free(current_file_path);
closedir(dirp);
return files;
}Linkuri ajutătoare:
Scrieți o funcție C
double myFunct(char *f, double *vec, int n, int *err)unde f este numele unui fișier ce conține în format binar elemente de tip double, iar n numărul de elemente din vec.
Funcția returnează produsul scalar dintre vec și vectorul format din primele n elemente din f. Dacă sunt mai puțin de n elemente în f, atunci elementele lipsă vor fi considerate ca având valoarea 0.0. err este parametru de ieșire și va avea valoarea 0 în caz de success și -1 în caz de eroare.
Rezolvare:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
double myFunct(char *f, double *vec, int n, int *err)
{
int fd = open(f, O_RDONLY);
if(fd < 0)
{
*err = -1;
return 0.0;
}
int bytes_read;
int i = 0;
double prod_scalar = 0;
double buf;
while( (bytes_read = read(fd, &buf, sizeof(double))) >= sizeof(double) && i < n)
{
if(bytes_read == -1)
{
close(fd);
*err = -1;
return 0.0;
}
prod_scalar += buf * vec[i];
i++;
}
*err = 0;
close(fd);
return prod_scalar;
}TODO: De explicat exercițiul ăsta.