
Mənim əsas işim, daha çox, proqram təminatının sistemlərinin yayılmasıdır, yəni bir çox vaxt belə suallara cavab verməyə çalışaraq keçirəm:
- Proqram təminatı müdeveloperi üçün işləyir, amma mənim üçün yox. Niyə?
- Dünən bu proqram təminatı mənim üçün işləyirdi, amma bu gün yox. Niyə?
Bu, adi proqram təminatının debuq edilmesindən bir az fərqli olan bir çeşiddir. Adi debuq etmə — kodun məntiqi ilə bağlıdır, amma yayılmanın debuq edilməsi — kodun və mühitin qarşılıqlı əlaqəsinə aiddir. Problem bir məntiqi xətadan qaynaqlansa da, bir maşında hər şey işləyərkən, digərində olmaması deməkdir ki, problem mühitlə bağlıdır.
Ona görə də adi debuq alətləri kimi gdb mənim yayılmanın debuq edilməsi üçün başqa bir alətlər komplektim var. Və «Niyə bu proqram mənim üçün işləmir?» tipli problemləri aradan qaldırmaq üçün sevdiyim alət strace.
strace nədir?
— sistem çağırışlarının «izlənməsi» üçün bir alətdir. Əvvəlcə Linux üçün yaradılmışdı, amma bu debuq etmə üsullarını digər sistemlərin alətləri ilə də həyata keçirmək olar ( və ).
Tətbiqi çox sadədir. Hər hansı bir əmr ilə strace-i başlatmaq kifayətdir və o, bütün sistem çağırışlarını yığın dump-a göndərəcək (amma əvvəlcə, yəqin ki, strace-i quraşdırmalısınız): strace):
$ strace echo Hello
...Nümunə bir çox məlumat...
write(1, "Hellon", 6) = 6
close(1) = 0
close(2) = 0
exit_group(0) = ?
+++ 0 ilə bitdi +++Sistem çağırışları nədir? Bu, əməliyyat sisteminin nüvəsi üçün bir növ API-dir. Uzun müddət əvvəl proqramlara işlədikləri «dəmir»-ə birbaşa giriş vardı. Məsələn, bir şeyin ekranda görüntülənməsi lazım olduğunda, o, portlar və ya video avadanlıq üçün ayrılan registirlər ilə oynayırdı. Çoxlu tapşırıqları dəstəkləyən kompüter sistemləri məşhurlaşdıqca, qarışıqlıq baş verdi, çünki müxtəlif proqramlar «dəmir» üçün mübarizə aparırdılar. Bir proqramda olan səhvlər, bəzən bütün sistemi sarsıda bilərdi. Sonra CPU-da imtiyaz rejimləri (ya da «halqa qoruma») meydana gəldi. Ən imtiyazlı olan nüvənin özündə idi: o, «dəmir»-ə tam giriş əldə edərək, daha az imtiyazlı proqramları yaradırdı ki, onlar da nüvədən «dəmir» ilə qarşılıqlı əlaqə üçün icazə istəməli idilər — sistem çağırışları vasitəsilə.
Binar səviyyədə sistem çağırışı, sadə funksiya çağırışından bir az fərqlənir, amma əksər proqramlar standart kitabxanada bir qabıqdan istifadə edir. Yəni, standart POSIX C kitabxanasında write()funksiya çağırışı var ki, bu, sistem çağırışı üçün bütün arxitektura asılı kodu ehtiva edir. write.

