Tarkibga o'tish

Dasturlar va jarayonlar: bajariladigan fayldan tirik vazifagacha

Diskdagi dastur harakatsiz. /usr/sbin/nginx fayl tizimida turgan bir necha megabayt baytdan iborat: u CPU vaqtini sarflamaydi va blok keshidan tashqari hech qanday xotira egallamaydi. Kernel uni yuklab, unga manzil maydoni bermaguncha va uning ko'rsatmalarini bajara boshlamaguncha u bilan hech narsa sodir bo'lmaydi. Shu paytda u jarayonga (process) aylanadi — o'z identifikatoriga ega, kuzatib boriladigan va resurslarni egallagan tirik obyektga. Bu ikkisini adashtirish production’dagi kutilmagan holatlarning keng tarqalgan manbai: apt upgrade bilan diskdagi binar faylni almashtirish allaqachon ishlab turgan jarayon bajarayotgan kodni o'zgartirmaydi. Aynan shu sababli kutubxona yoki binar fayl yangilangandan keyin xizmatni aniq qayta ishga tushirish kerak.

Fayl ruxsatlari xavfsizligi oldingi modulni ishlab turgan dastur qaysi identifikator nomidan ish yuritadi degan savol bilan yakunladi. Bu maqola undan oldin keladigan savolga javob beradi: fayl eng avvalo qanday qilib jarayonga aylanadi, kernel u haqida aslida nimani kuzatib boradi va bu holatni ps bilan qanday o'qish mumkin? Uzluksiz kuzatish, xotirani hisoblash, rejalashtirish ustuvorligi, signallar va job’larni boshqarish vositalari bu modulda keyinroq har biri alohida maqolada ko'rib chiqiladi; bu yerda esa ularning hammasi tayanadigan poydevorni quramiz.

Dastur va jarayon

Dastur — mashina kodini o'z ichiga olgan fayl. Linux’da u odatda ELF (Executable and Linkable Format) formatida bo'ladi, masalan /usr/bin/less yoki /usr/sbin/nginx. Uni nusxalash, ko'chirish yoki o'chirish tizimning ishlab turgan holatiga hech qanday ta’sir qilmaydi, chunki fayl o'z-o'zidan bajarilmaydi.

Jarayon — dasturning ishlab turgan nusxasi. Kernel har bir jarayon uchun ma’lumotlar tuzilmasini (kernelning ichki task_struct tuzilmasi) yuritadi va unda quyidagilar qayd etiladi:

  • yagona PID (process ID) va uni yaratgan jarayonning PPID (parent process ID) si;
  • u qaysi hisob ma’lumotlari (credentials) bilan ishlashi — real, effective va saqlangan UID/GID, shuningdek qo'shimcha guruhlar;
  • uning virtual xotira tuzilishi — kod, ma’lumotlar, heap, stek va xotiraga ulangan kutubxonalar;
  • uning ochiq fayl deskriptorlari — oddiy fayllar, socket’lar, pipe’lar, qurilmalar;
  • uning rejalashtirish holati — ishlayapti, uxlayapti, to'xtatilgan yoki "tozalanishini" kutayotgan zombi;
  • signallarni qayta ishlash sozlamalari — qaysi signallarni ushlaydi, qaysilarini e’tiborsiz qoldiradi va qaysilari sukut bo'yicha uni tugatishiga yo'l qo'yadi.

Bitta dastur bir vaqtda ko'plab jarayonlarga asos bo'lishi mumkin va ularning har biri mustaqil holatga ega. sleep 300 ni ikki alohida terminalda ishga tushirsangiz, ikkalasi ham bir xil /usr/bin/sleep binar faylini bajarsa ham, har biri o'z PID iga ega bo'lgan ikkita bir-biriga bog'liq bo'lmagan jarayon hosil bo'ladi:

sleep 300 &
ps -C sleep -o pid,ppid,user,cmd

