Unix фајл систем

Извор: ВикиЕТФ

Unix има обједињени фајл систем за разлику од Windows оперативног система где свака партиција има засебно стабло. Све партиције, и сви фајлови се налазе испод коренског директоријума (/). Монтирањем повезујете партиције са тачкама у фајл систему. То је згодно због безбедности и лакоће одржавања.

Осим што има обичне фајлове и директоријуме Unix воли да држи на фајл систему и тачке приступа (енг. socket), фајлове уређаја,... То увелико олакшава посао програмерима који пишу програме, јер уместо да измишљају посебне механизме за рад са уређајима могу све да обједине у фајл систем. Можда ћете помислити да оваква архитектура личи помало на Франкештајна али нисте управу јер је управо био циљ да се ствари поједноставе и буду лакше за одржавање.

Садржај

Путање

На Unix-у се путања до сваког фајла на систему може рећи на два начина, кроз релативну и апсолутну путању. Релативна путања означава локацију фајла у односу на текући директоријум (dir/file.txt), док апсолутна то ради у односу на коренски директоријум (/home/user/dir/file.txt).

$ pwd
/usr/share/doc
$ cd ../../bin
$ pwd
/usr/bin

Прво прво смо се налазили у директоријуму /usr/share/doc а потом смо са релативном путањом ушли у /usr/bin директоријум. За разумевање релативне путање у овом примеру потребно је знати да сваки директоријум у себи садржи два фајла који су уствари чврсте везе (енг. hard links). Веза "." показује на текући директоријум a ".." показује на родитеља. Једино код коренског директоријума су ова два показивача иста.

$ ./neki_program

Овде видимо да када користимо тачку ми само прецизирамо командном окружењу да изврши фајл из текућег директоријума уместо да тражи кроз директоријуме "$PATH" променљиве.

Организација фајл система

Иако постоји стандард за структуру фајл система (Filesystem Hierarchy Standard) морате очекивати одступања од њега. Он углавном важи на Linux системима и није прихваћен на Unix-у.


Путања Опис садржаја
/bin Основни програми на систему.
/boot Кернел и фајлови потребни за стартовање система
/dev Фајлови уређаја
/etc Конфигурациони фајлови и покретачки скриптови
/lib Основне библиотеке
/proc Информације о процесима и тренутном стању система
/sbin Системски програми за подизање и опоравак система
/tmp Привремени фајлови који се бришу при поновном стартовању система
/usr Секундарна хијерархија
/usr/bin Већина програма
/usr/include Фајлови заглавља за C програме
/usr/lib Библиотеке и фајлови за подршку стандардним програмима
/usr/local Локална хијерархија
/usr/sbin Мање битне алатке за администрацију система.
/usr/share Фајлови који могу бити заједнички на више система
/usr/share/man Странице ман приручника
/usr/src Изворни код програма и кернела
/var Конфигурацијски и наменски фајлови система
/var/log Разне датотеке записника
/var/tmp Привремени простор који се чува при поновном стартовању система
/home/ Овде се налазе фајлови корисника
/cdrom Тачка монтирања за cdrom уређај
/media Овде се налазе директоријуми који служе као тачке монтирања за екстерне уређаје
/mnt Привремена тачка монтирања

Монтирање партиција

Да бисмо монтирали партицију морамо да знамо њен фајл у "/dev" директоријуму. Сваки хард-диск добија свој фајл у складу са типом контролера са којим је прикачен на компјутер. IDE хард-дискови добијају "hd" ознаку, а SCSI/SATA "sd". То уме да варира, нпр. пре су резачи ишли преко SCSI емулације зато што је подршка за SCSI контролере била боља.

Код IDE контролера имамо однос "master/slave". Постоје два уређаја на једном каблу и један је главни (master) а други је секундарни (slave). На пример главни хард-диск на првом контролеру ће имати ознаку "hda" док ће секундарни имати ознаку "hdb", и тако редом главни CD-ROM на другом IDE контролеру ће имати ознаку "hdc",...