Qısa desək, proqramın öz mühitilə (kompüter sistemləri) əlaqəsi sistem çağırışları vasitəsilə həyata keçirilir. Buna görə də bir maşında işləyən proqramın digərində işə yaramaması halında, sistem çağırışlarının izini araşdırmaq yaxşı olardı. Daha spesifik desək, burada sistem çağırışlarının izinin köməyi ilə analiz edə biləcəyiniz tipik məsələlərin siyahısı var:
- Konsol giriş-çıxışı
- Şəbəkə giriş-çıxışı
- Fayl sisteminə çıxış və fayl giriş-çıxışı
- Proses axınının ömrünü idarə etmək
- Aşağı səviyyəli yaddaş idarəsi
- Xüsusi cihazların sürücülərinə çıxış
strace ne zaman istifadə olunur?
Teorik olaraq, strace istifadəçi mühitindəki hər hansı proqramla istifadə olunur, çünki istifadəçi mühitindəki hər bir proqram sistem çağırışları etməlidir. O, daha çox kompilə olunan, aşağı səviyyəli proqramlarla daha səmərəli işləyir, lakin Python kimi yüksək səviyyəli dillərlə də işləyir, əgər icra mühitinin və tərcüməçinin yaradacağı əlavə səs-küyü aşsanız.
Bütün parıltısı strace proqramın bir maşında yaxşı işlədiyi, ancaq digərində birdən-birə işləməmə zamanı özünü göstərir. Bu, çaşqın fayl, icazə və ya bəzi komandaların icrasını uğursuz cəhdlər barədə mücərrəd mesajlar verən hallarda baş verir. Təssüf ki, yüksək səviyyəli problemlərlə, məsələn, sertifikat təsdiqləmə xətası ilə o qədər də yaxşı uyğun gəlmir. Ümumiyyətlə, burada strace, bəzən və daha yüksək səviyyəli alətlərin (məsələn, sertifikatın təhlili üçün komanda xətası aləti) kombinasiyasına ehtiyac olur. openssl İzolyasiya olunmuş serverdə iş misalını götürək, lakin sistem çağırışlarının izini daha mürəkkəb yerləşdirmə platformalarında da yerinə yetirmək mümkündür. Yalnız uyğun alət seçmək lazımdır.
Sadə bir debuq nümunəsi
Deyək ki, möhtəşəm bir server tətbiqi foo işlətmək istəyirsiniz, amma belə olur:
$ foo Konfiqurasiya faylını açmaqda xəta: Belə bir fayl və ya direktor yoxdur
Aydındır ki, o, sizin yazdığınız konfiqurasiya faylını tapa bilmədi. Bu, bəzən paket menecerləri tətbiqi tərtib edərkən faylların gözlənilən yerini üst-üstə düşdüyü üçün baş verir. Quraşdırma təlimatına uyğun gedəndə bir distropda faylları tamamilə fərqli yerdə tapa bilirsən. Problemi bir neçə saniyə içərisində həll etmək olar, əgər xəta mesajı konfiqurasiya faylını harada axtarmaq lazım olduğunu desə, lakin bu, demir. Bəs harada axtarmaq lazımdır?Əgər mənbə koduna çıxışınız varsa, onu oxuyub hər şeyi öyrənə bilərsiniz. Yaxşı bir alternativ plan, lakin ən sürətli həll deyil. Adım-adım debuq vasitələrinə kimi müraciət edə bilərsiniz.
gibi gdb və proqramın nə etdiyinə baxmaq, lakin mühitlə qarşılıqlı əlaqəni göstərmək üçün xüsusi olaraq hazırlanmış bir alətdən istifadə etmək daha effektlidir: strace.
Çıktı strace artıq görünə bilər, lakin yaxşı xəbər odur ki, onun böyük bir hissəsini rahatlıqla görməməzlikdən gəlmək olar. Tez-tez, izləniş nəticələrini ayrıca faylda saxlamaq üçün -o operatorundan istifadə etmək faydalı olur:
$ strace -o /tmp/trace foo
Konfiqurasiya faylını açmaqda xəta: Heç bir belə fayl və ya qovluq yoxdur
$ cat /tmp/trace
execve("foo", ["foo"], 0x7ffce98dc010 /* 16 vars */) = 0
brk(NULL) = 0x56363b3fb000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (Heç bir belə fayl və ya qovluq yoxdur)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=25186, ...}) = 0
mmap(NULL, 25186, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f2f12cf1000
close(3) = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "177ELF2113 3 > 1 260A2 "..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1824496, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2f12cef000
mmap(NULL, 1837056, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f2f12b2e000
mprotect(0x7f2f12b50000, 1658880, PROT_NONE) = 0
mmap(0x7f2f12b50000, 1343488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7f2f12b50000
mmap(0x7f2f12c98000, 311296, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x16a000) = 0x7f2f12c98000
mmap(0x7f2f12ce5000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b6000) = 0x7f2f12ce5000
mmap(0x7f2f12ceb000, 14336, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f2f12ceb000
close(3) = 0
arch_prctl(ARCH_SET_FS, 0x7f2f12cf0500) = 0
mprotect(0x7f2f12ce5000, 16384, PROT_READ) = 0
mprotect(0x56363b08b000, 4096, PROT_READ) = 0
mprotect(0x7f2f12d1f000, 4096, PROT_READ) = 0
munmap(0x7f2f12cf1000, 25186) = 0
openat(AT_FDCWD, "/etc/foo/config.json", O_RDONLY) = -1 ENOENT (Heç bir belə fayl və ya qovluq yoxdur)
dup(2) = 3
fcntl(3, F_GETFL) = 0x2 (flags O_RDWR)
brk(NULL) = 0x56363b3fb000
brk(0x56363b41c000) = 0x56363b41c000
fstat(3, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x8), ...}) = 0
write(3, "Konfiqurasiya faylını açmaqda xəta"..., 60) = 60
close(3) = 0
exit_group(1) = ?
+++ 1 ilə çıxır +++Təxminən çıxışın ilk səhifəsi strace — adətən başlatma üçün aşağı səviyyəli hazırlıqdır. (Çox sayda çağırış mmap, mprotect, brk aşağı səviyyəli yaddaşın aşkar edilməsi və dinamik kitabxanaların göstərilməsi üçün şeylər. Adətən, baxma zamanı çıxışları strace ən sonundan oxumaq daha yaxşıdır. Aşağıda olacaq çağırış write, xəta mesajını verən. Yuxarıya baxırıq və ilk səhv sistem çağırışını görürük — çağırış openat, xəta verən ENOENT ("fayl və ya qovluq tapılmadı"), açmağa çalışdığı /etc/foo/config.json. Burada konfiqurasiya faylı olmalıdır.
Bu sadəcə bir nümunə idi, lakin demək olar ki, 90% vaxt, nə zaman ki, istifadə edirəm strace, daha çox bu cür mürəkkəb şeylərin edilməsi lazım olmur. Aşağıda — budaqlanma üzrə tam addım-addım bələdçi:
- Proqramdan sistem xəta mesajı haqqında başa düşülməz bir şeyə görə özünü narahat etmək
- Proqramı yenidən başlatmaqla strace
- İzləniş nəticələrində xəta mesajını tapmaq
- Yuxarıya doğru gedin, ilk uğursuz sistem çağırışına rastlaşana qədər
Dördüncü addımdakı sistem çağırışı nəyin səhv getdiyini göstərə bilər.
İpucu
Daha karmaşık bir hata ayıklama örneği göstermeden önce, etkili kullanım için birkaç ipucu vereceğim. strace:
man — arkadaşınızdır.
Pek çok *nix sisteminde, çekirdek sistem çağrılarının tam listesini elde etmek için çalıştırmanız yeterlidir: man syscalls. Şeyler şunları göreceksiniz: brk(2), bu yüzden daha fazla bilgi için şunu çalıştırabilirsiniz: man 2 brk.
Küçük tuzaklar: man 2 fork bana kabuk sayfasını gösteriyor. fork() daxilindədir. GNU libc, görünüşe göre, çağrı ile gerçekleştirilmiştir: clone(). Çağrının semantiği fork bir program yazarak kalır fork(), ve izleme yapıp başvuruları bulamayacağım — yerine şunları göreceğim: fork. Böyle tuzaklar, kaynak kodunu çıktı ile karşılaştırmaya başladığınızda kafa karıştırabilir. clone()İçeriği bir dosyaya kaydetmek için -o kullanın. strace.
Genellikle geniş bir çıktı üretebilir, bu yüzden izleme sonuçlarını ayrı dosyalarda saklamak faydalı olabilir (yukarıdaki örnekte olduğu gibi). Ayrıca, yazılım çıktısını
strace konsoldaki çıktıyla karıştırmamak için yardımcıdır. strace Daha fazla argüman verisi görmek için -s kullanın.
Kesinlikle, hata mesajının ikinci kısmının yukarıdaki izleme örneğinde gösterilmediğini fark etmişsinizdir. Bunun nedeni,
varsayılan olarak argüman dizgesinin yalnızca ilk 32 baytını göstermesidir. Daha fazlasını görmek istiyorsanız, çağrıya şunu ekleyin: strace -s 128 çağrısına -u, dosya soketleri ve benzeri şeylerin izlenmesini kolaylaştırır. strace.
‘Her şey bir dosyadır’ demek, *nix sistemlerinin tüm girdi/çıktıları dosya tanıtıcıları kullanarak gerçekleştirildiği anlamına gelir; bu ya dosya, ya ağ ya da süreçler arası kanallar için geçerlidir. Bu, programlama açısından uygundur, ancak olayları gerçekten takip etmeyi zorlaştırır, özellikle de genel olarak gördüğünüzde:
sistem çağrısı izleme sonuçlarında. read və write Bir işçi tanıtıcısını çıkışta her bir tanıtıcıyı gösterdiğinizde -u ekleyin.
Artık başlamış bir işleme -p ile bağlanın. Aşağıdaki örnekten de görüleceği gibi, bazen zaten çalışan bir programı izlemek gerekir. Eğer onun 1337 süreci olarak çalıştığını biliyorsanız (örneğin, çıktıdan), bu şekilde izleyebilirsiniz:$ strace -p 1337 ...sistem çağrısı izleme çıktısı... strace Root yetkilerine ihtiyacınız olabilir.
-f ile alt süreçleri izleyin.
Varsayılan olarak yalnızca tek bir süreci izler. Eğer bu süreç alt süreçler yaratıyorsa, bir alt sürecin yaratımına dair sistem çağrısını görebilirsiniz, ancak alt sürecin sistem çağrıları görünmeyecektir. [user@testhost ~]$ ps PID TTY TIME CMD 761020 pts/2 00:00:00 bash 809720 pts/2 00:00:00 psEğer hatanın alt süreçte olduğunu düşünüyorsanız, şu operatörü kullanın:
, bu onun izlenmesini sağlayacaktır. Bunun dezavantajı, çıktının daha fazla kafa karıştırıcı hale gelmesidir. Ne zamançıkışa geçmez ve sonra tam olarak ne olduğunu anlamak zor olur.
«fork()» ifadelerinde sistem çağrılarını izleyebilirsiniz.
strace Bu durumda, durumumuzu görmek için izleme çıktısını yaymak isteyebiliriz.
İşte bazen izleme çıktısını ayarlamak önemli bir adım anlamına gelir. -f. strace bir prosesi və ya bir budağı izlədikdə, o, çağırışların tək axınını göstərir. Bir anda bir neçə prosesi izlədikdə, çağırışın başlanğıcını, mesajla kəsilən birini görə bilərsiniz <unfinished …>, daha sonra — digər icra budaqları üçün çağırışların toplusunu, və yalnız bundan sonra — birincinin bitməsini <… foocall resumed>. Yoxsa bütün izləmə nəticələrini müxtəlif fayllara ayırın, eyni zamanda operatoru istifadə edərək -ff (şəraitlər — yerinə strace).
İzləməni -e ilə filtr edin
Gördüyünüz kimi, izləmə nəticəsi — bütün mümkün sistem çağırışlarının gerçek bir yığıncıdır. Bayraqlarla -e izləməni filtr edə bilərsiniz (bax. yerinə strace). Əsas üstünlük odur ki, filtrasiya ilə izləməni başlamaq, tam izləmə etdikdən sonra grepyerinə yetirməkdən daha sürətlidir. Düzünü desək, mənim üçün əksər hallarda əhəmiyyətli deyil.
Bütün xətalar pis deyil
Sadə və yaygın bir misal — bir proqramın bir neçə yerdə bir faylı axtarması, məsələn, icra edilə bilən faylın saxlandığı səbət kataloqunu axtaran bir qabuk:
$ strace sh -c uname
...
stat("/home/user/bin/uname", 0x7ffceb817820) = -1 ENOENT (Belə bir fayl və ya kataloq yoxdur)
stat("/usr/local/bin/uname", 0x7ffceb817820) = -1 ENOENT (Belə bir fayl və ya kataloq yoxdur)
stat("/usr/bin/uname", {st_mode=S_IFREG|0755, st_size=39584, ...}) = 0
...Heüristika "son uğursuz sorğu xəta mesajından əvvəl" müvafiq xətaları axtarmaq üçün yaxşıdır. Nə olursa olsun, ən sonunda başlamaq məntiqidir.
Sistem çağırışlarını anlamağa kömək edən C dilində proqramlaşdırma bəlgələridir
Standart C kitabxanası çağırışları — sistem çağırışları deyil, yalnız incə bir səth qatıdır. Ona görə də, əgər C dilində necə və nə etməli olduğunuzu bir az başa düşsəniz, sistem çağırışlarının izləmə nəticələrində daha asan anlamalısınız. Məsələn, şəbəkə sistemlərinə çağırışların təhlilində çətinliklərlə qarşılaşırsınızsa, həmən klassik .
Daha mürəkkəb bir debuq misalı
Artıq dediyim kimi, sadə debuq misalı — daha çox məşğul olduğum şeylərin bir nümunəsidir strace. Amma bəzən gerçək bir araşdırma tələb olunur, buna görə də sizə daha mürəkkəb bir debuq misalı təqdim edirəm.
— vəzifənin icraçısı, başqa bir *nix demonunun tətbiqi cron. O, serverdə quraşdırılıb, amma kimsə cədvəli redaktə etməyə cəhd edərkən bu baş verir:
# crontab -e -u logs
bcrontab: Fatal: Could not create temporary fileYaxşı, deməli, bcron bir fayl yazmağa cəhd etdi, lakin nəyə görə olduğunu etiraf etmir. O zaman strace:
# strace -o /tmp/trace crontab -e -u logs
bcrontab: Fatal: Could not create temporary file
# cat /tmp/trace
...
openat(AT_FDCWD, "bcrontab.14779.1573691864.847933", O_RDONLY) = 3
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f82049b4000
read(3, "#Ansible: logsaggn20 14 * * * lo"..., 8192) = 150
read(3, "", 8192) = 0
munmap(0x7f82049b4000, 8192) = 0
close(3) = 0
socket(AF_UNIX, SOCK_STREAM, 0) = 3
connect(3, {sa_family=AF_UNIX, sun_path="/var/run/bcron-spool"}, 110) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f82049b4000
write(3, "156:Slogs #Ansible: logsaggn20 1"..., 161) = 161
read(3, "32:ZCould not create temporary f"..., 8192) = 36
munmap(0x7f82049b4000, 8192) = 0
close(3) = 0
write(2, "bcrontab: Fatal: Could not creat"..., 49) = 49
unlink("bcrontab.14779.1573691864.847933") = 0
exit_group(111) = ?
+++ exited with 111 +++Demək olar ki, sonlarda bir xəta mesajı var write, amma bu dəfə bir şey fərqlidir. Birincisi, ondan əvvəl baş verən müvafiq sistem çağırışı yoxdur. İkincisi, görünür ki, kimlərsə artıq xəta mesajını oxuyub. Görünür, həqiqi problem başqa bir yerdədir, və bcrontab sadəcə olaraq mesajı təkrarlayır.
Əgər man 2 read-ə baxarsanız, sonra görmək mümkündür ki, birinci arqument (3) - bu *nix tərəfindən bütün giriş-çıxış emalı üçün istifadə olunan fayl təsvirçisidir. Fayl təsvirçisi 3-ün nəyi təmsil etdiyini necə bilmək olar? Bu xüsusi halda, işə salmaq mümkündür strace operators Aşağıdaki örnekten de görüleceği gibi, bazen zaten çalışan bir programı izlemek gerekir. Eğer onun 1337 süreci olarak çalıştığını biliyorsanız (örneğin, çıktıdan), bu şekilde izleyebilirsiniz: (yuxarıda bax), və o avtomatik olaraq bunu izah edəcək, lakin belə şeyləri hesablamaq üçün, izləmə nəticələrini necə oxumaq və analiz etmək lazım olduğunu bilmək faydalıdır.
Fayl təsvirçisinin qaynağı bir çox sistem çağırışlarından biri ola bilər (bu, təsvirçinin nə üçün olduğunu nəzərə alır – konsol, şəbəkə soketi, konkret fayl və ya digər bir şey üçün), amma necə olursa olsun, çağırışları axtardığımızda 3-ü qaytarmağa çalışırıq (yəni izləmə nəticələrində "= 3" axtarırıq). Bu nəticədə 2 var: openat ən üstdə və socket ortada. openat faylı açır, amma close(3) sonra yenidən bağlandığını göstərəcək. (Bir qazanc: fayl təsvirçiləri açıldıqda və bağlandıqda yenidən istifadə edilə bilər). socket() uyğundur, çünki o, read(), və beləliklə bcrontabın bir şeylə soket vasitəsilə işlədiyini öyrənirik. Növbəti sətir göstərir ki, fayl təsvirçisi ilə bağlıdır unix domain socket yolunda /var/run/bcron-spool.
Beləliklə, qarşı tərəfdə bağlı olan prosesi tapmaq lazımdır unix socket üçün. Bu məqsəd üçün bir neçə zərif fənd var, və ikisi də server yerləşdirmələrini tdebug etmək üçün faydalıdır. Birincisi - istifadə etməkdir netstat və ya daha yeni ss (socket status). Hər iki əmrlə sistemin aktiv şəbəkə bağlantıları göstərilir və operatoru -l eşidən soketləri təsvir etmək üçün, həmçinin operatoru -p soketə müştəri kimi qoşulan proqramları göstərmək üçün. (Faydalı seçimlər daha çoxdur, amma bu iş üçün bu ikisi kifayətdir.)
# ss -pl | grep /var/run/bcron-spool
u_str LISTEN 0 128 /var/run/bcron-spool 1466637 * 0 users:(("unixserver",pid=20629,fd=3))Bu, eşidənin inixserverolduğunu göstərir, proses ID 20629 ilə işləyir. (Və müvafiq olaraq, o, soket olaraq fayl təsvirçisi 3 istifadə edir.)
Eyni məlumati tapmaq üçün ikinci həqiqətən faydalı alət lsofadlanır. O, sistemdə bütün açılan faylları (və ya fayl təsvirçilərini) sıralayır. Yaxud bir konkret fayl haqqında məlumat ala bilərsiniz:
# lsof /var/run/bcron-spool
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
unixserve 20629 cron 3u unix 0x000000005ac4bd83 0t0 1466637 /var/run/bcron-spool type=STREAMProses 20629 - uzun ömürlü bir serverdir, beləliklə, ona qoşula bilərsiniz strace bir şeylə strace -o /tmp/trace -p 20629. Kron işini başqa bir terminalda redaktə etsək – izləmə nəticələrinin çıxışı ilə səhv baş verəcək. Budur nəticə:
accept(3, NULL, NULL) = 4
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7faa47c44810) = 21181
close(4) = 0
accept(3, NULL, NULL) = ? ERESTARTSYS (Restarting if SA_RESTART is set)
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=21181, si_uid=998, si_status=0, si_utime=0, si_stime=0} ---
wait4(0, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG|WSTOPPED, NULL) = 21181
wait4(0, 0x7ffe6bc36764, WNOHANG|WSTOPPED, NULL) = -1 ECHILD (No child processes)
rt_sigaction(SIGCHLD, {sa_handler=0x55d244bdb690, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7faa47ab9840}, {sa_handler=0x55d244bdb690, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7faa47ab9840}, 8) = 0
rt_sigreturn({mask=[]}) = 43
accept(3, NULL, NULL) = 4
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7faa47c44810) = 21200
close(4) = 0
accept(3, NULL, NULL) = ? ERESTARTSYS (Restarting if SA_RESTART is set)
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=21200, si_uid=998, si_status=111, si_utime=0, si_stime=0} ---
wait4(0, [{WIFEXITED(s) && WEXITSTATUS(s) == 111}], WNOHANG|WSTOPPED, NULL) = 21200
wait4(0, 0x7ffe6bc36764, WNOHANG|WSTOPPED, NULL) = -1 ECHILD (No child processes)
rt_sigaction(SIGCHLD, {sa_handler=0x55d244bdb690, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7faa47ab9840}, {sa_handler=0x55d244bdb690, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7faa47ab9840}, 8) = 0
rt_sigreturn({mask=[]}) = 43
accept(3, NULL, NULL)(Sonuncu accept() başqa bir dəfə, təəssüf ki, bu nəticə axtardığımız xətanı ehtiva etmir. Socketə bcrontag-dan göndərilən və ya ondan qəbul edilən heç bir mesaj görmürük. Onun yerinə, yalnız proses idarəçiliyi var (clone, wait4, SIGCHLD və s.) Bu proses, təsəvvür etdiyiniz kimi, faktiki işi yerinə yetirən bir alt proses yaradır. Onun izini tutmaq lazım gələrsə, çağrışa əlavə edin strace -f. Yeni nəticədə strace ilə xətanın mesajını axtardıqda burada nə tapacağıq -f -o /tmp/trace -p 20629:
21470 openat(AT_FDCWD, "tmp/spool.21470.1573692319.854640", O_RDWR|O_CREAT|O_EXCL, 0600) = -1 EACCES (İcazə verilmədi)
21470 write(1, "32:ZTemporary f yarada bilmədik"..., 36) = 36
21470 write(2, "bcron-spool[21470]: Fatal: logs:"..., 84) = 84
21470 unlink("tmp/spool.21470.1573692319.854640") = -1 ENOENT (Belə bir fayl və ya qovluq yoxdur)
21470 exit_group(111) = ?
21470 +++ 111 ilə çıxdı +++Budur, bu artıq bir şeydir. Proses 21470, yolu "tmp/spool.21470.1573692319.854640" olan faylı yaratmağa çalışarkən "icazə verilmədi" xətası alır. tmp/spool.21470.1573692319.854640 (indiki işçi kataloquna aiddir). Əgər indiki işçi kataloqunu bilsəydik, tam yolu da biləcəkdik və prosesin orada müvəqqəti faylını niyə yarada bilməyəcəyini öyrənə bilərdik. Təəssüf ki, proses artıq çıxdığı üçün sadəcə lsof -p 21470 istifadə edərək indiki kataloqu tapmaq mümkün deyil, lakin geriyə doğru işləmək mümkündür - PID 21470-in kataloqu dəyişdirən sistem çağırışlarını axtarmaq. (Belə çağırışlar yoxdursa, PID 21470-in, ehtimal ki, onları valideynindən miras aldığı görünür və bu artıq lsof -p ilə aydın olmur.) Bu sistem çağırışı - chdir (müasir şəbəkə axtarış motorları ilə müəyyən etmək asandır). Və budur, PID 20629-un serverinin nəticələrində geriyə doğru axtarışların nəticəsi:
20629 clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7faa47c44810) = 21470
...
21470 execve("/usr/sbin/bcron-spool", ["bcron-spool"], 0x55d2460807e0 /* 27 vars */) = 0
...
21470 chdir("/var/spool/cron") = 0
...
21470 openat(AT_FDCWD, "tmp/spool.21470.1573692319.854640", O_RDWR|O_CREAT|O_EXCL, 0600) = -1 EACCES (İcazə verilmədi)
21470 write(1, "32:ZTemporary f yaratmaq mümkün olmadı"..., 36) = 36
21470 write(2, "bcron-spool[21470]: Fatal: qeydlər:"..., 84) = 84
21470 unlink("tmp/spool.21470.1573692319.854640") = -1 ENOENT (Belə bir fayl və ya direktoriy yoxdur)
21470 exit_group(111) = ?
21470 +++ 111 ilə çıxdı +++(İstifadəni itirsəniz, bəlkə, əvvəlki yazımı oxumaq faydalı olar .) Beləliklə, server PID 20629 yol üzrə bir fayl yaratmaq üçün icazə almadı /var/spool/cron/tmp/spool.21470.1573692319.854640. Bunun səbəbi ehtimal ki, klassik fayl sistemi icazə parametrləridir. Yoxlayaq:
# ls -ld /var/spool/cron/tmp/
drwxr-xr-x 2 root root 4096 Nov 6 05:33 /var/spool/cron/tmp/
# ps u -p 20629
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
cron 20629 0.0 0.0 2276 752 ? Ss Nov14 0:00 unixserver -U /var/run/bcron-spool -- bcron-spoolBurada problem vardır! Server istifadəçi cron kimi işləyir, amma yalnız root-un bu katalogda yazmaq icazəsi var /var/spool/cron/tmp/. Sadə əməl chown cron /var/spool/cron/tmp/ bu problemi düzəldəcəkdir. (Əgər problem bununla bağlı deyilsə, növbəti ən ehtimal olunan məsələ SELinux və ya AppArmor tipli təhlükəsizlik modulu olacaq, buna görə də mesaj jurnalını bcron dmesg yoxlamaq istərdim..)
Nəticədə
Sistem çağırış izlərinin nəticələrindən başlayaraq yeni başlayanlar başa düşə bilməz, amma ümid edirəm ki, bu problemlərin geniş spektrini düzəltmək üçün sürətli bir yol verdim. Təsəvvür edin ki, çoxprosesli bir bcronsistem çağırış izləyicisi ilə çalışırsınız.
Nəticələrin geri sıraya düzülməsi bacarıq tələb edir, amma artıq qeyd etdiyim kimi, əksər hallarda, yalnız stracealdığım izləri götürərək və sonundan başlayaraq səhvləri axtarıram. Hər halda, strace mənim üçün çox vaxtı başa düşməkdə kömək edir. Ümid edirəm ki, sizə də faydalı olacaq.
Mənbə: habr.com
