Enn ein Heisenbug framhjá krókódílnum

Enn ein Heisenbug framhjá krókódílnum

$> set -o pipefail

$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Повезло!

$> fortune | head -1 > /dev/null && echo "Повезло!" || echo "Вы проиграли"
Вы проиграли

Hér fortune skilyrt forrit án exit(rand()).

Geturðu útskýrt? hvað er að hérna?

Ljóðsögulegur útrás

Þessum Heisenbug kynntist ég fyrst fyrir aldarfjórðungi. Síðan fyrir gáttina í FaxNET var nauðsynlegt að búa til nokkur tól í gegnum lagnir "spila tígli" undir FreeBSD. Eins og við var að búast taldi ég mig vera háþróaðan og nokkuð reyndan forritara. Þess vegna ætlaði ég að gera allt eins vandlega og vandlega og hægt var og huga sérstaklega að villumeðferð...

Fyrri reynsla mín af því að takast á við villur í sendmail og uucp/uupc jók kostgæfni mína við „rækilega villumeðferð“. Það þýðir ekkert að kafa ofan í smáatriði þeirrar sögu, en ég barðist við þessa Heisenbug í tvær vikur í 10-14 tíma. Þess vegna var þess minnst og í gær kíkti þessi gamli kunningi við í heimsókn aftur.

TL;DR svar

Gagnsemi head getur loka rásinni frá fortune í einu um leið og hann les fyrstu línuna. Ef fortune gefur út fleiri en eina línu, síðan samsvarandi símtal write() mun skila villu eða tilkynna að færri bæti séu framleidd en beðið var um. Aftur á móti skrifað með varkárri villumeðferð fortune hefur rétt til að endurspegla þessa stöðu í útgöngustöðu sinni. Þá vegna uppsetningar set -o pipefail mun virka || echo "Вы проиграли".

Hins vegar, head kemst kannski ekki í tæka tíð loka áður fortune mun klára að gefa út gögn. Þá mun það virka && echo "Повезло!".

Í einum af mínum í dag GNUMakefile það er svoleiðis brot:

echo '#define MDBX_BUILD_COMPILER "$(shell set -o pipefail; $(CC) --version | head -1 || echo 'Please use GCC or CLANG compatible compiler')"'

Þýtt á mannlegt

Það er algengt hér fyrir GNU Make и bash á þýðanda hátt með því að nota valkostinn --version það spyr hver hann sé, og ef valmöguleikinn er ekki studdur, þá er stubbur settur inn "Vinsamlegast notaðu GCC eða CLANG samhæfðan þýðanda".

Svipað ketilplötu má finna hvar sem er. Það birtist á þessum stað fyrir löngu síðan og virkaði fullkomlega alls staðar (Linux, Solaris, OSX, FreeBSD, WSL o.s.frv.). En í gær í altlinux á pallinum Elbrus 2000 (E2K) Ég tók eftir:

#define MDBX_BUILD_COMPILER "lcc:1.23.20:Sep--4-2019:e2k-v3-linux Please use GCC or CLANG compatible compiler"

Satt að segja þekkti ég ekki gamla „kunningja“ minn strax. Þar að auki hefur verkefnið þegar verið prófað margsinnis á Elbrus og undir mörgum mismunandi dreifingum, þar á meðal Alt. Með ýmsum þýðendum, útgáfum af GNU Make og bash. Þess vegna vildi ég ekki sjá mistök mín hér.

Þegar reynt var að endurskapa vandamálið og/eða skilja hvað var í gangi fóru skrýtnari hlutir að gerast.
Skipanalínustafur:

echo "#define MDBX_BUILD_COMPILER '$(set -o pipefail; LC_ALL=C cc --version | head -1 || echo "Please use GCC or CLANG compatible compiler")'"

Öðru hvoru myndi það framleiða aukatexta, svo ekki... Oft myndi einn af valmöguleikunum standa í ansi langan tíma, en ef þú potaðir lengur, fékkstu alltaf báða!

Auðvitað, strace allt okkar! Og eftir að hafa slegið inn strace tirade, en ekki haft tíma til að ýta á Enter, þekkti ég gamla vin minn, herra Heisenbug, og teymið þýðandi sjálfur fyrir 25 árum, Nostalgía… Og ég ákvað að vera sorgmædd og skrifa þessa athugasemd 😉

