Szoftvertelepítés hibakeresése a strace segítségével

Szoftvertelepítés hibakeresése a strace segítségével

A napi munkám nagyrészt szoftvertelepítésből áll, ami azt jelenti, hogy sok időt töltök azzal, hogy megválaszoljam az alábbi kérdéseket:

  • Ez a szoftver a fejlesztőnek működik, de nekem nem. Miért?
  • Tegnap ez a szoftver működött nekem, de ma már nem. Miért?

Ez egyfajta hibakeresés, amely kissé eltér a szokásos szoftveres hibakereséstől. A rendszeres hibakeresés a kód logikájáról szól, de a telepítési hibakeresés a kód és a környezet közötti kölcsönhatásról. Még ha a probléma gyökere logikai hiba is, az a tény, hogy az egyik gépen minden működik, a másikon nem, azt jelenti, hogy a probléma valahogy a környezetben van.

Tehát a szokásos hibakereső eszközök helyett, mint pl gdb Különféle eszköztáram van a telepítés hibakereséséhez. És a kedvenc eszközöm a probléma kezelésére, mint például: „Miért nem működik ez a szoftver nekem?” hívott Eztán.

Mi az a strace?

Eztán egy eszköz a „rendszerhívás nyomkövetéséhez”. Eredetileg Linuxra készült, de ugyanezek a hibakereső trükkök más rendszerekhez is használható eszközökkel (DTrace vagy ktration).

Az alap alkalmazás nagyon egyszerű. Csak le kell futtatnod a strace-t bármilyen paranccsal és kiírja az összes rendszerhívást (bár először valószínűleg magadnak kell telepítenie Eztán):

$ strace echo Hello
...Snip lots of stuff...
write(1, "Hellon", 6)                  = 6
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

Mik ezek a rendszerhívások? Ez olyasmi, mint egy API az operációs rendszer kerneléhez. Valamikor a szoftvernek közvetlen hozzáférése volt ahhoz a hardverhez, amelyen futott. Ha például valamit megjelenítenie kellett a képernyőn, akkor a videoeszközök portjaival vagy memórialeképezett regisztereivel játszott. Amikor a többfeladatos számítógépes rendszerek népszerűvé váltak, káosz uralkodott, miközben különféle alkalmazások harcoltak a hardverért. Az egyik alkalmazás hibái másokat is lerombolhatnak, ha nem az egész rendszert. Ezután a privilégium módok (vagy „gyűrűvédelem”) megjelentek a CPU-ban. A kernel lett a legkiváltságosabb: teljes hozzáférést kapott a hardverhez, így kevésbé kiváltságos alkalmazások jöttek létre, amelyeknek már hozzáférést kellett kérniük a kerneltől, hogy rendszerhívásokon keresztül interakcióba lépjenek a hardverrel.

A bináris szinten a rendszerhívás némileg eltér az egyszerű függvényhívástól, de a legtöbb program a szabványos könyvtárban wrappert használ. Azok. a POSIX C standard könyvtár függvényhívást tartalmaz ír(), amely tartalmazza a rendszerhívás összes architektúra-specifikus kódját ír.

Szoftvertelepítés hibakeresése a strace segítségével

Röviden, az alkalmazás és környezete (számítógépes rendszerek) közötti bármilyen interakció rendszerhívásokon keresztül valósul meg. Ezért, ha a szoftver működik az egyik gépen, de nem a másikon, jó lenne megnézni a rendszerhívás-követés eredményeit. Pontosabban, itt van egy lista azokról a tipikus pontokról, amelyek rendszerhívási nyomkövetéssel elemezhetők:

  • Konzol I/O
  • Hálózati I/O
  • Fájlrendszer hozzáférés és fájl I/O
  • Egy folyamatszál élettartamának kezelése
  • Alacsony szintű memóriakezelés
  • Hozzáférés adott eszközillesztő-programokhoz

Mikor kell használni a strace-t?

Elméletben, Eztán a felhasználói térben lévő bármely programmal használható, mert a felhasználói területen lévő bármely programnak rendszerhívást kell végrehajtania. Hatékonyabban működik lefordított, alacsony szintű programokkal, de működik olyan magas szintű nyelvekkel is, mint a Python, ha át tudja vágni a futási környezetből és az értelmezőből származó további zajokat.