Namunaviy natija (sizning PID va foydalanuvchi nomingiz boshqacha bo'ladi):

    PID    PPID USER     CMD
   4821    3990 ali      sleep 300

Oxiridagi & buyruqni terminalni band qilmasdan fonda ishga tushiradi; uning ortidagi mexanizm Foreground va background job’lar maqolasining mavzusi. ps -C sleep buyruq nomi bo'yicha filtrlaydi. Xuddi shu qatorni yana ishga tushirsangiz, ikkinchi, mustaqil PID paydo bo'ladi — bitta fayl, ikkita alohida jarayon.

Note

"Paket o'rnatilgan" degan gapni "ishlab turgan jarayon o'sha paketning joriy versiyasini aks ettiradi" deb tenglashtirmang. Kutubxona yoki binar faylni yangilash diskdagi faylni almashtiradi; eski faylni xotiraga allaqachon ulab olgan jarayon esa qayta ishga tushirilmaguncha eski koddan foydalanishda davom etadi. Shuning uchun xavfsizlik tuzatishi uchun umumiy kutubxonani yangilash unga bog'langan har bir jarayon qayta ishga tushirilmaguncha tugallangan hisoblanmaydi — bu mavzu keyingi modullarda paketlar va xizmatlarni boshqarish bo'limida davom etadi.

Kernel jarayonni qanday yaratadi: fork va exec

Linux jarayonni yo'qdan yaratmaydi. Bitta istisnoni — yuklanish paytida kernel to'g'ridan-to'g'ri yaratadigan PID 1 ni — hisobga olmasa, har bir jarayonni mavjud jarayon ikki bosqichli mexanizm orqali hosil qiladi:

  1. fork() chaqirayotgan jarayonni nusxalaydi. Kernel yangi PID bilan yangi jarayon yaratadi, ota jarayonning xotirasini nusxalaydi (copy-on-write orqali, ya’ni tomonlardan biri yozmaguncha jismoniy xotira aslida nusxalanmaydi), ochiq fayl deskriptorlarini takrorlaydi va bolaga ota jarayon ushlab turgan qolgan hamma narsaning shaxsiy nusxasini beradi. fork() dan darhol keyin ota va bola bir xil dasturni ishlatadi, faqat ikkita alohida jarayon sifatida.
  2. execve() (ko'pincha exec oila nomi bilan ataladi) chaqirayotgan jarayonning xotiradagi tasvirini boshqa dastur bilan almashtiradi. PID o'zgarmaydi, lekin kod, ma’lumotlar va stek yangi bajariladigan fayl belgilagan narsa bilan to'liq almashtiriladi.

Shell har safar buyruq bajarganda shu andozani ko'rsatadi: bash bola jarayon yaratadi (fork), bola esa darhol so'ralgan dasturni exec qiladi (ls, sleep, nginx yoki nima yozilgan bo'lsa). Shell’ning o'ziga tegilmaydi; agar buyruq fonga yuborilmagan bo'lsa, u shunchaki bola jarayon tugashini kutadi.

strace o'rnatilgan bo'lsa, bu ikki qadamni kuzatish mumkin:

strace -f -e trace=execve,clone -- bash -c 'echo hi'

Qisqartirilgan namunaviy parcha:

clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, ...) = 5391
[pid  5391] execve("/usr/bin/echo", ["echo", "hi"], 0x... /* 24 vars */) = 0
hi

clone() — fork() ga o'xshash xatti-harakatni amalga oshiradigan haqiqiy Linux system call’i (fork() ning o'zi hozir odatda ma’lum bayroqlar to'plami bilan clone() ustidagi yupqa o'rama). So'ng execve() shu yangi PID ichiga /usr/bin/echo kodini qo'yadi. -f fork qilingan bola jarayon ortidan boradi, shunda uning o'z execve chaqiruvi ham izda ko'rinadi. -e trace= esa natijani faqat shu ikki chaqiruv bilan cheklaydi, shunda boshqa system call’lar oqimi andozani yashirib qo'ymaydi.

Intervyuda

Intervyuda tez-tez beriladigan savol: fork() butun manzil maydonini nusxalayotgandek ko'rinsa ham, nega uni arzon deyishadi? Javob — copy-on-write: kernel fork() dan darhol keyin ota va bola sahifalarini faqat o'qish uchun va umumiy deb belgilaydi va sahifani faqat jarayonlardan biri unga yozgan paytdagina nusxalaydi. Shu sababli fork() ni chaqirib, darhol execve() qiladigan jarayon (eng ko'p uchraydigan holat) "nusxalash" uchun deyarli hech narsa to'lamaydi — eski tasvir tashlab yuborilishidan oldin deyarli hech narsa aslida nusxalanmaydi.

Thread’lar va jarayonlar: bitta manzil maydonini ulashish

Thread — jarayon ichidagi mustaqil bajarilish oqimi. fork() manzil maydonining o'z shaxsiy nusxasiga ega yangi jarayonni hosil qilsa, thread CLONE_VM|CLONE_THREAD|CLONE_FILES kabi bayroqlar o'rnatilgan clone() bilan yaratiladi. Shuning uchun u yaratuvchi jarayonning xotirasini, ochiq fayl deskriptorlarini va signal sozlamalarini nusxalash o'rniga ulashadi. Bitta dasturning barcha thread’lari bir xil o'zgaruvchilarni to'g'ridan-to'g'ri o'qiy va ularga yoza oladi. Aynan shu narsa ularni yengil va o'zaro tez aloqa qiladigan qiladi, shuningdek to'liq alohida jarayonlarda bo'lmaydigan ma’lumotlar poygasi (data race) muammosini ham keltirib chiqaradi.

Linux thread’ni shunchaki rejalashtiriladigan yana bir vazifa (task) sifatida amalga oshiradi va bu odamlarni chalg'itadigan bitta farqni keltirib chiqaradi:

  • vositalar PID deb ataydigan narsa aslida thread guruhi identifikatori (TGID) — jarayondagi barcha thread’lar ulashadigan va ps chiqaradigan raqam;
  • har bir thread’ning o'z kernel vazifa identifikatori (TID) ham bor va u har bir thread uchun yagona;
  • bitta thread’li jarayonda ikkalasi teng bo'ladi — shuning uchun ko'p thread’li server paydo bo'lmaguncha bu farqni sezmay qolish oson.

Jarayon thread’larini bevosita ko'rish:

ps -o pid,tid,%cpu,comm -L -p "$(pgrep -x nginx | head -1)"
ls /proc/$(pgrep -x nginx | head -1)/task

ps -L har bir thread uchun bitta qator chiqaradi va TID ustunini qo'shadi; /proc/<pid>/task da esa har bir thread uchun bitta ichki katalog bo'ladi va ularning har biri jarayon katalogining kichik nusxasiga o'xshaydi. Bu top -H interaktiv ko'rsatadigan ko'rinishning aynan o'zi (Jarayonlarni kuzatish maqolasida ko'rib chiqiladi). Quyidagi STAT kodlari orasida l bayrog'i paydo bo'lishining sababi ham shu — u ko'p thread’li jarayonni belgilaydi.

Intervyuda

"Jarayon va thread o'rtasida qanday farq bor?" degan savolga Linux’da noaniq emas, aniq javob bor: ikkalasi ham vazifa sifatida rejalashtiriladi, lekin fork() bilan yaratilgan jarayon manzil maydonining shaxsiy, copy-on-write nusxasini oladi, clone(... CLONE_VM|CLONE_THREAD ...) bilan yaratilgan thread esa manzil maydoni, fayl deskriptorlari va PID ni (TGID sifatida) o'z hamkasblari bilan ulashadi. Amaliy oqibatlari shundan kelib chiqadi: thread’lar umumiy xotira orqali IPC’siz aloqa qiladi, lekin poyga holatlaridan qochish uchun sinxronlanishi kerak; alohida jarayonlar esa sukut bo'yicha izolyatsiyalangan va o'zaro aloqa uchun pipe, socket yoki umumiy xotira segmentidan foydalanishi kerak. Izolyatsiya va arzon aloqa o'rtasidagi aynan shu muvozanat ikkalasi ham mavjud bo'lishining sababidir.

PID, PPID va jarayonlar daraxti

Har bir jarayon har qanday paytda aynan bitta PPID ga ega va bu PID 1 dan boshlanadigan daraxtni hosil qiladi (ko'pchilik zamonaviy distributivlarda bu systemd, tarixan esa init). pstree bu daraxtni to'g'ridan-to'g'ri chizib beradi:

pstree -p $$
bash(3990)---pstree(5423)

$$ joriy shell’ning o'z PID iga almashtiriladi, shuning uchun bu natija joriy shell’ni va u hozirgina bola jarayon sifatida ishga tushirgan pstree jarayonini ko'rsatadi. To'liq tizimda pstree -p 1 ni ishga tushirish systemd dan pastga qarab butun daraxtni ko'rsatadi — xizmatlar, ularning ishchi jarayonlari, login sessiyalari va ularning har biri o'z navbatida ishga tushirgan hamma narsa.

Ota jarayon bola jarayondan oldin tugasa, bola hech qanday ajdodsiz yetim qolib ketmaydi, balki odatda PID 1 ga (yoki systemd asosidagi cgroup sozlamalarida ba’zan yaqin atrofdagi subreaper’ga) qayta biriktiriladi. Bu amalda muhim: agar nazorat qiluvchi jarayonni uning bolalari ham o'ladi deb kutib o'ldirsangiz, ular jimgina qayta biriktirilib, resurs sarflashda davom etgan holda undan uzoqroq yashashi mumkin.

ps -eo pid,ppid,user,stat,cmd --forest | head -20

--forest alohida vositasiz, xuddi shu ota–bola munosabatini to'g'ridan-to'g'ri ps natijasi ichida ASCII rasm ko'rinishida chizadi.

Jarayon holatini ps bilan o'qish

ps — bir lahzadagi surat: top yoki htop dan farqli ravishda (Jarayonlarni kuzatish maqolasida ko'rib chiqiladi) u bir marta chiqaradi va tugaydi. Shu sababli u jonli kuzatishdan ko'ra skriptlar va loglash uchun ko'proq mos keladi.

Ko'pincha foydali bo'ladigan to'liq ro'yxat:

ps -eo pid,ppid,user,%cpu,%mem,stat,start,etime,cmd --sort=-%cpu | head

Namunaviy natija:

    PID    PPID USER     %CPU %MEM STAT START     ELAPSED CMD
   1842       1 www-data  4.2  1.1 Sl   09:12       3:41:07 /usr/sbin/nginx: worker process
    987       1 postgres 1.8  3.4 Ssl  09:10       3:43:52 /usr/lib/postgresql/16/bin/postgres
  • -e faqat joriy terminalga bog'langanlarini emas, tizimdagi har bir jarayonni tanlaydi.
  • -o qaysi maydonlarni qaysi tartibda chiqarishni aniq tanlaydi. Bu tarixiy ps aux yoki ps -ef qisqartmalaridan ancha bashorat qilinadigan: ular ps tarixiy moslik uchun bir vaqtda qo'llab-quvvatlaydigan BSD va UNIX opsiya uslublari o'rtasida biroz farq qiladi.
  • --sort=-%cpu CPU foizi bo'yicha kamayish tartibida saralaydi; o'sish tartibi uchun - ni olib tashlang.
  • STAT — jarayon holati kodi: R ishlayapti yoki ishga tayyor, S uzilishi mumkin bo'lgan uyqu, D uzilmas uyqu (odatda I/O ni kutish), T to'xtatilgan, Z zombi. Oxiridagi s sessiya yetakchisini, l ko'p thread’li jarayonni, + esa oldingi plandagi (foreground) jarayonlar guruhi a’zosini bildiradi.
  • ETIME — jarayon ishga tushgandan beri o'tgan real vaqt. U sarflangan haqiqiy protsessor vaqtini foiz sifatida ko'rsatadigan %CPU dan farq qiladi.

Muayyan jarayonni topish

Har biri biroz boshqacha holatga mos keladigan uchta keng tarqalgan usul:

pgrep -a nginx
pidof nginx
ps -C nginx -o pid,ppid,user,cmd

pgrep -a nom bo'yicha (yoki -f bilan to'liq buyruq qatoriga nisbatan regular expression bo'yicha) moslaydi va har bir PID yonida mos kelgan buyruqni chiqaradi — interaktiv ishlatish uchun eng qulayi. pidof faqat PID larni chiqaradi va skriptlar uchun qulay: kill -0 $(pidof nginx) haqiqiy signal yubormasdan kamida bitta nusxa tirikligini tekshiradi. ps -C ro'yxatni aniq buyruq nomi bilan cheklaydi va natija maydonlarini to'g'ridan-to'g'ri tanlashga imkon beradi.

Warning

pgrep/pkill jarayon nomiga yoki -f bilan butun buyruq qatoriga nisbatan moslaydi. Keng andoza argumentlarining biror joyida shunchaki xuddi shu satr bo'lgan, umuman bog'liq bo'lmagan jarayonlarga ham kutilmaganda mos kelishi mumkin. Natijani kill yoki pkill ga uzatishdan oldin aynan nima mos kelishini ko'rish uchun doim avval pgrep -af '<pattern>' ni bajaring.

Real vaziyat: xizmat toza qayta ishga tushganini tasdiqlash

Muammo. catalog.service ning yangi build’ini deploy qilgandan keyin eski jarayon haqiqatan tugaganini va kutilgan foydalanuvchi ostida yangisi ishlayotganini tasdiqlash kerak — deploy uni almashtira olmay, jimgina muvaffaqiyatsiz bo'lmaganiga ishonch hosil qilish uchun.

Biror narsaga tegishdan oldin joriy holatni tekshiring:

systemctl show -p MainPID -p ExecMainStartTimestamp catalog.service
MainPID=18422
ExecMainStartTimestamp=Wed 2026-07-29 08:03:11 UTC

Bu PID va ishga tushish vaqtini yozib qo'ying — ular qayta ishga tushirishdan keyin solishtiriladigan boshlang'ich nuqta.

Qayta ishga tushiring va tekshiring:

sudo systemctl restart catalog.service
systemctl show -p MainPID -p ExecMainStartTimestamp catalog.service
ps -o pid,ppid,user,lstart,cmd -p "$(systemctl show -p MainPID --value catalog.service)"

Kutiladigan natija. MainPID yangi raqamga o'zgaradi va ExecMainStartTimestamp qayta ishga tushirish vaqtiga suriladi. ps qatori yangi PID xizmat uchun sozlangan foydalanuvchi ostida ishlayotganini (agar bu haqiqatan ko'zlangan hisob bo'lmasa, root emas) va uning ishga tushish vaqti (lstart) systemd vaqt tamg'asiga mos kelishini tasdiqlaydi.

Xulosa. Agar restart dan keyin PID o'zgarmasa, ehtimol unit haqiqatan qayta ishga tushmagan, balki eski jarayonni to'xtata olmagan (haqiqiy xatoni journalctl -u catalog.service dan tekshiring). Bu holatda faqat systemctl restart ning chiqish kodiga ishonish haqiqiy muammoni yashirib qo'ygan bo'lardi. PID va vaqt tamg'asini solishtirishning aynan shu andozasi faqat shu misol uchun emas, har qanday xizmatning qayta ishga tushganini tekshirish uchun asos bo'ladi.

Keng tarqalgan xatolar

Paket yangilanishi xizmatni qayta ishga tushirgan deb o'ylash

apt upgrade diskdagi fayllarni almashtiradi; u o'zi eski faylni xotiraga ulab olgan har bir jarayonni qayta ishga tushirmaydi. Tuzatish haqiqatan kuchga kirgan deb hisoblashdan oldin needrestart natijasini (Debian/Ubuntu’da) tekshiring yoki systemctl show -p ExecMainStartTimestamp ni paket o'rnatilgan vaqt bilan solishtiring.

PID qayta ishlatilishini o'sha jarayon tirik qolgani bilan adashtirish

Kernel jarayon tugab, tozalangandan keyin PID raqamlarini qayta ishlatadi. PID ni saqlab, keyinroq uni tekshiradigan skript (kill -0 $PID), agar yetarli vaqt o'tib, jarayonlar ko'p almashgan bo'lsa, bu raqam boshqa, bog'liq bo'lmagan jarayonga berilgani tufayli aldanib qolishi mumkin. Juda qisqa muddatli tekshiruvdan boshqa har qanday holatda faqat PID ni emas, jarayonning ishga tushish vaqti yoki buyruq qatorini ham solishtiring.

Ota jarayonni o'ldirib, bolalari yo'qoladi deb kutish

Nazorat qiluvchi jarayonni tugatish uning bolalarini avtomatik tugatmaydi — ular odatda PID 1 ga qayta biriktiriladi va ishlashda davom etadi. Butun daraxt tugashi kerak bo'lsa, jarayonlar guruhiga signal yuboring (Signallar va jarayonlarni boshqarish maqolasiga qarang) yoki xizmat menejerining o'z to'xtatish mexanizmidan foydalaning.

ps aux natijasini qaysi uslubda ekanini bilmasdan o'qish

ps bir vaqtda BSD uslubidagi opsiyalarni (ps aux), UNIX uslubidagi opsiyalarni (ps -ef) va GNU uzun opsiyalarini qo'llab-quvvatlaydi. Ularni aralashtirish (ps -aux) ba’zi tizimlarda ogohlantirish chiqaradi, chunki -aux BSD uslubidagi aux emas, UNIX uslubidagi bayroqlar sifatida tahlil qilinadi. Aniq -o maydon tanlovidan foydalanish bu noaniqlikni butunlay yo'qotadi va aynan nimani ko'rmoqchi ekaningizni hujjatlashtiradi.

Mashqlar

  1. Turli davomiylikdagi uchta fon sleep jarayonini ishga tushiring, so'ng sleep nomli jarayonlar uchun faqat PID, PPID, o'tgan vaqt va buyruqni o'tgan vaqt bo'yicha kamayish tartibida chiqaradigan bitta ps -eo ... buyrug'ini yozing.
  2. O'zingiz tanlagan qisqa shell pipeline’iga (masalan, bash -c 'ls | wc -l') strace -f -e trace=execve,clone ni qo'llang va har bir clone/execve juftligini aniqlang. Nechta bola jarayon yaratildi va ularning har biri qaysi buyruqni exec qildi?
  3. Tizimingizdagi ishlab turgan xizmatni tanlang (yoki fonda python3 -m http.server kabi xizmatni ishga tushiring) va uning PID, PPID va ishga tushish vaqtini yozib oling. Agar mumkin bo'lsa, faqat uni ishga tushirgan ota shell’ni o'ldiring va ps -eo pid,ppid,cmd yordamida xizmat jarayoni qayta biriktirilganini kuzating.
  4. Hech narsani ishga tushirmasdan, jarayon diskdan o'qishni kutib bloklanganda va terminaldan kiritishni kutayotganda STAT da qanday holat ko'rsatishini va jarayon "qotib qolgandek" ko'ringanda bu farq nega muhim ekanini tushuntiring.

Mashqni bajardim deyish uchun: javoblaringiz shunchaki "ishlayotgandek ko'rinadi" yoki "to'xtagandek ko'rinadi" degan umumiy tavsifga emas, Linux vositalari ko'rsatadigan haqiqiy maydon nomlariga (PID, PPID, STAT, ETIME) tayanishi kerak.

Manbalar

  • Linux man-pages: ps(1): https://man7.org/linux/man-pages/man1/ps.1.html
  • Linux man-pages: fork(2): https://man7.org/linux/man-pages/man2/fork.2.html
  • Linux man-pages: execve(2): https://man7.org/linux/man-pages/man2/execve.2.html
  • Linux man-pages: clone(2): https://man7.org/linux/man-pages/man2/clone.2.html
  • Linux man-pages: proc(5): https://man7.org/linux/man-pages/man5/proc.5.html
  • Linux man-pages: pgrep(1), pkill(1): https://man7.org/linux/man-pages/man1/pgrep.1.html
  • Linux man-pages: credentials(7): https://man7.org/linux/man-pages/man7/credentials.7.html
  • Linux kernel hujjatlari: process scheduling overview: https://www.kernel.org/doc/html/latest/scheduler/index.html
  • strace loyihasi hujjatlari: https://strace.io/