2-bo‘lim
Yadro rejimi va tizim chaqiruvlari
Foydalanuvchi va yadro rejimi farqi, tizim chaqiruvi qanday ishlaydi, strace bilan kuzatish va kutubxona funksiyasi bilan farqi.
Ushbu bo‘lim mundarijasi
Dasturingiz diskka yoza olmaydi. U faqat yadrodan so'ray oladi. Bu chegara operatsion tizimning eng muhim g'oyasi.
Ikki rejim #
Agar har bir dastur diskka to'g'ridan-to'g'ri yoza olsa:
- xato qilgan dastur butun diskni buzardi;
- zararli dastur boshqalarning ma'lumotini o'qirdi;
- ikki dastur bir vaqtda yozsa - ma'lumot buzilardi.
Shuning uchun protsessorning o'zida ikki rejim bor. Foydalanuvchi rejimida ba'zi buyruqlar umuman bajarilmaydi - protsessor ularni rad etadi.
Tizim chaqiruvi qanday ishlaydi? #
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main(void) {
/* kutubxonasiz - to'g'ridan-to'g'ri tizim chaqiruvi */
const char *xabar = "Tizim chaqiruvi orqali yozildi\n";
ssize_t yozildi = write(STDOUT_FILENO, xabar, strlen(xabar));
if (yozildi < 0) {
return 1;
}
printf("Yozilgan baytlar: %zd\n", yozildi);
return 0;
}
Tizim chaqiruvi orqali yozildi
Yozilgan baytlar: 31
printf va write farqi #
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(void) {
/* printf - kutubxona funksiyasi, buferlaydi */
printf("printf orqali\n");
/* write - tizim chaqiruvi, darhol yuboradi */
const char *matn = "write orqali\n";
write(STDOUT_FILENO, matn, strlen(matn));
printf("yana printf\n");
return 0;
}
Terminalda ishga tushirsangiz, tartib kutilgandek chiqadi:
printf orqali
write orqali
yana printf
Endi xuddi shu dasturni faylga yo'naltiring:
./dastur > natija.txt
cat natija.txt
write orqali
printf orqali
yana printf
Bir xil dastur, ikki xil natija - farq faqat chiqish qayerga ketayotganida.
| Chiqish qayerga | printf qanday ishlaydi |
|---|---|
| Terminalga | Qator bo'yicha buferlaydi - yangi qatorda darhol yuboradi |
| Faylga yoki quvurga | To'liq buferlaydi - 4 KB to'lguncha kutadi |
write esa har doim darhol yozadi, chunki u to'g'ridan-to'g'ri
tizim chaqiruvi.
Shuning uchun faylga yo'naltirilganda write birinchi bo'lib chiqadi,
printf ning matni esa dastur tugaganda buferdan bo'shatiladi.
Yechim: ikkalasini aralashtirmang. Aralashtirish kerak bo'lsa,
printf dan keyin fflush(stdout) chaqiring.
strace - tizim chaqiruvlarini kuzatish #
strace ./salom
execve("./salom", ["./salom"], 0x7ffd...) = 0
brk(NULL) = 0x55e4...
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\237\2\0\0\0\0\0"..., 832) = 832
mmap(NULL, 2036952, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f2a...
close(3) = 0
write(1, "Salom, dunyo!\n", 14) = 14
exit_group(0) = ?
printf("Salom, dunyo!\n") oxir-oqibat bitta write chaqiruviga
aylandi.
strace - eng foydali tashxis vositasistrace -c ./dastur # statistika: qaysi chaqiruv necha marta
strace -e trace=openat ./dastur # faqat fayl ochishlarni ko'rsatish
strace -f ./dastur # bola jarayonlarni ham kuzatish
strace -p 1234 # ishlayotgan jarayonga ulanish
Dastur nima uchun sekin ishlayotganini yoki qaysi faylni topa olmayotganini bilish uchun eng tez usul.
macOS'da dtruss, Windows'da Process Monitor shunga o'xshash.
Necha xil tizim chaqiruvi bor? #
ls /usr/include/asm-generic/unistd.h && grep -c "^#define __NR_" /usr/include/asm-generic/unistd.h
Linux'da taxminan 400 dan ortiq tizim chaqiruvi bor. Lekin kundalik ishda ularning 20-30 tasi ishlatiladi.
| Guruh | Chaqiruvlar |
|---|---|
| Fayllar | open, read, write, close, lseek, stat |
| Jarayonlar | fork, execve, wait4, exit_group, getpid |
| Xotira | mmap, munmap, brk, mprotect |
| Aloqa | pipe, socket, bind, connect, send, recv |
| Signallar | kill, sigaction, sigprocmask |
| Vaqt | clock_gettime, nanosleep |
| Huquqlar | chmod, chown, setuid, getuid |
Kutubxona funksiyasi va tizim chaqiruvi #
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int main(void) {
/* Bular tizim chaqiruvi EMAS - hammasi foydalanuvchi rejimida */
int uzunlik = (int) strlen("salom");
double ildiz = sqrt(16.0);
int *xotira = malloc(100);
printf("strlen : %d\n", uzunlik);
printf("sqrt : %.1f\n", ildiz);
printf("malloc : %s\n", xotira != NULL ? "muvaffaqiyatli" : "xato");
free(xotira);
return 0;
}
strlen : 5
sqrt : 4.0
malloc : muvaffaqiyatli
malloc tizim chaqiruvimi?Bu ayyor savol. malloc kutubxona funksiyasi, lekin u ba'zan yadrodan
xotira so'raydi.
Qanday ishlaydi:
malloco'zida xotira zaxirasi saqlaydi;- Kichik so'rov bo'lsa - zaxiradan beradi, tizim chaqiruvi yo'q;
- Zaxira tugasa -
mmapyokibrkbilan yadrodan katta bo'lak so'raydi.
Shuning uchun ming marta malloc(10) qilsangiz, tizim chaqiruvi ming
marta emas, bir necha marta bo'ladi. Bu ancha tezroq.
Chaqiruv narxi #
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#define MARTA 200000
int main(void) {
struct timespec boshlandi, tugadi;
char belgi = 'x';
/* Bo'sh fayl deskriptoriga yozamiz - /dev/null ga */
FILE *nol = fopen("/dev/null", "w");
if (nol == NULL) {
return 1;
}
int fd = fileno(nol);
/* 1. Har safar tizim chaqiruvi */
clock_gettime(CLOCK_MONOTONIC, &boshlandi);
for (int i = 0; i < MARTA; i++) {
write(fd, &belgi, 1);
}
clock_gettime(CLOCK_MONOTONIC, &tugadi);
double tizim = (double) (tugadi.tv_sec - boshlandi.tv_sec) +
(double) (tugadi.tv_nsec - boshlandi.tv_nsec) / 1e9;
/* 2. Buferlangan yozuv */
clock_gettime(CLOCK_MONOTONIC, &boshlandi);
for (int i = 0; i < MARTA; i++) {
fputc(belgi, nol);
}
fflush(nol);
clock_gettime(CLOCK_MONOTONIC, &tugadi);
double buferli = (double) (tugadi.tv_sec - boshlandi.tv_sec) +
(double) (tugadi.tv_nsec - boshlandi.tv_nsec) / 1e9;
fclose(nol);
printf("%d marta yozish tekshirildi\n", MARTA);
printf("Buferlangan yozuv tezroqmi? %s\n", buferli < tizim ? "ha" : "yo'q");
/* Aniq vaqtlarni ko'rish uchun quyidagi ikki qatorni yoqing:
printf(" write (tizim chaqiruvi) : %.6f soniya\n", tizim);
printf(" fputc (buferlangan) : %.6f soniya\n", buferli);
*/
return 0;
}
200000 marta yozish tekshirildi
Buferlangan yozuv tezroqmi? ha
Izohdagi qatorlarni yoqsangiz, aniq vaqtlarni ko'rasiz. Bir sinovda natija shunday chiqdi:
write (tizim chaqiruvi) : 0.119500 soniya
fputc (buferlangan) : 0.000500 soniya
Bu sinovda buferlangan yozuv 200 barobardan ko'proq tezroq bo'ldi.
Sabab oddiy: write 200 000 marta yadroga murojaat qildi - har safar
rejim almashinuvi bilan. fputc esa ma'lumotni 4 KB buferga to'plab,
atigi bir necha yuz marta chaqirdi.
Sizning kompyuteringizda raqamlar boshqacha bo'ladi, lekin nisbat shunga o'xshash qoladi. Aynan shu sababdan har qanday tilda kiritish-chiqarish buferlanadi.
Xato kodlari #
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main(void) {
int fd = open("/mavjud/emas/fayl.txt", O_RDONLY);
if (fd < 0) {
printf("open muvaffaqiyatsiz\n");
printf("errno : %d\n", errno);
printf("Sababi : %s\n", strerror(errno));
return 0;
}
close(fd);
return 0;
}
open muvaffaqiyatsiz
errno : 2
Sababi : No such file or directory
errno ni darhol tekshiringint fd = open("fayl.txt", O_RDONLY);
printf("Ochilmadi\n"); /* bu printf errno ni o'zgartirishi mumkin! */
printf("%s\n", strerror(errno)); /* endi noto'g'ri sabab chiqadi */
errno faqat xato bo'lgan chaqiruvdan keyin darhol ishonchli.
Har qanday funksiya uni o'zgartirishi mumkin.
To'g'ri yo'l - qiymatni darhol saqlash:
int fd = open("fayl.txt", O_RDONLY);
int saqlangan_xato = errno;
errno | Nomi | Ma'nosi |
|---|---|---|
| 1 | EPERM | Ruxsat yo'q |
| 2 | ENOENT | Fayl topilmadi |
| 9 | EBADF | Noto'g'ri fayl deskriptori |
| 12 | ENOMEM | Xotira yetmadi |
| 13 | EACCES | Kirish taqiqlangan |
| 17 | EEXIST | Allaqachon mavjud |
| 28 | ENOSPC | Diskda joy yo'q |
/proc - yadro bilan gaplashish #
Linux'da yadro holati oddiy fayllar kabi ko'rinadi:
cat /proc/cpuinfo | grep "model name" | head -1
cat /proc/meminfo | head -3
cat /proc/uptime
model name : Intel(R) Core(TM) i7-10700 CPU @ 2.90GHz
MemTotal: 16283544 kB
MemFree: 7123456 kB
MemAvailable: 11234567 kB
12345.67 8901.23
/proc - haqiqiy fayl emas/proc ichidagi fayllar diskda yotmaydi. Siz ularni o'qiganingizda
yadro javobni o'sha zahoti yaratadi.
Shuning uchun ls -l /proc/uptime hajmni 0 deb ko'rsatadi, lekin
cat qilsangiz ma'lumot chiqadi.
Bu UNIX falsafasining namunasi: hamma narsa fayl. Yadro holatini
o'qish uchun maxsus API kerak emas - oddiy open va read yetadi.
#include <stdio.h>
int main(void) {
FILE *f = fopen("/proc/uptime", "r");
if (f == NULL) {
printf("Bu misol faqat Linux'da ishlaydi\n");
return 0;
}
double soniya, bosh_vaqt;
if (fscanf(f, "%lf %lf", &soniya, &bosh_vaqt) == 2) {
int kun = (int) (soniya / 86400);
int soat = (int) ((soniya - kun * 86400) / 3600);
int daqiqa = (int) ((soniya - kun * 86400 - soat * 3600) / 60);
printf("Tizim ishlash vaqti aniqlandi\n");
printf("Kun / soat / daqiqa qiymatlari o'qildi: %s\n",
(kun >= 0 && soat >= 0 && daqiqa >= 0) ? "ha" : "yo'q");
}
fclose(f);
return 0;
}
Tizim ishlash vaqti aniqlandi
Kun / soat / daqiqa qiymatlari o'qildi: ha
writebilan ekranga matn chiqaruvchi dastur yozing.- Uni
stracebilan ishga tushirib, chaqiruvlarni ko'ring. strace -cbilan qaysi chaqiruv eng ko'p ishlatilganini aniqlang.printfvawriteni aralashtirib, natijani faylga yo'naltiring.- Mavjud bo'lmagan faylni ochib,
errnoni chiqaring. - Turli xatolar uchun
errnoqiymatlarini taqqoslang. /proc/cpuinfodan protsessor nomini o'qing./proc/meminfodan xotira hajmini C dasturida o'qing.strace -e trace=openatbilan dastur qaysi fayllarni ochishini ko'ring.- Buferlangan va buferlanmagan yozuv tezligini
%.6fbilan o'lchang.
Xulosa #
- Protsessorda ikki rejim bor: foydalanuvchi va yadro.
- Dastur temirga to'g'ridan-to'g'ri murojaat qila olmaydi - u yadrodan so'raydi.
- Tizim chaqiruvi - foydalanuvchi rejimidan yadro rejimiga o'tishning yagona qonuniy yo'li.
- Rejim almashinuvi qimmat, shuning uchun kutubxonalar buferlaydi.
printf- kutubxona funksiyasi,write- tizim chaqiruvi.- Ularni aralashtirsangiz, chiqish tartibi buzilishi mumkin.
stracedastur qanday tizim chaqiruvlari qilayotganini ko'rsatadi.- Xato sababi
errnoda; uni darhol o'qing. - Linux'da
/procorqali yadro holati oddiy fayl kabi o'qiladi.
Keyingi bo'limda jarayon nima ekanini va u qanday holatlarda bo'lishini ko'ramiz.
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.