Multi-threaded Bash-scripting en procesbeheer op de opdrachtregel

  • Michael Arnold
  • 0
  • 4067
  • 1112
>

De dingen die u kunt doen met het Bash-script zijn grenzeloos. Zodra u begint met het ontwikkelen van geavanceerde scripts, zult u al snel merken dat u de limieten van het besturingssysteem tegenkomt. Heeft uw computer bijvoorbeeld 2 CPU-threads of meer (veel moderne machines hebben 8-32 threads)? Als dat het geval is, zult u waarschijnlijk profiteren van multi-threaded Bash-scripting en codering. Lees verder en ontdek waarom!

In deze tutorial leer je:

  • Hoe multi-threaded Bash-oneliners rechtstreeks vanaf de opdrachtregel te implementeren
  • Waarom multi-threaded codering bijna altijd de prestaties van uw scripts kan en zal verbeteren
  • Hoe achtergrond- en voorgrondprocessen werken en hoe wachtrijen kunnen worden gemanipuleerd
Multi-threaded Bash-scripting en procesbeheer

Gebruikte softwarevereisten en -conventies

Softwarevereisten en Linux Command Line-conventies
Categorie Vereisten, conventies of gebruikte softwareversie
Systeem Distributie-onafhankelijk, Bash-versie-afhankelijk
Software Bash-opdrachtregelinterface (bash)
Conventies # - vereist dat gegeven linux-commando's worden uitgevoerd met root-privileges, hetzij direct als rootgebruiker, hetzij door gebruik van sudo opdracht
$ - vereist dat gegeven linux-commando's worden uitgevoerd als een gewone niet-geprivilegieerde gebruiker

Wanneer je een Bash-script uitvoert, gebruikt het maximaal een enkele CPU-thread, tenzij je subshells / threads start. Als uw machine ten minste twee CPU-threads heeft, kunt u CPU-bronnen maximaal benutten met behulp van multi-threaded scripting in Bash. De reden hiervoor is simpel; zodra een secundaire 'thread' (lees: subshell) wordt gestart, dan kan (en zal) die volgende thread vaak een andere CPU-thread gebruiken.

Stel je voor dat je een moderne machine hebt met 8 of meer draden. Kun je beginnen te zien hoe als we in staat zouden zijn om code uit te voeren - acht parallelle threads allemaal tegelijk, elk draaiend op een andere CPU-thread (of gedeeld over alle threads) - op deze manier zou het veel sneller worden uitgevoerd dan een single-threaded proces draait op een enkele CPU-thread (die kan worden gedeeld met andere actieve processen)? De gerealiseerde winsten zullen een beetje afhangen van wat er wordt uitgevoerd, maar winsten zullen er bijna altijd zijn!

Opgewonden? Super goed. Laten we erin duiken.

Eerst moeten we begrijpen wat een subshell is, hoe deze wordt gestart, waarom u er een zou gebruiken en hoe deze kan worden gebruikt om multi-threaded Bash-code te implementeren.

Een subshell is een ander Bash-clientproces dat wordt uitgevoerd / gestart vanuit het huidige. Laten we iets eenvoudigs doen en er een starten vanuit een geopende Bash-terminalprompt:

$ bash $ exit exit $ 

Wat is hier gebeurd? Eerst begonnen we met een andere Bash-shell (bash) die begon en op zijn beurt een opdrachtprompt opleverde ($). Dus de tweede $ in het bovenstaande voorbeeld is eigenlijk een andere Bash-shell, met een andere PID (PID is de proces-ID; een unieke nummeridentificatie die elk lopend proces in een besturingssysteem op unieke wijze identificeert). Eindelijk verlieten we de subshell via Uitgang en keerde terug naar de bovenliggende subshell! Kunnen we op de een of andere manier bewijzen dat dit echt is wat er is gebeurd? Ja:

$ echo $$ 220250 $ bash $ echo $$ 222629 $ exit exit $ echo $$ 220250 $ 

Er is een speciale variabele in bash $$, die de PID van de huidige shell die in gebruik is. Kun je zien hoe de proces-ID veranderde toen we ons in een subshell bevonden??

Super goed! Nu we weten wat subshells zijn, en een beetje over hoe ze werken, laten we een paar voorbeelden van multi-threaded codering bekijken en meer leren!

Eenvoudig multi-threading in Bash

Laten we beginnen met een eenvoudig one-liner multi-threaded voorbeeld, waarvan de uitvoer er in eerste instantie wat verwarrend uitziet:

$ voor i in $ (seq 1 2); echo $ i; gedaan 1 2 $ voor i in $ (seq 1 2); do echo $ i & done [1] 223561 1 [2] 223562 $ 2 [1] - Klaar echo $ i [2] + Klaar echo $ i $ 