Teljes pompájában Eztán olyan szoftverek hibakeresése során nyilvánul meg, amelyek az egyik gépen jól működnek, de a másikon hirtelen leállnak, és homályos üzeneteket produkál a fájlokról, engedélyekről, vagy sikertelen próbálkozásokról néhány parancs végrehajtására vagy valami másra... Kár, de nem olyan jól kombinálható magas szintű problémákkal, mint például a tanúsítvány-ellenőrzési hibák. Ez általában kombinációt igényel Eztánnéha ltnyom és magasabb szintű eszközök (például a parancssori eszköz openssl a tanúsítvány hibakereséséhez).

Példaként egy önálló kiszolgálót fogunk használni, de a rendszerhívás-követés gyakran bonyolultabb telepítési platformokon is elvégezhető. Csak a megfelelő eszközöket kell kiválasztania.

Egyszerű hibakeresési példa

Tegyük fel, hogy szeretné futtatni a csodálatos foo szerveralkalmazást, és ez az, amit a végén:

$ foo
Error opening configuration file: No such file or directory

Úgy tűnik, hogy nem találta az általad írt konfigurációs fájlt. Ez azért történik, mert néha a csomagkezelők egy alkalmazás fordításakor felülbírálják a várt fájlhelyeket. És ha követi az egyik disztribúció telepítési útmutatóját, a másikban teljesen eltérő fájlokat talál, mint amire számított. A probléma néhány másodperc alatt megoldható lenne, ha a hibaüzenetből kiderülne, hol kell keresni a konfigurációs fájlt, de nem. Szóval hol kell nézni?

Ha hozzáfér a forráskódhoz, akkor elolvashatja és mindent megtudhat. Jó tartalék terv, de nem a leggyorsabb megoldás. Használhat egy lépésről lépésre hibakeresőt, mint például gdb és nézze meg, mit csinál a program, de sokkal hatékonyabb egy olyan eszköz használata, amelyet kifejezetten a környezettel való interakció megjelenítésére terveztek: Eztán.

Teljesítmény Eztán feleslegesnek tűnhet, de a jó hír az, hogy a legtöbbet nyugodtan figyelmen kívül lehet hagyni. Gyakran hasznos az -o operátor használata a nyomkövetési eredmények külön fájlba mentéséhez:

$ strace -o /tmp/trace foo
Error opening configuration file: No such file or directory
$ cat /tmp/trace
execve("foo", ["foo"], 0x7ffce98dc010 /* 16 vars */) = 0
brk(NULL)                               = 0x56363b3fb000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
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 (No such file or directory)
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, "Error opening configuration file"..., 60) = 60
close(3)                                = 0
exit_group(1)                           = ?
+++ exited with 1 +++

Körülbelül a kimenet teljes első oldala Eztán - Ez általában alacsony szintű felkészítés az indulásra. (Sok hívás mmap, mprotect, írótoll olyan dolgokra, mint például az alacsony szintű memória észlelése és a dinamikus könyvtárak megjelenítése.) Valójában a hibakeresés során a kimenet Eztán Jobb, ha a legvégétől olvasod. Lent lesz egy kihívás ír, amely hibaüzenetet jelenít meg. Feljebb nézünk, és látjuk az első hibás rendszerhívást - a hívást openat, ami hibát jelez ENOENT ("fájl vagy könyvtár nem található") megpróbálja megnyitni /etc/foo/config.json. Itt kell lennie a konfigurációs fájlnak.

Ez csak egy példa volt, de azt mondanám, hogy az esetek 90%-át használom Eztán, nincs ennél sokkal nehezebb tennivaló. Az alábbiakban egy teljes, lépésenkénti hibakeresési útmutató található:

  • Mérges lehet egy program rendszer-y hibájával kapcsolatos homályos üzenet miatt
  • Indítsa újra a programot a Eztán
  • Keresse meg a hibaüzenetet a nyomkövetési eredmények között
  • Menjen feljebb, amíg el nem éri az első sikertelen rendszerhívást

Nagyon valószínű, hogy a 4. lépésben a rendszerhívás felfedi, hogy mi hibázott.

tippek

Mielőtt bemutatnék egy példát a bonyolultabb hibakeresésre, mutatok néhány trükköt a hatékony használat érdekében Eztán:

az ember a barátod

Sok *nix rendszeren a kernelhez intézett rendszerhívások teljes listája lekérhető futtatással férfi syscalls. Ilyeneket fog látni brk (2), ami azt jelenti, hogy futással több információhoz juthatunk férfi 2 brk.