Постоје два типа партиција, примарна и проширена. Примарне партиције су нумерисане редом од 1-4, а остале партиције су проширене. Проширене партиције уствари само деле једну примарну партицију на више делова.

$ sudo mount /dev/sdb1 /mnt

Извршавамо команду преко "sudo"-а зато што захтева привилегије администратора. Фајл /dev/sdb1 означава прву примарну партицију на секундарном хард-диску на првом IDE контролеру. И на крају наводимо тачку монтирања.

Још је битно знати да кад се систем подиже он чита фајл "/etc/fstab" и монтира партиције пратећи спецификације које су наведене у том фајлу. Фајл има следећи формат:

# <Uredjaj particije> <Tacka Montiranja>   <Tip fajl sistema>  <opcije>       <dump>  <redosled skeniranja>
/dev/sdb2              /home                  reiserfs          defaults        0              2

Ознака "dump" каже "dump" програму који служи за прављење резервних копија да ли да прође кроз тај уређај, ако је тај број 0 одговор је негативан. Редослед скенирања нам каже којим редом ће скенирати партиције на хард диску. Пожељно је да различити хард дискови деле исте бројеве а различите партиције на истим хард дисковима да буду одвојено нумерисане. Ово ће омогућити да се хард дискови скенирају паралелно, док би паралелно скенирање истих партиција на истом диску само успорило подизање система.

stat

Пре него што будемо размотрили типове фајлова размотрићемо како Линукс посматра фајлове и које су специфичности Уникс фајл система.

Сваки фајл на одвојеном фајл систему има јединствену идентификацију која се зове инод (енг. "i-node"). Инод је уствари број који је јединствен и идентификује структуру која садржи атрибуте тог фајла. У тој структури се између осталог и налази:

Све ове податке можемо видети са програмом "stat".

$ stat /bin/ls
  File: `/bin/ls'
  Size: 96216     	Blocks: 200        IO Block: 4096   regular file
Device: 811h/2065d	Inode: 578285      Links: 1
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2009-03-13 22:57:31.000000000 +0100
Modify: 2008-06-27 02:31:57.000000000 +0200
Change: 2009-03-13 22:45:38.000000000 +0100

Као што видимо све структуре које се налазе у иноду се налазе у излазу команде "stat" па их није потребно додатно објашњавати.

touch

Од свих параметара који се налазе у инод табели још само не знамо како да мењамо време последњег приступа или модификације фајла. Док време последње промене инод података не можемо да мењамо. У неким примерима са користио "touch" за креирање празних фајлова занемарујући његову основну примену, а то је мењање временских ознака у инод табели фајла.

touch [opcije] fajl

Ако наведете као параметар име фајла које не постоји, "touch" програм га креира. Док подразумевано "touch" ресетује време модификације и приступа на тренутно време.

Опција Опис
-a Мења само време последњег приступа фајлу
-m Мења само време последње промене садржаја фајла
-t [[CC]YY]MMDDHHMM[.SS] "CC" и "YY" означавају годину, док "MM" означава месец, "DD" дан, "HH" сат, "MM" минут и "SS" секунде времена које желимо да доделимо као време приступа или мењања фајла
$ stat fajl
  File: `fajl'
  Size: 0         	Blocks: 0          IO Block: 4096   regular empty file
