bash pipelining wtf

Fórumok

Történt egyszer, hogy megszámoltam az egyszerre futó openvpn processeket kétféle módon, és a két módszer közötti különbségre lettem figyelmes:

root@konzumribanc:~ # ps ax | grep -c openvpn
22
root@konzumribanc:~ # ps ax | grep openvpn | wc -l
23

ebből következik, hogy:

root@konzumribanc:~ # ps ax | grep grep
root@konzumribanc:~ #

vagy egyszerűbben:

root@konzumribanc:~ # ps | grep grep
root@konzumribanc:~ #

továbbgondolva:

root@konzumribanc:~ # ps | grep grep
root@konzumribanc:~ #
root@konzumribanc:~ # ps | grep grep | tee
8922 pts/0 00:00:00 grep
root@konzumribanc:~ # ps | tee | grep grep
root@konzumribanc:~ #

Ez hogy lehet? A grep-nek kéne "látnia" saját magát, hiszen fut, nem?
Lehet, hogy valami sysctl miatt a pipe bufferel, majd mikor EOF-ot (vagy buffer overflowot) kap, akkor indítja a grepet?

root@konzumribanc:~ # ps --version && grep --version && bash --version && uname -sr
procps version 3.2.6
grep (GNU grep) 2.5.1
GNU bash, version 3.1.0(1)-release (i686-pc-linux-gnu)
Linux 2.4.33.3

Egyébként meg Devil-Linux 1.2.11 a disztró, de az 1.2.8-as verzióval is ugyanez van. Más (sid, gentoo, ubuntu) disztribbel jónak tűnt.

Hozzászólások

valami ilyesmi van, hogyha 3 processzt hoz letre az ember, akkor lehet hogy az elso" ma'r le is futott, mig a 3-ik el sem indult:


- pipe letrehoz
- bash forkol
      - pipe egyik fele dup2()-zve stdout-ra
      - exec ps
      - child lea'll
- pipe egyik fele beza'r
- masikpipe letrehoz
- bash forkol
      - pipe masik fele dup2()-zve stdin-re
      - masikpipe egyik fele dup2()-zve stdout-ra
      - exec grep
      - child leall
- maskpipe egyik fele beza'r
- bash forkol
      - masikpipe masik fele dup2()-zve stdin-re
      - exec tee
      - child leall

itt az elso es a harmadik processznek nincs semmi kozos dolga, tehat minden tovabbi nelkul elofordulhat, hogy az elso" mar le is allt, mikozben a harmadik me'g el sem indult. de persze az is lehet (olyan a scheduling), hogy az elso" me'g fut, mikor a harmadik elindul.

pl:


apal@xeft:~$ echo | ps -A | wc -l
90
apal@xeft:~$ echo | ps -A | wc -l
89
apal@xeft:~$ echo | ps -A | wc -l
90
apal@xeft:~$ echo | ps -A | wc -l
90
apal@xeft:~$ echo | ps -A | wc -l
90
apal@xeft:~$ echo | ps -A | wc -l
90
apal@xeft:~$ echo | ps -A | wc -l
89
apal@xeft:~$ echo | ps -A | wc -l
93
apal@xeft:~$ echo | ps -A | wc -l
92
apal@xeft:~$ echo | ps -A | wc -l
93

vagy hasonloan:


apal@xeft:~$ for x in  `seq 20` ; do echo | ps -A | wc -l ; done
92
93
93
92
92
93
93
92
92
93
93
93
92
93
93
92
92
93
93
93

nincs valami 'ugyes' shell alias beallitva?

$ ps aux |grep grep
bandi 14036 0.0 0.2 3676 584 pts/0 R+ 00:16 0:00 grep grep

nekem megvan :P
--
A vegtelen ciklus is vegeter egyszer, csak kelloen eros hardver kell hozza!

egyszeru a magyarazat:
nem egyszerre indul a ps es a grep. bash elinditja a ps-t, fork, inditja a grepet, es a megfelelo in/out-okat osszekapcsolja. ha valamiert a grep lassan indul, akkor nem lesz benne a ps listaban.

de ha megvarom mig elindul a grep akkor mar mindig benne lesz:
$ for i in `seq 1 10`; do (sleep 1;ps aux) |grep grep; done | wc -l
10

update: ehe, ugyanezt irta apal is, bar o annyiban tevedett, hogy a processek nem allnak le, szepen megvarjak mig mindenki vegez:
$ (sleep 1;ps aux) |(grep ps;sleep 3)| grep ps
bandi 26335 0.0 0.2 3676 592 pts/0 S+ 03:03 0:00 grep ps
bandi 26336 0.0 0.2 3680 592 pts/0 S+ 03:03 0:00 grep ps
bandi 26337 0.0 0.3 2564 860 pts/0 R+ 03:03 0:00 ps aux