Kis gereblye: férfi 2 villa megmutatja a shell oldalát Villa() в GNU libc, ami, mint kiderült, hívással valósul meg klón (). Hívja a szemantikát villa változatlan marad, ha programot írunk Villa(), és futtasson egy nyomkövetést – nem találok hívást villa, helyettük lesz klón (). Az ilyen rake-ek csak akkor zavarnak meg, ha elkezdi összehasonlítani a forrást a kimenettel Eztán.

A -o gombbal mentheti a kimenetet fájlba

Eztán kiterjedt kimenetet generálhat, ezért gyakran hasznos a nyomkövetési eredményeket külön fájlokban tárolni (mint a fenti példában). Ez segít elkerülni a programkimenet és a kimenet összekeverését Eztán a konzolban.

A -s használatával további argumentumadatokat tekinthet meg

Talán észrevette, hogy a hibaüzenet második fele nem jelenik meg a fenti példakénti nyomon. Azért, mert Eztán alapértelmezetten csak a karakterlánc argumentum első 32 bájtja látható. Ha többet szeretne látni, adjon hozzá valami hasonlót -s 128 a hívásra Eztán.

-y megkönnyíti a fájlok, socketek stb. nyomon követését.

Az "All is file" azt jelenti, hogy a *nix rendszerek minden I/O-t fájlleírók segítségével hajtanak végre, függetlenül attól, hogy ez fájlra, hálózatra vagy folyamatközi csövekre vonatkozik. Ez kényelmes a programozáshoz, de megnehezíti annak nyomon követését, hogy mi is történik valójában, ha közös dolgokat látunk olvas и ír a rendszerhívás nyomkövetési eredményeiben.

Operátor hozzáadásával -y, erőltetni fogod Eztán a kimenetben minden fájlleíró megjegyzéssel jelölje meg, hogy mire mutat.

Csatolás egy már futó folyamathoz a -p** segítségével

Amint az alábbi példából látni fogja, néha egy már futó programot kell nyomon követnie. Ha ismert, hogy 1337-es folyamatként fut (mondjuk a kimenetről ps), akkor így követheti nyomon:

$ strace -p 1337
...system call trace output...

Szükség lehet root jogokra.

Az -f használatával figyelheti a gyermekfolyamatokat

Eztán Alapértelmezés szerint csak egy folyamatot követ. Ha ez a folyamat gyermekfolyamatokat hoz létre, akkor a gyermekfolyamat létrehozásához szükséges rendszerhívás látható, de az utódfolyamat rendszerhívásai nem jelennek meg.

Ha úgy gondolja, hogy a hiba egy gyermekfolyamatban van, használja az utasítást -f, ez lehetővé teszi a nyomkövetését. Ennek az a hátránya, hogy a kimenet még jobban megzavarja Önt. Amikor Eztán nyomon követ egy folyamatot vagy egy szálat, egyetlen hívási eseményfolyamot jelenít meg. Ha egyszerre több folyamatot nyomon követ, előfordulhat, hogy egy üzenet megszakítja a hívás kezdetét , majd - egy csomó hívás a többi végrehajtási ághoz, és csak ezután - az első vége <…fooccal resumed>. Vagy ossza fel az összes nyomkövetési eredményt különböző fájlokra, szintén az operátor használatával -ff (részletek itt vezetés on Eztán).

Nyomok szűrése a -e használatával

