exec ?kapcsolók? arg ?arg ...?Az
exec parancs egy parancs pipeline elemeinek
alfolyamatként történő végrehajtását kezdeményezi. Az
exec argumentumai a pipeline részét képező unix
parancsokat és ezek kapcsolatát írják le. A pipeline megadásának
szintaxisa a unix-shellekben megszokotthoz hasonló.
A pipeline parancsai szavakból állnak, minden argumentum pontosan egy
szót ad meg. A parancs első szava a parancs neve. Ha a parancs neve
nem ad meg könyvtárat, vagyis a tilda-helyettesítés elvégzése után nem
tartalmaz '/' karaktert, az exec a PATH
környezeti változóban felsorolt könyvtárakban elsőként megtalált
megfelelő nevű fájlt hajtja végre.
A pipeline parancs argumentumai pontosan a parancs nevét követő szavak
lesznek. Az exec parancs ezeken semmilyen helyettesítést
nem végez. Ha mégis a shellben megszokott mintákkal szeretnénk
fájlneveket paraméterként átadni, a glob parancsot kell
alkalmaznunk. Ekkor azonban nem elég a glob parancsot
parancshelyettesítésbe bújtatni, mert eredménye az exec
és ezzel egyben a végrehajtandó parancs egyetlen argumentumává válna.
Egyik lehetséges kiút az eval parancs használata.
A vezérlőargumentumok a pipeline parancsainak elválasztására és
inputjuk és outputjuk irányítására szolgálnak, ezek nem kerülnek a
végrehajtott programoknak átadásra. A paraméteres vezérlőargumentumok
paramétere képezheti a vezérlőargumentum részét (például
'>fájlnév'), de állhat a következő argumentumban is
(például '> fájlnév').
| | | A pipeline két parancsát választja el. A megelőző parancs standard outputját a következő parancs standard inputjába irányítja. |
| |& | A pipeline két parancsát választja el. A megelőző parancs standard és error outputját a következő parancs standard inputjába irányítja. Felülbírálja a '2>' és '>&' jellegű átirányításokat. |
| <fájlnév | A megnevezett fájlt a pipeline első parancsának standard inputjába irányítja. |
| <@azonosító | Az azonosítóval adott, olvasásra is megnyitott fájlt a pipeline első parancsának standard inputjába irányítja. |
| <<érték | A megadott értéket a pipeline első parancsának standard inputjába irányítja. |
| >fájlnév | A pipeline utolsó parancsának standard outputját a megadott fájlba irányítja, felülírva annak eredeti tartalmát. |
| >@azonosító | A pipeline utolsó parancsának standard outputját az azonosítóval adott, írásra is megnyitott fájlba irányítja. |
| >>fájlnév | A pipeline utolsó parancsának standard outputját a megadott fájl végéhez fűzi. |
| 2>fájlnév | A pipeline összes parancsának error outputját a megadott fájlba irányítja, felülírva annak eredeti tartalmát. |
| 2>@azonosító | A pipeline összes parancsának error outputját az azonosítóval adott, írásra is megnyitott fájlba irányítja. |
| 2>>fájlnév | A pipeline összes parancsának error outputját a megadott fájl végéhez fűzi. |
| >&fájlnév | A pipeline utolsó parancsának standard outputját és összes parancsának error outputját a megadott fájlba irányítja, felülírva annak eredeti tartalmát. |
| >&@azonosító | A pipeline utolsó parancsának standard outputját és összes parancsának error outputját az azonosítóval adott, írásra is megnyitott fájlba irányítja. |
| >>&fájlnév | A pipeline utolsó parancsának standard outputját és összes parancsának error outputját a megadott fájl végéhez fűzi. |
| & | Utolsó argumentumként adható meg, a pipeline aszinkron végrehajtását eredményezi. |
Ha a pipeline első parancsának standard inputját nem adtuk meg külön, akkor pipeline a Tcl interpretert végrehajtó folyamat standard inputjáról fog olvasni.
Szinkron végrehajtás esetén (nem '&' argumentummal zártuk le a
parancsot) az exec parancs megvárja a pipeline
parancsainak befejeződését. Ha nem irányítottuk át, a pipeline utolsó
parancsának standard outputja lesz a parancs visszatérési értéke.
Az exec parancs hibát jelez, ha a pipeline bármely eleme
abnormálisan fejeződik be. A hibaüzenet a pipeline outputjából és az
abnormális befejeződést leíró diagnózisból áll, az errorCode változó az
utolsóként abnormálisan befejeződött paranccsal kapcsolatos
információkat tartalmaz. Az exec parancs akkor is hibát
ad vissza, ha a pipeline bármely eleme error outputjára ír, és az
nincsen átirányítva. A hibaüzenet ekkor a standard outputot és a
diagnózist követően az error outputot is megadja.
Akár normálisan, akár hibával fejeződik be a szinkron
exec parancs, törli az eredmény utolsó karakterét, ha az
újsor karakter.
Aszinkron végrehajtáskor az exec nem várja meg a pipeline
végrehajtásának befejeződését, visszatérési értéke a pipeline
elemeinek folyamat-azonosító számait tartalmazó lista. Az aszinkron
futó pipeline parancsainak standard illetve error outputja - ha nem
irányítottuk át - a Tcl interpretert futtató folyamat standard illetve
error outputjára kerül.
Az exec parancs két kapcsolót támogat:
-keepnewline --
pid ?azonosító?A
pid parancsot meghívhatjuk egy argumentummal vagy
argumentum nélkül. Az argumentum egy fájlazonosító lehet. Ha az
azonosító az open paranccsal megnyitott pipeline azonosítója, a
visszatérési érték a pipeline elemeinek folyamat-azonosító számát
tartalmazó lista. Ha az azonosító nem pipeline azonosítója, a
visszaadott lista üres.
Argumentum nélkül kiadva a pid parancs a Tcl interpretert
végrehajtó folyamat-azonosító számát adja vissza.
exit ?kód?Az
exit parancs a Tcl interpretert futtató folyamatot a
megadott visszatérési kóddal terminálja. A visszatérési kód
alapértelmezése 0.
Ez a fejezet a Tcl egyik fontos kiegészítéséről, a thread csomagról szól, amely lehetővé teszi szálak használatát egy Tcl szkriptben.
A szálkezelés rendkívül lényeges része egy programozási nyelvnek, hiszen enélkül bizonyos feladatokat csak nagyon körülményesen, vagy nem kielégítően lehet megoldani. Legegyszerűbb példa erre a grafikus kezelőfelület: egy-egy bonyolultabb folyamat elindításakor természetes elvárás, hogy amíg a gép dolgozik, a felhasználó tudjon dolgozni a programmal, vagy akár leállítsa az éppen futó folyamatot. Ehhez a programot párhuzamos folyamatokra kell bontani, amit szálakkal érhetünk el. Mivel a Tcl programozási nyelvet a Tk kiegészítés révén általában grafikus prototípusok készítésére használják, az ilyen problémák mindennaposak. Természetesen a helyzet megoldható az update paranccsal, illetve külső programok futtatásával és azok eredményeinek felhasználásával is, de egy ilyen gyakorta felmerülő kérdésre elegánsabb választ igényel a programozó világ - a szálakat.
A szálak használatára nincs felkészítve minden Tcl interpreter; fordításkor ezt általában egy parancssori argumentummal (vagy egy #define-al a Makefile-ban) lehet megkövetelni. Ha az interpreter vagy egy betöltött csomag nem szálbiztos, az könnyen a program hibás futásához illetve szegmentációs hibához vezethet.
A csomag letölthető a Tcl/Tk honlapjáról, betöltéséhez a forrásban a következő sornak kell szerepelnie:
package require Thread 2.1ahol a "2.1" természetesen a megkövetelt verziószámnak megfelelően változtatandó.
A Tcl szálmodellje szerint minden szálhoz tartozik legalább egy Tcl interpreter, de minden egyes interpretert csak egy-egy szál használhat. Ennek az osztott adatkezelés megvalósításánál van jelentősége - a szálak biztonságos használata érdekében az adatokat vagy a szálakban lokálisan kell definiálni, vagy mutexekkel kell megvédeni (bővebben erről később lesz szó, a szálbiztosság kapcsán). Következésképp a Tcl inkább az olyan egyszerűbb szálas feladatokra alkalmas, amelyek munkáltató - dolgozó alapúak, azaz van egy fő szál, ami több mellékfolyamatot elindít és kommunikál velük, illetve vár, hogy azok válaszoljanak. A szálak kommunikációja az interpreterek eseménysorán (event queue) keresztül történik, ez lehet szimmetrikus vagy aszimmetrikus. Szimmetrikus esetben a hívó fél megvárja, hogy válasz érkezzen, aszimmetrikus esetben rögtön továbbmegy. A 8.4-es verziótól kezdve már I/O csatornákat is át lehet adni szálak közt.
sajat_hibakezelo szal_id hiba_adatok
Ha egy Tcl szkriptben szálakat használunk, akkor minden, a szálakban használt utasításnak szálbiztosnak kell lennie, ellenkező esetben a program futása nem lesz biztonságos. A Tcl szkriptek prototípus-jellegük miatt legtöbbször C-ben írt saját könyvtárakat is betöltenek - ahhoz, hogy ezek szálbiztosak legyenek, használni kell a Thread kiegészítés C API-jait. Ehhez használhatóak mutexek, szinkronizálást elősegítő feltételváltozók (condition variables), valamint a szálakra nézve lokális adattárolás. Ezek a struktúrák mind automatikusan törlődnek, amikor a szál terminál, így nem kell velük külön törődni. Ha a lokális adatterületben kell felszabadítani memóriát, akkor ez egy szálspecifikus kilépéskezelővel (exit handler) tehető meg.
Egy régi forráskód szálbiztossá tételénél általában elég a globális adatstruktúrákra figyelni, és vagy mutexekkel korlátozni az ezekhez való hozzáférést, vagy a szálakra nézve lokális adatterületre átvinni őket.
TCL_DECLARE_MUTEX(mutexValtozo) (...) Tcl_MutexLock(&mutexValtozo); (műveletek a közös adatokkal...) Tcl_MutexUnlock(&mutexValtozo);
Tcl_MutexLock(&mutexValtozo);
while (a_feltétel_hamis) {
Tcl_ConditionWait(&feltetelValtozo, &mutexValtozo, NULL /* nincs időkorlát */);
}
Tcl_MutexUnlock(&mutexValtozo);
Tcl_MutexLock(&mutexValtozo); /* közös/osztott állapot beállítása */ Tcl_ConditionNotify(&feltetelValtozo); Tcl_MutexUnlock(&mutexValtozo);
Néhány szálbiztos könyvtár a Tcl-hez:
Egy egyszerű példa párhuzamosításra:
package require Thread 2.1
#
# Új szál indítása. A visszatérési értéket (a szál azonosítóját)
# berakjuk a threadID változóba, hogy később hivatkozni tudjunk rá.
#
set threadID [thread::create {
#
# Innentől kezdve a thread::wait utasításig (amivel a szál belép az
# eseményciklusba) definiáljuk azokat az eljárásokat, amiket majd a
# főprogram meg fog hívni.
#
# Ebben a példában egy egyszerű kis eljárás van csak, ami lefuttatja
# a parancsot amit átadtak neki. Ez a parancs lehet akár egy hosszan
# futó Tcl utasítás, vagy éppen egy külső program is.
#
# Az eredményt visszaküldi az ID azonosítóval meghatározott szálnak,
# a globális printResult eljárás segítségével.
#
# A thread::wait utasításra azért van szükség, hogy a szál ne fejezze
# be a működését és lépjen ki, hanem maradjon benn az eseményciklusban.
#
proc runCommand { ID command } {
set result [eval $command]
eval [subst {thread::send -async $ID \
{::printResult [list $result]}}]
}
thread::wait
}]
#
# Ez az eljárás hívódik meg, amikor a szál befejezte a parancs futtatását.
#
proc printResult { result } {
puts $result
exit
}
#
# Kiírja az aktuális időt és beütemezi önmagát egy másodperc múlvára.
#
proc passTheTime { } {
puts [clock format [clock seconds]]
after 1000 passTheTime
}
#
# Ez itt egy általános utasítás külső program futtatására, csak az
# exec utáni részt kell lecserélni valami másra.
#
set commandString "exec du -sk /usr/local"
eval [subst {thread::send -async $threadID \
{runCommand 1 [list $commandString ]}}]
#
# Maga a program csak kiírogatja az időt és vár az örökkévalóságig.
# Ezzel megmutatjuk, hogy miközben a szál dolgozik, a főprogram él.
#
passTheTime
vwait forever