(SIG)WINCH kesik?

 ( apal | 2019. május 9., csütörtök - 18:56 )

Adott ez a kicsi szkriptecske:

#!/bin/bash

trap "stdbuf -o0 echo xyz" WINCH

while true; do
        echo $RANDOM
        sleep 5
done

Miert lehet az hogy a signal kesik es csak akkor hajtja vegre a shell es/vagy a rendszer amikor aktivitas van a terminalon? Az internetekben csak ezt a `stdbuf` dolgot talaltam igy hirtelen, ami kapcsolodo lehet - de nem az a nyitja. Buffereltlenul meg bufferelten is ugyanugy kesik. Rendszer: debian/stretch.

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

"amikor aktivitas van a terminalon" – bármit is értesz ezalatt pontosan, ahhoz semmi köze. A stdbuf meg valóban irreleváns.

Addig nem fut le a trap (talán rá se néz a szignálra a shell), amíg az előtérben indított külső parancs (esetünkben "sleep 5") fut. Majd ha az lefutott, és ismét dolga akad a shellnek, majd akkor foglalkozik az ablakméret szignállal.

Na jo, de ha azt irom oda meg az elejebe, hogy:

trap "stdbuf -o0 echo abc" INT

akkor az miert fut le azonnal ha ctrl+c-zek egyet? szignal-szignal, nem?

A WINCH szignállal a sleep nem törődik. A shell igen, kezeli amikor ráér.

Az INT szignállal viszont törődik a shell és a sleep is, a sleep kilép. És ekkor a shell is rögtön kezelni tudja a saját INT-jét. Cseréld ki a sleep-et egy olyan parancsra, amelyik nem lép ki INT-re (például bc), hiába ütöd a Ctrl+C-t, nem fog megjelenni az "abc".

1. a Ctrl-C nyomása interaktív shell-ben nem egészen ugyanaz mint simán egy SIGINT ellövése valahonnan.
az interaktív shell ugye új pgid-del indítja el az egy parancssorban megadott parancsot vagy parancsokat (compound command), és Ctrl-C-nél az egész process groupra küld SIGINTet.

2. mellékesen stdbuf echo helyett /bin/echo mint külső parancs is kielégíti az stdout flush-olást.

~~~~~~~~
deb http://deb.uucp.hu/ wheezy yazzy repack

> stdbuf echo helyett /bin/echo mint külső parancs is kielégíti az stdout flush-olást.

A beépített echo is. A bash flush-ol minden beépített parancsa után.

biztos? én 4.2.37(1)-gyel nem ezt tapasztalom.
ugye a külső echo se flush-ol explicit, viszont zárja a pipe-ot és ezért az OS flush-ol.

~~~~~~~~
deb http://deb.uucp.hu/ wheezy yazzy repack

> biztos?

Eléggé. Nemrég volt róla szó a bash levlistán. Így alakult történelmileg, és így tud csak a viselkedés konzisztens lenni külső programok illetve beépített parancsok között.

> én 4.2.37(1)-gyel nem ezt tapasztalom.

Hogy néz ki a konkrét progid, és hogy viselkedik?

> ugye a külső echo se flush-ol explicit, viszont zárja a pipe-ot és ezért az OS flush-ol.

Kavarsz.

Mit értesz OS alatt? A kernel szintjén a flusholás nem létező koncepció, a kernel szintjén csak írni lehet. Amit kiírtál, azt kiírtad pont akkor. Amit nem, az nincs.

A pufferelést és flusholást a libc vezeti be egy felette lévő stdio rétegként. A külső echo, ha stdio-t használ, tehát pufferel (amiben nem vagyok biztos, de jó eséllyel igen), akkor valószínűleg kilépéskor is flushol, mivel a felső szintű fájlkezelő rutinok a libc-ben így vannak megírva, tehát nem ragad benn adat. De tudtommal nem zárja a fájlleírót (majd a kernel, amikor a processz kilép, kénytelen lesz), és nem is érdekli hogy pipe-ba megy-e. Sőt, magát a pipe-ot mint objektumot, melynek akár más író processze is lehet, nem zárja (bármit is értsél ez alatt) explicit módon, nem is tudná.

valóban megtévedtem azzal, hogy a flush nem syscall, hanem libc szintű függvény.