Amint látja, a nyomkövetés eredménye az összes lehetséges rendszerhívás valódi halma. Zászló -e Szűrheti a nyomot (lásd vezetés on Eztán). A fő előnye, hogy gyorsabb egy szűrt nyomkövetés futtatása, mint egy teljes nyomkövetés, majd utána grep`at. Őszintén szólva szinte mindig nem érdekel.

Nem minden hiba rossz

Egy egyszerű és általános példa egy olyan program, amely egyszerre több helyen keres egy fájlt, például egy shell egy végrehajtható fájlt tartalmazó könyvtárat:

$ strace sh -c uname
...
stat("/home/user/bin/uname", 0x7ffceb817820) = -1 ENOENT (No such file or directory)
stat("/usr/local/bin/uname", 0x7ffceb817820) = -1 ENOENT (No such file or directory)
stat("/usr/bin/uname", {st_mode=S_IFREG|0755, st_size=39584, ...}) = 0
...

Az olyan heurisztika, mint az „utolsó sikertelen kérés a hiba bejelentése előtt”, jók a releváns hibák megtalálásában. Bárhogy is legyen, logikus a legvégéről kezdeni.

A C programozási oktatóanyagok segíthetnek megérteni a rendszerhívásokat.

A C-könyvtárak szabványos hívásai nem rendszerhívások, hanem csak egy vékony felületi réteg. Tehát, ha legalább egy kicsit megérti, hogyan és mit kell csinálni C-ben, akkor könnyebben megértheti a rendszerhívás nyomkövetésének eredményeit. Például problémái vannak a hálózati rendszerek hívásainak hibakeresésével, nézze meg ugyanazt a klasszikust Bija útmutatója a hálózati programozáshoz.

Egy bonyolultabb hibakeresési példa

Már mondtam, hogy az egyszerű hibakeresés példája egy példa arra, amivel leginkább foglalkoznom kell, amikor dolgozom Eztán. Néha azonban valódi vizsgálatra van szükség, ezért itt van egy valós példa a fejlettebb hibakeresésre.

bcron - feladatfeldolgozás ütemező, a *nix démon másik megvalósítása cron. Telepítve van a szerveren, de ha valaki megpróbálja szerkeszteni az ütemezést, a következő történik:

# crontab -e -u logs
bcrontab: Fatal: Could not create temporary file

Oké, ez azt jelenti bcron megpróbált írni egy bizonyos fájlt, de nem sikerült, és nem ismeri el, miért. Felfedezés Eztán:

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

Hibaüzenet jelenik meg a legvégén ír, de ezúttal valami más. Először is, nincs releváns rendszerhívási hiba, ami általában ez előtt következik be. Másodszor, egyértelmű, hogy valahol valaki már elolvasta a hibaüzenetet. Úgy tűnik, az igazi probléma valahol máshol van, és bcrontab egyszerűen lejátssza az üzenetet.

Ha megnézed férfi 2 olvas, láthatja, hogy az első argumentum (3) egy fájlleíró, amelyet a *nix minden I/O feldolgozáshoz használ. Honnan tudhatom meg, hogy a 3. fájlleíró melyiket jelöli? Ebben az esetben futhat Eztán kezelővel -y (lásd fent), és automatikusan megmondja, de az ehhez hasonló dolgok kiderítéséhez hasznos tudni, hogyan kell olvasni és elemezni a nyomkövetési eredményeket.

A fájlleíró forrása lehet egy a sok rendszerhívás közül (minden attól függ, hogy mire való a leíró – konzolra, hálózati socketre, maga a fájlra vagy valami másra), de akárhogy is legyen, keresünk 3-at visszaadva hív (azaz „= 3”-t keresünk a nyomkövetési eredményekben). Ebben az eredményben ezek közül kettő van: openat a legtetején és foglalat Középen. openat megnyitja a fájlt, de közel(3) ekkor azt mutatja, hogy újra bezárul. (Rake: a fájlleírók újrafelhasználhatók, amikor megnyitják és bezárják). Hívás foglalat() alkalmas, mert ez az utolsó olvas(), és kiderül, hogy a bcrontab aljzaton keresztül működik valamivel. A következő sor azt mutatja, hogy a fájlleíró hozzá van rendelve unix domain socket az út mentén /var/run/bcron-spool.

Tehát meg kell találnunk a kapcsolódó folyamatot unix foglalat a másik oldalon. Van néhány ügyes trükk erre a célra, mindkettő hasznos a szervertelepítések hibakereséséhez. Az első a használata netstat vagy újabb ss (aljzat állapota). Mindkét parancs megmutatja a rendszer aktív hálózati kapcsolatait, és átveszi az utasítást -l lehallgató aljzatok, valamint az operátor leírására -p a sockethez csatlakoztatott programok kliensként történő megjelenítéséhez. (Sokkal több hasznos lehetőség van, de ez a kettő elegendő ehhez a feladathoz.)

# ss -pl | grep /var/run/bcron-spool
u_str LISTEN 0   128   /var/run/bcron-spool 1466637   * 0   users:(("unixserver",pid=20629,fd=3))

Ez arra utal, hogy a hallgató a parancs inixszerver, amely 20629-es folyamatazonosítóval fut. (És véletlenül a 3. fájlleírót használja foglalatként.)

A második igazán hasznos eszköz ugyanazon információk megtalálására az ún lsof. Felsorolja az összes megnyitott fájlt (vagy fájlleírót) a rendszeren. Vagy kaphat információkat egy adott fájlról:

# 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=STREAM

A Process 20629 egy hosszú élettartamú kiszolgáló, így csatlakoztatható Eztán valami hasonlót használva strace -o /tmp/trace -p 20629. Ha szerkeszt egy cron feladatot egy másik terminálon, akkor hibás nyomkövetési kimenetet fog kapni. És itt az eredmény:

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 (To be restarted 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 (To be restarted 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

(Utolsó elfogad() nyomkövetéskor nem fejeződik be.) Ez az eredmény sajnos ismét nem tartalmazza a keresett hibát. Nem látunk olyan üzeneteket, amelyeket a bcrontag küld a socketnek, vagy fogad onnan. Ehelyett teljes folyamatvezérlés (klón, várj4, SIGCHLD stb.) Ez a folyamat egy gyermekfolyamatot szül, amely, ahogy sejthető, elvégzi az igazi munkát. És ha el kell találnia a nyomát, csatlakozzon a híváshoz strace -f. Ezt fogjuk megtalálni, ha az új eredményben a hibaüzenetre keresünk a strace segítségével -f -o /tmp/trace -p 20629:

21470 openat(AT_FDCWD, "tmp/spool.21470.1573692319.854640", O_RDWR|O_CREAT|O_EXCL, 0600) = -1 EACCES (Permission denied) 
21470 write(1, "32:ZCould not create temporary f"..., 36) = 36
21470 write(2, "bcron-spool[21470]: Fatal: logs:"..., 84) = 84
21470 unlink("tmp/spool.21470.1573692319.854640") = -1 ENOENT (No such file or directory)
21470 exit_group(111)                   = ?
21470 +++ exited with 111 +++

Na, ez már valami. A 21470-es folyamat "hozzáférés megtagadva" hibát kap, amikor fájlt próbál létrehozni az elérési úton tmp/spool.21470.1573692319.854640 (az aktuális munkakönyvtárhoz kapcsolódóan). Ha ismernénk az aktuális munkakönyvtárat, akkor a teljes elérési utat is ismernénk, és ki tudnánk találni, hogy a folyamat miért nem tudja abban létrehozni az ideiglenes fájlt. Sajnos a folyamat már befejeződött, így nem lehet csak úgy használni lsof -p 21470 az aktuális címtár megtalálásához, de az ellenkező irányba is dolgozhat - keresse meg a PID 21470 rendszerhívásokat, amelyek megváltoztatják a címtárat. (Ha nincsenek ilyenek, akkor a 21470-es PID biztosan a szülőjétől örökölte őket, és ez már megtörtént lsof -p nem lehet kideríteni.) Ez a rendszerhívás az chdir (amit a modern online keresők segítségével könnyű kideríteni). És itt van a fordított keresések eredménye a nyomkövetési eredmények alapján, egészen a 20629-es szerver PID-ig:

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 (Permission denied) 
21470 write(1, "32:ZCould not create temporary f"..., 36) = 36
21470 write(2, "bcron-spool[21470]: Fatal: logs:"..., 84) = 84
21470 unlink("tmp/spool.21470.1573692319.854640") = -1 ENOENT (No such file or directory)
21470 exit_group(111)                   = ?
21470 +++ exited with 111 +++

(Ha eltévedtél, érdemes elolvasnod az előző bejegyzésemet a *nix folyamatkezelésről és a shellekről.) Tehát a 20629-es kiszolgáló PID nem kapott engedélyt fájl létrehozására az elérési úton /var/spool/cron/tmp/spool.21470.1573692319.854640. Ennek valószínűleg a klasszikus fájlrendszer-engedély-beállítások az okai. Ellenőrizzük:

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

Ott van a kutya elásva! A szerver cron felhasználóként fut, de csak a root jogosult a könyvtárba írni /var/spool/cron/tmp/. Egyszerű parancs chown cron /var/spool/cron/tmp/ akaraterő bcron megfelelően működjön. (Ha nem ez volt a probléma, akkor a következő legvalószínűbb gyanúsított egy kernel biztonsági modul, például a SELinux vagy az AppArmor, ezért ellenőrizném a kernel üzenetnaplóját dmesg.)

Összességében

A rendszerhívások nyomkövetése elsöprő erejű lehet egy kezdő számára, de remélem, megmutattam, hogy ezek egy gyors módja a gyakori telepítési problémák egész osztályának hibakeresésének. Képzelje el, hogy megpróbál hibakeresni egy többfolyamatot bcronlépésenkénti hibakereső segítségével.

A nyomkövetési eredmények visszafelé elemzése a rendszerhívási lánc mentén szakértelmet igényel, de mint mondtam, szinte mindig a használata Eztán, csak megkapom a nyomkövetési eredményt, és a végétől kezdve keresem a hibákat. Akárhogyan is, Eztán segít sok időt megtakarítani a hibakeresés során. Remélem neked is hasznos lesz.

Forrás: will.com

Hozzászólás