12-bo‘lim
Fayl tizimi
Fayl deskriptori, ochish-o'qish-yozish, kursor, kataloglar, yo'llar, huquqlar, qattiq va ramziy havolalar.
Ushbu bo‘lim mundarijasi
Unix falsafasida hamma narsa fayl: disk, klaviatura, tarmoq ulanishi, hatto jarayonlar haqidagi ma'lumot ham.
Fayl deskriptori #
Fayl ochish va o'qish #
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
int main(void) {
const char *nom = "sinov_fayl.txt";
const char *matn = "Birinchi qator\nIkkinchi qator\n";
/* Yozish uchun ochamiz */
int fd = open(nom, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) {
perror("open");
return 1;
}
printf("Yozish uchun ochilgan fd: %d\n", fd);
ssize_t yozildi = write(fd, matn, strlen(matn));
printf("Yozilgan bayt: %zd\n", yozildi);
close(fd);
/* O'qish uchun qayta ochamiz */
fd = open(nom, O_RDONLY);
if (fd < 0) {
return 1;
}
char bufer[64];
ssize_t oqildi = read(fd, bufer, sizeof bufer - 1);
bufer[oqildi] = '\0';
printf("O'qilgan bayt : %zd\n", oqildi);
printf("Mazmuni to'g'rimi? %s\n", strcmp(bufer, matn) == 0 ? "ha" : "yo'q");
close(fd);
unlink(nom);
return 0;
}
Yozish uchun ochilgan fd: 3
Yozilgan bayt: 30
O'qilgan bayt : 30
Mazmuni to'g'rimi? ha
Har bir jarayon uchta fayl deskriptori bilan tug'iladi:
| fd | Nomi | Odatiy manzili |
|---|---|---|
0 | stdin | Klaviatura |
1 | stdout | Ekran |
2 | stderr | Ekran |
open eng kichik bo'sh deskriptorni qaytaradi. 0, 1, 2 band
bo'lgani uchun birinchi yangi fayl 3 ni oladi.
Bu xossa quvur (pipe) qurishda ishlatiladi: close(1) qilib darhol
open chaqirsangiz, yangi fayl stdout o'rniga o'tiradi. Amalda
buning uchun dup2 ishlatiladi - 15-bo'limda ko'ramiz.
Ochish bayroqlari #
| Bayroq | Ma'nosi |
|---|---|
O_RDONLY | Faqat o'qish |
O_WRONLY | Faqat yozish |
O_RDWR | O'qish va yozish |
O_CREAT | Bo'lmasa yaratish (rejim kerak) |
O_TRUNC | Bor bo'lsa bo'shatish |
O_APPEND | Har yozishda oxiriga o'tish |
O_EXCL | O_CREAT bilan: bor bo'lsa xato |
O_NONBLOCK | Bloklamaydigan rejim |
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main(void) {
const char *nom = "yagona.txt";
/* Birinchi marta - muvaffaqiyatli */
int birinchi = open(nom, O_WRONLY | O_CREAT | O_EXCL, 0644);
printf("Birinchi urinish: %s\n", birinchi >= 0 ? "yaratildi" : "xato");
/* Ikkinchi marta - O_EXCL xato beradi */
int ikkinchi = open(nom, O_WRONLY | O_CREAT | O_EXCL, 0644);
printf("Ikkinchi urinish: %s\n", ikkinchi >= 0 ? "yaratildi" : "xato");
if (ikkinchi < 0) {
printf("Xato sababi : %s\n", strerror(errno));
printf("EEXIST mi? : %s\n", errno == EEXIST ? "ha" : "yo'q");
}
if (birinchi >= 0) {
close(birinchi);
}
unlink(nom);
return 0;
}
Birinchi urinish: yaratildi
Ikkinchi urinish: xato
Xato sababi : File exists
EEXIST mi? : ha
O_CREAT | O_EXCL - poyga holatidan himoyaKo'p dasturchi shunday yozadi:
if (access(nom, F_OK) != 0) { /* fayl yo'qmi? */
fd = open(nom, O_WRONLY | O_CREAT, 0644);
}
Bu TOCTOU (Time-of-check to time-of-use) zaifligi. Tekshiruv va ochish orasida boshqa jarayon o'sha nomda fayl (yoki ramziy havola) yaratishi mumkin.
To'g'ri yechim - bitta atomar amal:
fd = open(nom, O_WRONLY | O_CREAT | O_EXCL, 0644);
Yadro tekshiruv va yaratishni bo'linmas tarzda bajaradi. Vaqtinchalik
fayl yaratishda esa mkstemp ishlating.
Kursor va lseek #
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
int main(void) {
const char *nom = "kursor.txt";
int fd = open(nom, O_RDWR | O_CREAT | O_TRUNC, 0644);
if (fd < 0) {
return 1;
}
write(fd, "0123456789", 10);
printf("Yozgandan keyingi kursor: %ld\n", (long) lseek(fd, 0, SEEK_CUR));
lseek(fd, 0, SEEK_SET);
printf("Boshiga qaytgan kursor : %ld\n", (long) lseek(fd, 0, SEEK_CUR));
char bufer[4];
read(fd, bufer, 3);
bufer[3] = '\0';
printf("Uch bayt o'qildi : %s\n", bufer);
printf("O'qigandan keyingi kursor: %ld\n", (long) lseek(fd, 0, SEEK_CUR));
/* Oxiridan orqaga */
long oxir = (long) lseek(fd, -2, SEEK_END);
read(fd, bufer, 2);
bufer[2] = '\0';
printf("Oxirdan 2 bayt oldin : %ld\n", oxir);
printf("O'sha yerdagi mazmun : %s\n", bufer);
close(fd);
unlink(nom);
return 0;
}
Yozgandan keyingi kursor: 10
Boshiga qaytgan kursor : 0
Uch bayt o'qildi : 012
O'qigandan keyingi kursor: 3
Oxirdan 2 bayt oldin : 8
O'sha yerdagi mazmun : 89
lseek bilan faylning oxiridan nariga o'tib yozish mumkin:
lseek(fd, 1024 * 1024 * 1024, SEEK_SET); /* 1 GB naridan */
write(fd, "X", 1);
Fayl hajmi 1 GB ko'rinadi, lekin diskda bir necha blok joy oladi. Oradagi "teshik" nol bayt sifatida o'qiladi, lekin haqiqatan saqlanmaydi.
ls -l teshikli.dat # ko'rsatilgan hajm
du -h teshikli.dat # haqiqiy disk joyi
Virtual mashina disk obrazlari, ma'lumotlar bazasi fayllari shu usuldan foydalanadi.
Fayl haqidagi ma'lumot #
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
int main(void) {
const char *nom = "malumot.txt";
int fd = open(nom, O_WRONLY | O_CREAT | O_TRUNC, 0640);
if (fd < 0) {
return 1;
}
write(fd, "12345", 5);
close(fd);
struct stat h;
if (stat(nom, &h) < 0) {
return 1;
}
printf("Hajmi : %ld bayt\n", (long) h.st_size);
printf("Oddiy faylmi? : %s\n", S_ISREG(h.st_mode) ? "ha" : "yo'q");
printf("Katalogmi? : %s\n", S_ISDIR(h.st_mode) ? "ha" : "yo'q");
printf("Huquqlar : %o\n", h.st_mode & 07777);
printf("Havolalar soni : %ld\n", (long) h.st_nlink);
printf("Bloklar soni : %ld\n", (long) h.st_blocks);
unlink(nom);
return 0;
}
Hajmi : 5 bayt
Oddiy faylmi? : ha
Katalogmi? : yo'q
Huquqlar : 640
Havolalar soni : 1
Bloklar soni : 8
st_blocks 512 baytli birliklarda sanaladi (tarixiy sabab).
8 × 512 = 4096 bayt = bitta 4 KB blok.
Ya'ni fayl tizimi eng kichik fayl uchun ham butun blok ajratadi. 1 baytlik million fayl 4 GB disk egallaydi.
Shuning uchun ko'p mayda fayl saqlaydigan tizimlarda (kesh, log) ularni bitta katta faylga birlashtirish samaraliroq.
Huquqlar #
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
int main(void) {
const char *nom = "huquq.txt";
/* umask ta'sirini yo'qotamiz */
umask(0);
int fd = open(nom, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (fd < 0) {
return 1;
}
close(fd);
struct stat h;
stat(nom, &h);
printf("Yaratilgandagi huquq: %o\n", h.st_mode & 0777);
chmod(nom, 0600);
stat(nom, &h);
printf("chmod dan keyin : %o\n", h.st_mode & 0777);
printf("O'qish mumkinmi? : %s\n", access(nom, R_OK) == 0 ? "ha" : "yo'q");
printf("Bajarish mumkinmi? : %s\n", access(nom, X_OK) == 0 ? "ha" : "yo'q");
unlink(nom);
return 0;
}
Yaratilgandagi huquq: 666
chmod dan keyin : 600
O'qish mumkinmi? : ha
Bajarish mumkinmi? : yo'q
umask - yashirin filtropen(nom, ..., 0666) yozsangiz ham fayl odatda 0644 bo'lib
yaratiladi. Sababi - umask.
umask - jarayonning "olib tashlanadigan huquqlar" niqobi:
So'ralgan huquq : 0666 (rw-rw-rw-)
umask : 0022 (----w--w-)
Natija : 0644 (rw-r--r--)
umask # joriy qiymatni ko'rish
umask 077 # faqat egaga ruxsat
Shuning uchun yuqoridagi misolda umask(0) chaqirdik - aks holda
natija 0666 emas, 0644 bo'lardi.
Maxfiy ma'lumot yozadigan dasturlarda ochish rejimini aniq
0600 qiling va umask ga tayanmang.
Kataloglarni o'qish #
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
int main(void) {
/* Sinov katalogi tuzamiz */
mkdir("sinov_katalog", 0755);
FILE *f;
const char *nomlar[3] = { "bir.txt", "ikki.txt", "uch.txt" };
char yol[64];
for (int i = 0; i < 3; i++) {
snprintf(yol, sizeof yol, "sinov_katalog/%s", nomlar[i]);
f = fopen(yol, "w");
if (f != NULL) {
fputs("x", f);
fclose(f);
}
}
DIR *katalog = opendir("sinov_katalog");
if (katalog == NULL) {
return 1;
}
int oddiy = 0;
int nuqtali = 0;
struct dirent *yozuv;
while ((yozuv = readdir(katalog)) != NULL) {
if (yozuv->d_name[0] == '.') {
nuqtali++;
} else {
oddiy++;
}
}
closedir(katalog);
printf("Oddiy fayllar : %d\n", oddiy);
printf("Nuqtali yozuvlar: %d\n", nuqtali);
printf("Nuqtalilar . va .. mi? %s\n", nuqtali == 2 ? "ha" : "yo'q");
for (int i = 0; i < 3; i++) {
snprintf(yol, sizeof yol, "sinov_katalog/%s", nomlar[i]);
unlink(yol);
}
rmdir("sinov_katalog");
return 0;
}
Oddiy fayllar : 3
Nuqtali yozuvlar: 2
Nuqtalilar . va .. mi? ha
Katalog - bu maxsus fayl, ichida (nom, inode raqami) juftliklari
saqlanadi.
. -> inode 128
.. -> inode 96
bir.txt -> inode 4521
ikki.txt -> inode 4522
Ya'ni fayl nomi faylda emas, katalogda saqlanadi. Faylning o'zida (inode da) nom yo'q - faqat hajm, huquqlar va ma'lumot bloklarining manzillari.
Bu muhim xulosaga olib keladi: bitta fayl bir necha nomga ega bo'lishi mumkin - qattiq havolalar.
readdir tartibini kafolatlab bo'lmaydi. Alifbo tartibi kerak
bo'lsa, scandir ishlating yoki o'zingiz saralang.
Qattiq va ramziy havolalar #
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
int main(void) {
const char *asl = "asl.txt";
FILE *f = fopen(asl, "w");
if (f == NULL) {
return 1;
}
fputs("mazmun", f);
fclose(f);
link(asl, "qattiq.txt"); /* qattiq havola */
symlink(asl, "ramziy.txt"); /* ramziy havola */
struct stat a, q, r, rl;
stat(asl, &a);
stat("qattiq.txt", &q);
stat("ramziy.txt", &r);
lstat("ramziy.txt", &rl);
printf("Asl inode raqami : %s\n",
a.st_ino == q.st_ino ? "qattiq havola bilan bir xil" : "farqli");
printf("Havolalar soni : %ld\n", (long) a.st_nlink);
printf("Ramziy havola (stat) : %s\n",
r.st_ino == a.st_ino ? "aslga olib boradi" : "boshqa");
printf("Ramziy havola (lstat) : %s\n",
rl.st_ino == a.st_ino ? "bir xil" : "o'zi alohida obyekt");
printf("Ramziy havolami? : %s\n",
S_ISLNK(rl.st_mode) ? "ha" : "yo'q");
/* Aslni o'chiramiz - qattiq havola orqali ma'lumot qoladi */
unlink(asl);
f = fopen("qattiq.txt", "r");
char bufer[16] = {0};
if (f != NULL) {
fgets(bufer, sizeof bufer, f);
fclose(f);
}
printf("Asl o'chirilgach qattiq: %s\n", bufer);
f = fopen("ramziy.txt", "r");
printf("Asl o'chirilgach ramziy: %s\n", f == NULL ? "buzilgan" : "ishlaydi");
if (f != NULL) {
fclose(f);
}
unlink("qattiq.txt");
unlink("ramziy.txt");
return 0;
}
Asl inode raqami : qattiq havola bilan bir xil
Havolalar soni : 2
Ramziy havola (stat) : aslga olib boradi
Ramziy havola (lstat) : o'zi alohida obyekt
Ramziy havolami? : ha
Asl o'chirilgach qattiq: mazmun
Asl o'chirilgach ramziy: buzilgan
| Xususiyat | Qattiq havola | Ramziy havola |
|---|---|---|
| Nima saqlaydi | Inode raqamini | Matn ko'rinishidagi yo'lni |
| Fayl tizimlari orasida | Mumkin emas | Mumkin |
| Katalogga | Mumkin emas | Mumkin |
| Asl o'chirilsa | Ma'lumot qoladi | Buziladi |
ls -l da | Oddiy fayl | -> bilan ko'rinadi |
unlink nomi bejiz emasFayl o'chirish tizim chaqiruvi delete emas, unlink deb
ataladi.
Sabab: u faylni emas, katalogdagi yozuvni o'chiradi va inode dagi havolalar sonini bittaga kamaytiradi.
Fayl ma'lumoti faqat ikki shart bajarilganda yo'qoladi:
- Havolalar soni nolga tushganda;
- Uni ochib turgan jarayon qolmaganda.
Shuning uchun ishlab turgan dastur log faylini o'chirsangiz, disk joyi bo'shamaydi - dastur qayta ishga tushirilgunicha:
lsof +L1 # o'chirilgan, lekin ochiq fayllarni ko'rish
Atomar almashtirish #
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(void) {
const char *asl = "sozlama.conf";
const char *vaqtinchalik = "sozlama.conf.yangi";
FILE *f = fopen(asl, "w");
if (f == NULL) {
return 1;
}
fputs("eski=1\n", f);
fclose(f);
/* Yangi mazmunni alohida faylga yozamiz */
f = fopen(vaqtinchalik, "w");
if (f == NULL) {
return 1;
}
fputs("yangi=2\n", f);
fflush(f);
fsync(fileno(f)); /* diskka kafolatli yozish */
fclose(f);
/* Atomar almashtirish */
rename(vaqtinchalik, asl);
f = fopen(asl, "r");
char bufer[32] = {0};
if (f != NULL) {
fgets(bufer, sizeof bufer, f);
fclose(f);
}
printf("Faylning yangi mazmuni: %s", bufer);
printf("Vaqtinchalik fayl qoldimi? %s\n",
access(vaqtinchalik, F_OK) == 0 ? "ha" : "yo'q");
unlink(asl);
return 0;
}
Faylning yangi mazmuni: yangi=2
Vaqtinchalik fayl qoldimi? yo'q
Bu xavfli:
f = fopen("sozlama.conf", "w"); /* fayl darhol bo'shaydi! */
yozish(f); /* shu paytda tok o'chsa... */
Natija - bo'sh yoki yarim yozilgan sozlama fayli. Xizmat qayta ishga tushmaydi.
To'g'ri usul (yuqoridagi misol):
- Yangi mazmunni vaqtinchalik faylga yozing;
fsyncbilan diskka kafolatli tushiring;renamebilan atomar almashtiring.
rename POSIX bo'yicha atomar: har qanday paytda fayl yo eski, yo
yangi mazmunda bo'ladi - o'rtasi yo'q.
Systemd, Git, ma'lumotlar bazalari - hammasi shu usulni ishlatadi.
openbilan fayl yaratib, qaytgan fd raqamini tushuntiring.O_CREAT | O_EXCLbilan ikki marta ochib,EEXISTni oling.lseekbilan kursorni harakatlantirib, o'qing.- Teshikli fayl yaratib,
ls -lvadu -hni solishtiring. statbilan hajm va bloklar sonini chiqaring.umaskni o'zgartirib, yaratilgan fayl huquqini kuzating.opendir/readdirbilan katalogni sanang.- Qattiq va ramziy havola yaratib, farqini isbotlang.
- Aslni o'chirib, ikkala havolaning holatini tekshiring.
- Sozlama faylini
renameorqali atomar yangilang.
Xulosa #
- Fayl deskriptori - jarayon jadvalidagi oddiy butun son.
0,1,2band bo'lgani uchun birinchi yangi fd 3 bo'ladi.O_CREAT | O_EXCLTOCTOU zaifligidan himoya qiladi.- Kursor har bir ochiq faylda alohida saqlanadi.
lseekbilan teshikli fayl yaratish mumkin.- Fayl tizimi eng kichik fayl uchun ham butun blok ajratadi.
umaskso'ralgan huquqlardan bitlarni olib tashlaydi.- Katalog -
(nom, inode)juftliklari saqlanadigan maxsus fayl. - Qattiq havola - o'sha inode ga yangi nom.
- Ramziy havola - matn ko'rinishidagi yo'l; asl o'chsa buziladi.
unlinkfaylni emas, katalog yozuvini o'chiradi.- Sozlama faylini
renameorqali atomar yangilang.
Keyingi bo'limda fayl tizimining ichki tuzilishini ochamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.