> nem zárja a fájlleírót
> majd a kernel, amikor a processz kilép

a külső echo processz futása végén van close(1) syscall a exit_group(0) előtt. erre gondoltam. vagy a program hívta explicit vagy a kernel a processz kilépése folyományán - ahogy te is mondod -, a lényeg számomra az hogy így külső echo-val egyből megjelenik a pipe másik végén az adat. míg megfigyelésem szerint belső echóval nem. de lehet hogy rosszul figyeltem meg, örülnék ha a végére tudnánk járni.

az én esetemben volt egy processz, ami csinált egy pipe-ot, forkolt, rá dup()-olta a pipe író végét az stdout-ra, az olvasó végét meg az stdin-re a szokott módon. aztán execelt egy bash szkriptet. a szkript normálisan built-in echo-val írt de nem egész sorokat (pl: echo -n processing .; sleep 60; echo -n .; sleep 60; echo -n .).
Ekkor azt tapasztaltam, hogy a parent processz csak komplett sorokat olvas ki, noha alacsony szintű sima read()-del olvasott 1 byte-onként nem blokkoló módon (fcntl F_SETFL O_NONBLOCK, read(1, buffer, 1)).
Míg ha a szkriptben külső echót használtam, akkor a félsorokat is megkapta.
Ebből arra következtettem, hogy a built-in echo kimenete valahol bufferelve van.
Korábban azt gondoltam, hogy mivel a belső/külső echo ugyanarra az fd-re írnak és a buffer beállítás az fd-re vonatkozna, nyilván sorbufferelés van és egy alprocessz meghívásával implicit kicsikarhatok belőle egy flush-t.
Node - miképp megvilágítottad – a bufferbeállítás a file handlerre vonatkozik és processzenként más-más lehet.
Ennek ellenére továbbra se tiszta számomra, hogy ha a bash minden egyes builtin echo után üríti a buffert, akkor miért nem látom az adatot addig amíg nem jön egy "\n".

~~~~~~~~
deb http://deb.uucp.hu/ wheezy yazzy repack

{ echo -n processing .; sleep 5; echo -n .; sleep 5; echo -n .; } | cat
( echo -n processing .; sleep 5; echo -n .; sleep 5; echo -n . ) | cat

Mindkettő azonnal írja nekem a pöttyöket, tehát a cicus megkapja egyenként azokat.

Tudsz mutatni kódot, ami reprodukálja az általad említett, pufferelt jelenséget?

nem tudom reprodukálni az a működést amire emlékeztem, ezért kénytelen vagyok elfogadni hogy a belső echo is flush-ol.

egyébként ebben a snippetben attól hogy más bracketinget használsz, letiltja a builtin parancsokat?

~~~~~~~~
deb http://deb.uucp.hu/ wheezy yazzy repack

Nem; a flusholás kérdése tekintetében valóban a két parancs azonos kell hogy legyen, így tulajdonképpen fölösleges volt mindkettőt leírnom.

> attól hogy más bracketinget használsz, letiltja a builtin parancsokat?

A { ... ; } forma esetén a shell nem hoz létre egy alprocessz a a zárójelek közötti ... parancsok végrehajtásához, míg a ( ... ) forma esetén igen.

=====
tl;dr
Egy-két mondatban leírnátok, hogy lehet ellopni egy bitcoin-t?

ezt vágom, de a builtin/external echo tekintetében nem különbözik. nem?

~~~~~~~~
deb http://deb.uucp.hu/ wheezy yazzy repack

a bash csak akkor dogozza fel a szignálokat, amikor nem futtat épp külső parancsot.
tehát szerintem bevárja a sleep-et.

én így szoktam elkerülni:

sleep()
{
  local sleep_pid exit_status
  command sleep "$@" &
  sleep_pid=$!
  while ! wait $pid; do true; done
  return $exit_status
}

bármilyen kód...

a 'while' a 'wait' köré azért kell, mert megszakad a wait, amikor szignált kap, hogy feldolgozza a szignált, de aztán nem lép vissza magától a 'wait', tehát addig waitelek újra meg újra amíg lehet.

EDIT

while kill -0 $sleep_pid; do wait $sleep_pid; exit_status=$?; done
csere
while ! wait $pid; do true; done

~~~~~~~~
deb http://deb.uucp.hu/ wheezy yazzy repack