Við the vegur, eins og allir sjálfsvirðingar Heisenbug, undir strace vill helst ekki fjölga sér.

Svo hvað er í gangi?

  • Gagnsemi head hefur rétt (eða réttara sagt, er jafnvel neyddur) til að loka rásinni sem verið er að lesa um leið og hún les umbeðinn fjölda lína.
  • Gagnaframleiðandi forritsritari (í þessu tilfelli cc) getur prenta margar línur og ókeypis Gerðu þetta með mörgum símtölum write().
  • Ef lesandinn mun hafa tíma til að loka rásinni hjá sér áður en upptöku lýkur hjá rithöfundinum, þá mun rithöfundurinn fá villu.
  • Rithöfundaforrit rétt á bæði hunsa rásarvilluna og endurspegla hana í útfyllingarkóðanum þínum.
  • Vegna uppsetningar set -o pipefail leiðslulokunarkóði verður ekki núll (rangur) ef niðurstaðan er ekki núll úr að minnsta kosti einum þætti, og þá mun það virka || echo "Please use GCC or CLANG compatible compiler".

Það geta verið afbrigði eftir því hvernig ritunarforritið virkar með merkjum. Til dæmis getur forritið hætt óeðlilega (með sjálfvirkri myndun á stöðvunarstöðu sem er ekki núll/villu), eða write() mun skila niðurstöðu um að skrifa færri bæti en beðið var um og stillt errno = EPIPE.

Hver er sekur?

Í því tilviki sem lýst er lítið af öllu. Villa meðhöndlun í cc (lcc:1.23.20:Sep—4-2019:e2k-v3-linux) er ekki óþarfi. Í mörgum tilfellum er betra að skipta sér af varkárni, þó að það afhjúpi skyndilega galla í ketilplötu sem er hannaður fyrir hefðbundna hegðun.

Hvað á að gera?

Rangt:

fortune | head -1 && echo "Повезло, но вы рискуете!" || echo "WTF?"

Rétt:

  1. (fortune && echo "Успешно" || echo "Ошибка") | head -1

    Hér mun snemmbúin lokun á pípu vera meðhöndluð af skipanatúlknum við þjónustu við hreiðri leiðslu ("innan" sviga). Í samræmi við það, ef fortune mun tilkynna skriflega villu til lokaðrar rásar í stöðunni, síðan úttakið || echo "Ошибка" það kemst hvergi þar sem rásin er þegar lokuð.

  2. fortune | cat - | head -1 && echo "Успешно" || echo "Ошибка"

    Hér er gagnsemin cat virkar sem dempara því það hunsar villuna EPIPE við afturköllun. Þetta er nóg fyrir niðurstöðu núna fortune lítill (nokkrar línur) og passar í biðminni rásarinnar (frá 512 bætum til ≈64K, í flestum stýrikerfum ≥4K). Annars gæti vandamálið komið aftur.

Hvernig á að vinna rétt EPIPE og aðrar upptökuvillur?

Það er engin ein rétt lausn, en það eru einfaldar ráðleggingar:

  • EPIPE krafist verður að afgreiða (og endurspeglast í útgöngustöðunni) þegar þú sendir út gögn sem krefjast heiðarleika. Til dæmis meðan á rekstri skjalavörslu eða öryggisafrita stendur.
  • EPIPE betra að hunsa þegar birtar eru upplýsingar og aukaskilaboð. Til dæmis þegar birtar eru upplýsingar um valkosti --help eða --version.
  • Ef kóðann sem verið er að þróa er hægt að nota í leiðslu áður | headþá EPIPE Það er betra að hunsa, annars er betra að vinna úr og endurspegla í útgöngustöðunni.

Ég vil nota tækifærið til að koma á framfæri þakklæti til liðanna MCST и altlinux fyrir frábært afkastamikið starf. Ákveðni þín er ótrúleg!
Haltu áfram Camarades, upp fundum í haust!

Takk berez til að leiðrétta innsláttarvillur og villur.
KDPV frá Georgi A.

Heimild: www.habr.com

Bæta við athugasemd