Elbandi
--
A vegtelen ciklus is vegeter egyszer, csak kelloen eros hardver kell hozza!

processek nem allnak le, szepen megvarjak mig mindenki vegez:
ez annyiban lehet igaz, hogy a parent-nek be kell varnia, wait()-tel vagy waitpid()-del, tehat a bash valoban tudni fogja, mikor ki allt le. es amig nem konfirmalja, addig zombike'nt tenyleg ottmarad (ilyet is lehet latni neha). de a child definite leall (exit()), amit ve'gez a dolga'val - mivel ma's nem avatkozik bele a futtata'sa'ba.

(vagyis nem pontosan igy, mert a bash /meg altalaban a multitaszkos programok/ explicite nem va'r/nak/, hanem akkor wait()-tel, ha kap valami szignalt, asszem a sigchld-t)

ps ax | grep -c openvpn | grep -v grep

Szűrd ki a grepet és mindig ugyanaz lesz az eredmény.

--
A nyúl egy igazi jellem. Ott ül a fűben, de akkor sem szívja!

Ha mar grep, akkor akar ki is hasznalhatod, hogy regexp-et tud:


ps ax | grep '[o]penvpn'

- ebben a grap parancssorban az a szoveg, hogy "openvpn" nem szerepel (leven az o utan all egy zaro szogletes zarojel) - e miatt a grep onmagat nem talalja meg, de a regexp miatt a valodi openvpn-t tartalmazo sorokat igen.
A "mikor latszik vagy nem latszik" magyarazatahoz jobban kell tudni, hogy mikent mukodik a shell (raadasul nyilvan ahanyfele shell, annyifelekent lehet megcsinalni, bar nagy vonalakban termeszetesen valoszinuleg hasonloak), de foleg a kernel belsejet kell erteni. Es szintugy oprendszer fuggo a dolog, tegnap pl. egy HP-UX-on jatszottuk, hogy egy vegtelen ciklusban futtattunk hasonloan rosszul megirt ps / grep parancsot, es kb negyedora alatt egyszer lett meg a grep, a tobbi esetben nem latszott. Mas UNIX-like rendszerekben pedig sokkar surubben lehet belefutni. Azaz a megoldas, hogy az ilyet az ember a fentihez hasonloan korrekten kodolja le :-)

Az ok egyszeru: a kernelben allithato a pipe size (lasd 'ulimit -a' kimenete). Alaphelyzetben 4K, de ulimit-tel rogton at is allitohato. Ha a ps kimenete belefer ebbe a 4K-ba, akkor a ps mar meghal, mire a grep futni kezd. Ha nem fer bele, akkor meg lathato lesz, mivel ha megtelik a buffer, akkor a kernel felfuggeszti az ado/iro process-t, es elinditja/ujrainditja a fogyaszto process-t.

Ezek egyszeru dolgok, igazan-nagyon, csak bele kell gondolni, hogy hogyan is mukodik a kernel. Amugy ha latsz valami operacios rendszerek konyvet (nem azt, amelyik a win95-ot meseli holtkezdoknek), akkor abban olvashatsz hasonlo -amugy kurvaerdekes- megoldasokrol, mint pl. IPC, message queue, utemezes, deadlock-kezeles, stb... Erdemes elolvasni, linuxosoknak (mar a komolyabbaknak ;) alapmu egy ilyen! Javaslom a tanenbaum-felet. Megdobbentoen jo!

Én az ilyesmik elkerülése végett szoktam a ps-nek a -C opcióját használni:


fules@chaos:~$ ps -C getty
  PID TTY          TIME CMD
 1732 tty1     00:00:00 getty
 1733 tty2     00:00:00 getty
 1734 tty3     00:00:00 getty
 1735 tty4     00:00:00 getty
 1736 tty5     00:00:00 getty
 1737 tty6     00:00:00 getty
fules@chaos:~$ ps --no-heading -C getty
 1732 tty1     00:00:00 getty
 1733 tty2     00:00:00 getty
 1734 tty3     00:00:00 getty
 1735 tty4     00:00:00 getty
 1736 tty5     00:00:00 getty
 1737 tty6     00:00:00 getty
fules@chaos:~$ ps --no-heading -o '%p' -C getty
 1732
 1733
 1734
 1735
 1736
 1737