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.

🕑 15 daqiqa o‘qish 📄 913 so‘z 👁 1 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Ikki rejim
  2. Tizim chaqiruvi qanday ishlaydi?
  3. printf va write farqi
  4. strace - tizim chaqiruvlarini kuzatish
  5. Necha xil tizim chaqiruvi bor?
  6. Kutubxona funksiyasi va tizim chaqiruvi
  7. Chaqiruv narxi
  8. Xato kodlari
  9. /proc - yadro bilan gaplashish
  10. Xulosa

Dasturingiz diskka yoza olmaydi. U faqat yadrodan so'ray oladi. Bu chegara operatsion tizimning eng muhim g'oyasi.

Ikki rejim #

Protsessorning ikki ish rejimi Foydalanuvchi rejimi (user mode) Cheklangan huquq: faqat o'z xotirasi, temirga to'g'ridan-to'g'ri kirish yo'q Sizning dasturingiz shu yerda ishlaydi Tizim chaqiruvi Yadro rejimi (kernel mode) To'liq huquq: butun xotira, barcha qurilmalar, barcha buyruqlar Bu yerdagi xato butun tizimni qulatadi (kernel panic)
Rejimni protsessorning o'zi ajratadi - dastur uni chetlab o'ta olmaydi
Nima uchun ikki rejim kerak?

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? #

C
#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;
}
Natija
Tizim chaqiruvi orqali yozildi
Yozilgan baytlar: 31
write() chaqirilganda nima sodir bo'ladi 1. Dastur write(1, buf, 31) 2. libc raqamni registrga qo'yadi 3. syscall buyrug'i rejim yadroga o'tadi 4. Yadro tekshiradi huquq, argumentlar 5. Ishni bajaradi terminalga yozadi 6. Dasturga qaytadi natija: 31 Rejim almashinuvi qimmat Shuning uchun printf ma'lumotni buferga to'plab, kamroq chaqiradi
Har bir tizim chaqiruvi rejim almashinuvini talab qiladi

printf va write farqi #

C
#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:

Natija
printf orqali
write orqali
yana printf

Endi xuddi shu dasturni faylga yo'naltiring:

Terminal
./dastur > natija.txt
cat natija.txt
Natija
write orqali
printf orqali
yana printf
Bufer tartibni buzadi

Bir xil dastur, ikki xil natija - farq faqat chiqish qayerga ketayotganida.

Chiqish qayergaprintf qanday ishlaydi
TerminalgaQator bo'yicha buferlaydi - yangi qatorda darhol yuboradi
Faylga yoki quvurgaTo'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 #

Terminal
strace ./salom
Natija
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 vositasi
Terminal
strace -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? #

Terminal
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.

GuruhChaqiruvlar
Fayllaropen, read, write, close, lseek, stat
Jarayonlarfork, execve, wait4, exit_group, getpid
Xotirammap, munmap, brk, mprotect
Aloqapipe, socket, bind, connect, send, recv
Signallarkill, sigaction, sigprocmask
Vaqtclock_gettime, nanosleep
Huquqlarchmod, chown, setuid, getuid

Kutubxona funksiyasi va tizim chaqiruvi #

C
#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;
}
Natija
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:

  1. malloc o'zida xotira zaxirasi saqlaydi;
  2. Kichik so'rov bo'lsa - zaxiradan beradi, tizim chaqiruvi yo'q;
  3. Zaxira tugasa - mmap yoki brk bilan 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 #

C
#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;
}
Natija
200000 marta yozish tekshirildi
Buferlangan yozuv tezroqmi? ha

Izohdagi qatorlarni yoqsangiz, aniq vaqtlarni ko'rasiz. Bir sinovda natija shunday chiqdi:

Natija
  write (tizim chaqiruvi) : 0.119500 soniya
  fputc (buferlangan)     : 0.000500 soniya
Farq nima uchun bunchalik katta?

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 #

C
#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;
}
Natija
open muvaffaqiyatsiz
errno   : 2
Sababi  : No such file or directory
errno ni darhol tekshiring
C
int 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:

C
int fd = open("fayl.txt", O_RDONLY);
int saqlangan_xato = errno;
errnoNomiMa'nosi
1EPERMRuxsat yo'q
2ENOENTFayl topilmadi
9EBADFNoto'g'ri fayl deskriptori
12ENOMEMXotira yetmadi
13EACCESKirish taqiqlangan
17EEXISTAllaqachon mavjud
28ENOSPCDiskda joy yo'q

/proc - yadro bilan gaplashish #

Linux'da yadro holati oddiy fayllar kabi ko'rinadi:

Terminal
cat /proc/cpuinfo | grep "model name" | head -1
cat /proc/meminfo | head -3
cat /proc/uptime
Natija
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.

C
#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;
}
Natija
Tizim ishlash vaqti aniqlandi
Kun / soat / daqiqa qiymatlari o'qildi: ha
Amaliy topshiriq
  1. write bilan ekranga matn chiqaruvchi dastur yozing.
  2. Uni strace bilan ishga tushirib, chaqiruvlarni ko'ring.
  3. strace -c bilan qaysi chaqiruv eng ko'p ishlatilganini aniqlang.
  4. printf va write ni aralashtirib, natijani faylga yo'naltiring.
  5. Mavjud bo'lmagan faylni ochib, errno ni chiqaring.
  6. Turli xatolar uchun errno qiymatlarini taqqoslang.
  7. /proc/cpuinfo dan protsessor nomini o'qing.
  8. /proc/meminfo dan xotira hajmini C dasturida o'qing.
  9. strace -e trace=openat bilan dastur qaysi fayllarni ochishini ko'ring.
  10. Buferlangan va buferlanmagan yozuv tezligini %.6f bilan 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.
  • strace dastur qanday tizim chaqiruvlari qilayotganini ko'rsatadi.
  • Xato sababi errno da; uni darhol o'qing.
  • Linux'da /proc orqali yadro holati oddiy fayl kabi o'qiladi.

Keyingi bo'limda jarayon nima ekanini va u qanday holatlarda bo'lishini ko'ramiz.

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.