In de eerste voor loop (zie ons artikel over Bash-loops voor meer informatie over het coderen van loops), we voeren gewoon de variabele uit $ i die zal variëren van 1 tot 2 (vanwege ons gebruik van de seq-opdracht), die - interessant genoeg - wordt gestart in een subshell!

NOTITIE
U kunt de $ (...) syntaxis overal binnen een opdrachtregel om een ​​subshell te starten: het is een zeer krachtige en veelzijdige manier om subshells rechtstreeks in andere opdrachtregels te coderen!

In de seconde voor loop hebben we slechts één teken gewijzigd. In plaats van gebruiken ; - een EOL (einde van de regel) Bash-syntaxis idioom die een bepaald commando beëindigt (je zou erover kunnen denken als Enter / Execute / Go ahead), we gebruikten &. Deze simpele wijziging zorgt voor een bijna compleet ander programma, en onze code is nu multi-threaded! Beide echo's zullen min of meer tegelijkertijd verwerken, met een kleine vertraging in het besturingssysteem die nog steeds de tweede loop run moet uitvoeren (om '2' te echoën).

U kunt erover nadenken & op een vergelijkbare manier als ; met het verschil dat & zal het besturingssysteem vertellen 'blijf de volgende opdracht uitvoeren, blijf de code verwerken', terwijl ; wacht op het huidige uitvoerende commando (beëindigd door ;) om te beëindigen / voltooien voordat u terugkeert naar de opdrachtprompt / voordat u doorgaat met het verwerken en uitvoeren van de volgende code.

Laten we nu de output bekijken. Wij zien:

[1] 223561 1 [2] 223562 $ 2 

Eerst gevolgd door:

[1] - Klaar echo $ i [2] + Klaar echo $ i $ 

En er is ook een lege regel tussenin, wat het resultaat is van achtergrondprocessen die nog steeds actief zijn terwijl u wacht op de volgende opdrachtinvoer (probeer deze opdracht een paar keer op de opdrachtregel, evenals enkele lichte variaties, en u krijgt een voel hoe dit werkt).

De eerste uitvoer ([1] 223561) laat zien dat er een achtergrondproces is gestart, met PID 223561 en het identificatienummer 1 werd eraan gegeven. Dan, al voordat het script de tweede echo bereikte (een echo is waarschijnlijk een dure code om uit te voeren), de output 1 werd getoond.

Ons achtergrondproces is niet volledig voltooid omdat de volgende uitvoer aangeeft dat we een tweede subshell / thread zijn gestart (zoals aangegeven door [2]) met PID 223562. Vervolgens voert het tweede proces het 2 ("Indicatief": OS-mechanismen kunnen hier invloed op hebben) voordat de tweede thread is voltooid.

Ten slotte zien we in het tweede uitvoerblok de twee processen eindigen (zoals aangegeven door Gedaan), evenals wat ze het laatst hebben uitgevoerd (zoals aangegeven door echo $ i). Merk op dat dezelfde nummers 1 en 2 worden gebruikt om de achtergrondprocessen aan te duiden.

Meer multi-threading in Bash

Laten we vervolgens drie slaapopdrachten uitvoeren, allemaal beëindigd door & (dus ze beginnen als achtergrondprocessen), en laten we hun slaapduur variëren, zodat we duidelijker kunnen zien hoe achtergrondverwerking werkt.

$ slaap 10 & slaap 1 & slaap 5 & [1] 7129 [2] 7130 [3] 7131 $ [2] - Klaar met slapen 1 $ [3] + Klaar met slapen 5 $ [1] + Klaar met slapen 10 

De output in dit geval zou voor zichzelf moeten spreken. De opdrachtregel keert onmiddellijk terug na onze slaap 10 & slaap 1 & slaap 5 & commando en 3 achtergrondprocessen, met hun respectievelijke PID's worden getoond. Ik heb tussendoor een paar keer op enter gedrukt. Na 1 seconde voltooide het eerste commando dat het Gedaan voor proces-ID [2]. Vervolgens werd het derde en eerste proces beëindigd, afhankelijk van hun respectievelijke slaapduur. Merk ook op dat dit voorbeeld duidelijk laat zien dat meerdere taken effectief tegelijkertijd op de achtergrond worden uitgevoerd.

Mogelijk hebt u ook het + teken in de uitvoervoorbeelden hierboven. Dit gaat allemaal over taakcontrole. We zullen in het volgende voorbeeld kijken naar job control, maar voorlopig is het belangrijk om dat te begrijpen + geeft aan dat de taak zal worden bestuurd als we taakbesturingsopdrachten zouden gebruiken / uitvoeren. Het is altijd de taak die het laatst aan de lijst met lopende taken is toegevoegd. Dit is de standaardtaak, die altijd de laatste is die aan de lijst met taken is toegevoegd.