Device: 803h/2051d	Inode: 25376       Links: 1
Access: (0600/-rw-------)  Uid: ( 1087/stanislav)   Gid: ( 1090/stanislav)
Access: 2009-03-25 19:49:03.000000000 +0100
Modify: 2009-03-25 19:49:03.000000000 +0100
Change: 2009-03-25 19:49:03.000000000 +0100
$ touch -t 199204010001.30 fajl
$ stat fajl
  File: `fajl'
  Size: 0         	Blocks: 0          IO Block: 4096   regular empty file
Device: 803h/2051d	Inode: 25376       Links: 1
Access: (0600/-rw-------)  Uid: ( 1087/stanislav)   Gid: ( 1090/stanislav)
Access: 1992-04-01 00:01:30.000000000 +0200
Modify: 1992-04-01 00:01:30.000000000 +0200
Change: 2009-03-25 19:49:52.000000000 +0100

Овде смо видели пример мењања времена приступа и модификације над фајлом "fajl". Такође опет наглашавам да подразумевано "touch" мења оба времена док ако желите да мењате само један од њих имате одговарајуће опције.

Прављење чврстих веза

Размотрили смо специфичности Уникс фајл система и његово коришћење инодова. Такође смо видели да инодови имају у себи бројач који нам говори колико чврстих веза постоји ка фајлу. Овај бројач је за нове директоријуме 2. Пошто они имају фајл "." који показује ка самом директоријуму.

$ ls -ld dir
drwxr-xr-x 2 user user 4096 2009-03-22 13:26 dir
           ^ Број чврстих веза ка фајлу

Сада долази на ред прављење чврстих веза ка постојећим фајловима. Пре свега треба знати да није дозвољено прављење чврстих веза ка директоријумима пошто то може довести до веома компликованих структура директоријума (графова). Такође обе чврсте везе се морају налазити на истом фајл систему.

Чврсте везе правимо са програмом "ln" са којим такође можемо правити симболичке везе, што ћемо видети касније, са следећом синтаксом:

$ ln stara_veza nova_veza

Овде сам користио изразе као што су "stara_veza" и "nova_veza" пошто нема једном кад будемо креирали чврсту везу неће постојати разлике између ове две везе, осим наравно у томе где се налазе. Па сад да размотримо пар примера.

$ ln neki_tekstualni_fajl.txt isti_txt_fajl.txt
$ ls -il neki_tekstualni_fajl.txt isti_txt_fajl.txt
11010087 -rw-r--r-- 2 stanislav stanislav 45 2008-06-27 02:31 isti_txt_fajl.txt
11010087 -rw-r--r-- 2 stanislav stanislav 45 2008-06-27 02:31 neki_tekstualni_fajl.txt

Овде видимо да када користимо проширени приказ програма "ls" видимо да постоје две чврсте везе ка фајлу и да су им инодови исти.

Типови фајлова

На Линуксу имамо шест различитих типова фајлова и то су:

Директоријуми

Директоријуме правимо уз помоћ програма "mkdir" а бришемо празме директоријуме са програмом "rmdir". Сваки директоријум има у себи подразумевана два чврсте везе једну ка себи и једну ка родитељском директоријуму, то су фајлови "." и ".." респективно.

$  ls -ai ; ls -id ../m/ ; ls -id ../../tmp; echo -n "PWD: "; pwd 
11010082 .
 4251780 ..
11010082 ../m/
4251780 ../../tmp
PWD: /home/user/tmp/m

Видимо да у текућем директоријуму постоје два фајла "." и ".." који са својим "i-node"-вима који се поклапају са директоријумом "m" и родитељским директоријумом "tmp" што се и поклапа са хијерархијом коју видимо у излазу команде "pwd".

Именоване цеви

Са цевима (енг. "pipe") смо се упознали у Bash Основама. Тамо смо уз помоћ командног окружења Bash вршили ову комуникацију, сад ћемо видети како то можемо да радимо уз помоћ специјалног фифо фајла.

Овај фајл креирамо са програмом "mkfifo", и потом све што преусмеримо у тај фајл се појављује на крају који ослушкује. Конекција са овим фајлом се аутоматски затвара кад се појави знак за крај фајла.

prvi_terminal$ mkfifo ~/echo_fifo
prvi_terminal$ ls -l ~/echo_fifo
prw-r--r-- 1 user user 0 2009-03-22 15:18 /home/user/echo_fifo
prvi_terminal$ cat - > ~/echo_fifo

Прве две команде се односе на прављење фифо фајла "echo_fifo". Трећа команда каже "cat" програму да чита са стандардног улаза (ознака "-") и командно окружење преусмерава стандардни излаз у фифо фајл "echo_fifo".

Ово ће нам омогућити да на првом терминалу куцамо текст док не унесемо знак за крај фајла (Ctrl+D на Униксу). А на другом терминалу можемо укуцати следећу команду да пратимо измене.

drugi_terminal$ cat ~/echo_fifo

Овако можемо да направимо једноставан програмчић за комуникацију између два корисника на истом рачунару.

Фајлови уређаја

На Линуксу фајл систему можемо наћи разне ствари на фајл систему, а између осталог и фајлове уређаја. Сви уређаји имају фајлове преко којих можете да им приступате. Већ смо се упознали са фајловима који представљају партиције и читаве хард дискове. Пар година уназад једна од ствари о којима се корисник морао да брине јесте и креирање фајлова уређаја за уређаје које користи. Ово се радило уз помоћ програма "mknod". Сада креирање ових фајлова обавља само језгро аутоматски.

mknod ime_fajla tip glavni_broj_uredjaja sporedni_broj_uredjaja

Прво "mknod" програму како желимо да нам се зове фајл. Као тип наводимо "c" или "b" у зависности да ли се ради о знаковном или блоковском уређају. Разлика између ова два типа фајлова је у томе да ли кернел врши баферовање уређаја (блоковски уређај) или се сам управљачки програм брине о томе (знаковни уређај).

Главни број уређаја означава управљачки програм преко ког приступамо датом уређају, а споредни број означава физичку јединицу у односу на коју се односи дати фајл.

$ ls -l /dev/sd*
brw-rw---- 1 root disk 8,  0 2009-03-22 09:23 /dev/sda
brw-rw---- 1 root disk 8,  1 2009-03-22 09:23 /dev/sda1
brw-rw---- 1 root disk 8,  2 2009-03-22 09:23 /dev/sda2
brw-rw---- 1 root disk 8, 16 2009-03-22 09:23 /dev/sdb
brw-rw---- 1 root disk 8, 17 2009-03-22 09:23 /dev/sdb1
brw-rw---- 1 root disk 8, 18 2009-03-22 09:23 /dev/sdb2
brw-rw---- 1 root disk 8, 21 2009-03-22 09:23 /dev/sdb5
brw-rw---- 1 root disk 8, 22 2009-03-22 09:23 /dev/sdb6

Овде видимо да је 8 главни број за "sd" управљачки програм, а споредни бројеви се разликују.

Симболичке везе

Симболичке везе за разлику од чврстих омогућавају прављење веза између различитих фајл система и као одредиште могу имати директоријум. Пошто симболичка веза у себи само садржи путању на одредишни фајл/директоријум она може бити и релативна , што може бити корисно при премештању директоријума. Синтакса команде је следећа:

ln -s odrediste precica

Примери коришћења:

$ ln -s /sbin sbin
$ ls -ld sbin
lrwxrwxrwx 1 user user 5 2009-03-22 15:49 sbin -> /sbin

Видимо да када користимо проширени приказ програма "ls" имамо ознаку "l" као тип фајла, и права приступа су потпуна. То је зато што кад радимо са симболичком везом ми уствари радимо са фајлом на који он показује, тако да не би имало смисла мењати права приступа везе.

Програмирање

Рад са фајловима

Сваки процес на Unix системима има табелу отворених фајлова поред свих осталих ресурса. На најнижем нивоу ми радимо са индексима фајлова(енг. "file descriptors"), који су уствари бројеви у табели отворених фајлова у процесу. Ниво изнад индекса фајлова су токови("file streams") са којима смо до сада радили(fopen/fclose).

open()

#include <fcntl.h>

int
open(const char *path, int flags, ...);

Фунција "open()" прима два параметра, први је путања ка фајлу који желимо да отворима(/креирамо) а други је метод отварања фајла који се формира тако што се "or"-ују следеће вредности:

Име Опис
O_RDONLY Само за читање
O_WRONLY Само за уписивање
O_RDWR За читање и уписивање
O_APPEND Додаје на крај фајла при сваком уписивању
O_CREAT Креира фајл ако не постоји

Треба још знати да при креирању фајла можемо да наведемо дозволе са којим желимо да га направимо. Овде треба знати да ће дозволе да се модификују у складу са "umask" вредношћу. Пошто umask садржи у окталном облику дозволе које ће се игнорисати приликом креирања фајла, ако желимо потом да изменимо

 1 #include  <stdio.h>
 2 #include  <fcntl.h>
 3 #include  <sys/stat.h>
 4 
 5 int main(int argc, char *argv[])
 6 {
 7     int fd;
 8     fd = open("test1", O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO);
 9     if (fd == -1) {
10         printf("Error\n");
11     }
12     close(fd);
13 
14     return 0;
15 }

Додајемо нови заглавље "sys/stat.h" где се налазе дефиниције модова при отварању. У линији (8) смо урадили операцију "or" над свим модовима, али остаје да видимо да ли смо успели да наместимо "rwx" за све три групе корисника. За детаље о модовима и "umask" вредности погледајте ман страницу "umask" и "chmod"-а. У случају грешке "open()" враћа -1 и намешта глобалну променљиву "errno" на одговарајућу вредност.

$ ./open_create
$ umask
0022
$ ls -l test1
-rwxr-xr-x 1 user user 0 2008-06-01 07:58 test1

Као што видимо због umask вредности (0022) одузета су права уписа (дозволе).

read()

#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>

ssize_t
read(int d, void *buf, size_t nbytes);

Функција "read()" прима као први параметар индекс фајла који чита(енг. "file descriptor"), следећа два се односе на бафер и његову величину.

 1 #include  <errno.h>
 2 #include  <fcntl.h>
 3 #include  <sys/types.h>
 4 #include  <sys/uio.h>
 5 #include  <unistd.h>
 6 #include  <stdio.h>
 7 
 8 #define BUFFER_SIZE 200
 9 int main(int argc, char *argv[])
10 {
11     int fd;
12     char buffer[BUFFER_SIZE];
13     int bytes_read;
14 
15     if (argc != 2) {
16         fprintf(stderr, "%s ime_fajla\n", argv[0]);
17         return -1;
18     }
19 
20     fd = open(argv[1], O_RDONLY);
21     if (fd == -1) {
22         fprintf(stderr, "Greska pri otvaranju fajla %s\n",
23                 argv[0]);
24         return errno;
25     }
26 
27     while ( bytes_read = read(fd, buffer, BUFFER_SIZE) ) {

Функција "read()" враћа број прочитаних бајтова или нулу ако је нашао на крај фајла што и користим у овој петљи.

28         write(1, buffer, bytes_read);

Функција "write()" је проста и прима исте параметре као и "read()" функција, само што не наводимо максималну величину бафера већ количину бајтова које треба да испише на стандардном излазу(индекс 1).

29     }
30     close(fd);
31 
32     return 0;
33 }

Функција "read()" враћа -1 при неуспешном читању и намешта глобалну променљиву "errno" у складу са грешком. При успешном читању враћа број прочитаних бајтова или нулу ако је наишла на крај фајла.

write()

#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>

ssize_t
write(int d, const void *buf, size_t nbytes);

Функција "write()" враћа -1 при неуспешном уписивању и намешта глобалну променљиву "errno" у складу са грешком. При успешном уписивању враћа број уписаних бајтова.

Везе

Спољашње везе

Личне алатке
Именски простори
Варијанте
Акције
Навигација
Алатке