EEN - geeft de taak aan die de volgende standaard voor taakbesturingsopdrachten zou worden als de huidige taak (de taak met de + teken) zou eindigen. Taakcontrole (of met andere woorden: het afhandelen van achtergrondthreads) klinkt in het begin misschien een beetje ontmoedigend, maar het is eigenlijk heel handig en gemakkelijk te gebruiken als je er eenmaal aan gewend bent. Laten we erin duiken!

Taakbeheer in Bash

$ sleep 10 & sleep 5 & [1] 7468 [2] 7469 $ jobs [1] - Running sleep 10 & [2] + Running sleep 5 & $ fg 2 sleep 5 $ fg 1 sleep 10 $ 

Hier hebben we twee slaapplaatsen op de achtergrond geplaatst. Nadat ze waren gestart, hebben we de momenteel lopende taken onderzocht met behulp van de banen opdracht. Vervolgens werd de tweede draad op de voorgrond geplaatst met behulp van de fg commando gevolgd door het jobnummer. Je kunt er zo over nadenken; de & in de slaap 5 commando werd omgezet in een ;. Met andere woorden, een achtergrondproces (waarop niet werd gewacht) werd een voorgrondproces.

We wachtten toen op de slaap 5 opdracht om af te ronden en vervolgens de slaap 10 commando op de voorgrond. Merk op dat elke keer dat we dit deden, we moesten wachten tot het voorgrondproces was voltooid voordat we onze opdrachtregel terug zouden ontvangen, wat niet het geval is wanneer we alleen achtergrondprocessen gebruiken (omdat ze letterlijk 'op de achtergrond draaien').

Taakcontrole in Bash: taakonderbreking

$ sleep 10 ^ Z [1] + Gestopte slaap 10 $ bg 1 [1] + slaap 10 & $ fg 1 slaap 10 $ 

Hier drukken we op CTRL + z om een ​​lopende slaap 10 te onderbreken (die stopt zoals aangegeven door Gestopt). Vervolgens plaatsen we het proces op de achtergrond en uiteindelijk plaatsen we het op de voorgrond en wachten tot het is voltooid.

Taakcontrole in Bash: taakonderbreking

$ sleep 100 ^ Z [1] + Gestopte slaap 100 $ kill% 1 $ [1] + Beëindigde slaap 100 

Een 100 seconden begonnen zijn slaap, we onderbreken vervolgens het lopende proces met CTRL + z, en beëindigen dan het eerste gestarte / actieve achtergrondproces door de doden opdracht. Let op hoe we gebruiken % 1 in dit geval in plaats van simpelweg 1. Dit komt omdat we nu werken met een hulpprogramma dat niet standaard is gekoppeld aan achtergrondprocessen, zoals fg en bg zijn. Dus om aan te geven dat we het eerste achtergrondproces willen uitvoeren, gebruiken we % gevolgd door het achtergrondprocesnummer.

Taakbeheer in Bash: proces verstoten

$ sleep 100 ^ Z [1] + Gestopte slaap 100 $ bg% 1 [1] + slaap 100 & $ disown 

In dit laatste voorbeeld beëindigen we opnieuw een running slaap, en plaats het op de achtergrond. Ten slotte voeren we het verloochenen commando dat je kunt lezen als: ontkoppel alle achtergrondprocessen (jobs) van de huidige shell. Ze blijven draaien, maar zijn niet langer 'eigendom' van de huidige shell. Zelfs als u uw huidige shell sluit en uitlogt, blijven deze processen actief totdat ze op natuurlijke wijze worden beëindigd.

Dit is een zeer krachtige manier om een ​​proces te onderbreken, het op de achtergrond te plaatsen, het af te wijzen en vervolgens uit te loggen van de machine die u gebruikte, op voorwaarde dat u niet meer met het proces hoeft te werken. Ideaal voor die langlopende processen via SSH die niet kunnen worden onderbroken. CTRL + z gewoon het proces (waardoor het tijdelijk wordt onderbroken), plaats het op de achtergrond, negeer alle taken en log uit! Ga naar huis en beleef een fijne, ontspannen avond in de wetenschap dat je baan blijft draaien!

Multi-threaded Bash-scripting en procesbeheer opdrachtregelvoorbeelden

Gevolgtrekking

In deze tutorial hebben we gezien hoe je multi-threaded Bash-one-liners rechtstreeks vanaf de opdrachtregel kunt implementeren, en hebben we onderzocht waarom multi-threaded codering vaak de prestaties van je scripts verhoogt. We onderzochten ook hoe achtergrond- en voorgrondprocessen werken, en we manipuleerden wachtrijen. Ten slotte hebben we onderzocht hoe we onze taakwachtrij uit het huidige proces kunnen verwijderen, waardoor we extra controle hebben over lopende processen. Geniet van je nieuw gevonden vaardigheden en laat hieronder een opmerking achter met je ervaringen op het gebied van jobcontrole!




Niemand heeft nog op dit artikel gereageerd.