Laborator 06 - Procese

Obiectivele laboratorului

  • Investigarea proceselor în Linux
  • Simularea proceselor pe Micro:bit

Instalarea mașinii virtuale de laborator

În acest laborator vom folosi o mașină virtuală cu interfață grafică pentru a putea rula mai multe terminale.

La începutul laboratorului descărcați mașina virtuală și importați fișierul .ova în VirtualBox.

În timp ce fișierul .ova se descarcă, rezolvați exercițiile pentru Micro:bit.

MakeCode Micro: bit

Pentru a simula procesele BBC Micro: bit vom scrie cod în MakeCode.

Editorul suportă atât programarea dispozitivului fizic, cât și a unui simulator. Cei care nu aveți dispozitivul, puteți folosi simulatorul implicit din editor.

Funcții utile

Basic
  • basic.show_number(value) - rulează pe ecran numărul dat ca și parametru; value poate fi si o variabilă, care stochează un număr
  • basic.show_string(value) - rulează pe ecran string-ul dat ca și parametru
  • basic.show_icon(IconNames.nameIcon) - afișează pe ecran icon-ul dat ca și parametru (icon-urile sunt ecchivalentul imaginilor utlizate în laboratoarele anterioare)
  • basic.show_leds(leds: str, interval: int32) - leds este un string care controlează ce led este pornit sau oprit; . înseamnă oprit, iar # însemna oprit
  • basic.clear_screen() - setează toate LED-urile la luminozitate 0
  • basic.pause(value) - este echivalentul metodei sleep; value este exprimat în milisecunde
Input
  • input.on_button_pressed(Button.B, on_button_pressed_b) - verifică dacă butonul este apăsat
  • input.on_gesture(gesture: Gesture, body) - înregistrează gesturile; gesture poate lua valoarea logo up, logo down, screen up, screen down, tilt left, tilt right, free fall, 3g, sau 6g.
  • input.temperature() - măsoară temperatura din încapere
  • input.light_level() - măsoară nivelul de luminozitate din încapere
LED
  • led.toggle(x: number, y: number) - aduce LED-ul în starea opusă (dacă LED-ul este pornit, îl oprește, iar dacă este oprit, îl pornește); x și y sunt coordonatele matricei de 25 de LED-uri, pot lua valori între 0 și 4
  • led.brightness() - returnează un număr care reărezintă jivelul de luminozitate al ecranului de LED-uri, poate avea valori între 0 și 225
Music
  • music.play_tone(frequency: number, ms: number) - frequency reprezintă numarul de Hertzi ai tonului, iar ms durata în milisecunde a sunetului

Control

Modulul control expune funcții de permit rularea de task-uri în paralel și interacțiunea între acestea.

control.in_background()

Funcția in_background(fun) primește ca parametru o funcție și o rulează în același timp cu programul principal.

Exemplul 1

Rulați aplicația de mai jos și observați output-ul obținut. Când rulează funcția din background? De ce?

def background_task():
    for i in range(10):
        print (i)
control.in_background(background_task)
for i in range (20,30)
    print (i)

Pentru că operațiile nu se execută efectiv odată, dacă nu folosim funcția pause nu putem observa că acestea ar rula în paralel.

Exercițiul 1

Modificați programul pentru a rula funcția background_task.

Exemplul 2

La apăsarea butonului a pe display-ul plăcuței se va afișa temperatura, iar pentru ca vrem sa simulăm două procese, în background va rula un sunet. Pentru a testa exemplul de mai jos faceți click aici.

def on_in_background(): 
    while True:
        music.play_tone(Note.C, music.beat(BeatFraction.QUARTER))
control.in_background(on_in_background)
 
def on_button_pressed_a():
    temp = input.temperature()
    basic.show_number(temp)
    basic.pause(100)
input.on_button_pressed(Button.A, on_button_pressed_a)

Comunicarea între task-uri

Pentru a realiza comunicarea între programul principal și task-ul din background, avem la dispoziție două funcții:

  • control.raise_event(source, value) - emite un eveniment de la sursă (source-valoare întreagă) cu valoarea value
  • control.event_value() - întoarce valoarea ultimului eveniment emis

Exemplul 3

Următorul program generează un task care emite valoarea 2.

def background_task():
    control.raise_event(1,2)
control.in_background(background_task)
pause (1000)
print (control.event_value())

Exerciții

  1. Creați un program care face un LED să pâlpâie o dată la o secundă în background, iar în programul principal afișează nivelul luminii o dată la 5 secunde.
  2. Creați un program care să facă două led-uri să pâlpâie concomitent folosind task-uri în background.
  3. Creați un program care citește valoarea luminii o dată la 2 secunde în background și o afișează în programul principal.
  4. Citiți despre funcția control.on_event() și implementați exercitiul anterior folosind această funcție.
  5. Folosiți funcția control.on_event() pentru a realiza un program care aprinde LED-ul (0,0) când temperatura depășeste 15 grade celsius și aprinde LED-ul (4,4) când lumina depășeste un anumit prag setat de voi.
  6. Creați un program în care LED-ul (0,0) pâlpâie o dată la o secundă până la apăsarea unui buton folosind task-uri in background.
  7. Creati un program care foloseste evenimente pentru a afisa temperatura de fiecare data cand se apasa butonul a si lumina de fiecare data cand se apasa butonul b.

Procese în Linux

Următoarele exemple și exerciții se vor rezolva în mașina virtuală de Linux, NU pe Micro:bit.

Utilizare terminal

Pentru a deschide un terminal nou:

Scurtătură Descriere
Ctrl+Alt+t pentru a deschide o nouă fereastră de terminal

Puteți folosi următoarele scurtături în terminal:

Scurtătură Descriere
Ctrl+Shift+t pentru a deschide un nou tab
Ctrl+PageDown pentru a merge la tab-ul următor
Ctrl+PageUp pentru a merge la tab-ul precedent
Alt+<index> pentru a sări direct la un tab
Ctrl+d pentru a închide un tab (sau comanda exit)

Pentru a naviga (scrolling) în cadrul unui terminal, mai ales atunci când o comandă afișează mult text, folosiți următoarele scurtături:

Scurtătură Descriere
Shift+PgDown pentru a derula în jos
Shift+PgUp pentru a derula în sus

Alte scurtături utile:

Scurtătură Descriere
Ctrl+Shift+c copiere text din terminal
Ctrl+Shift+v lipire text în terminal
Shift+Insert lipire text în terminal

Procese

Pornirea unei aplicații înseamnă că se alocă resursele sistemului (procesor, memorie, dispozitive de intrare/ieșire) pentru a rula aplicația. O aplicație care rulează, adică folosește resursele sistemului pentru a executa cod și a prelucra date, se numește proces. Atunci când pornim o aplicație, se creează un proces; atunci când oprim aplicația, sau când își încheie execuția, ne referim la încheierea execuției procesului.

Procesul este pornit dintr-un fișier executabil care conține codul (instrucțiunile) și datele aplicației. Fișierul executabil mai este numit și imaginea procesului. Fișierul executabil este un program. Spunem că procesul este un program aflat în execuție.

Investigarea proceselor

Un sistem de operare are de obicei mai multe aplicații care rulează, deci mai multe procese. Prea multe procese pot duce la o încărcare prea mare a sistemului, încetinind sau împiedicând funcționarea acestuia. Anumite procese pot consuma excesiv resurse afectând celelalte procese. De aceea, este util să investigăm procesele unui sistem și consumul de resurse al acestora.

Listarea proceselor

La nivel mai degrabă didactic, putem vizualiza lista de procese a unui sistem. Utilitarul ps afișează procesele curente în sistem (un snapshot al proceselor sistemului). La o rulare simplă, utilitarul ps afișează procesele din terminalul curent:

student@sde:~$ ps
  PID TTY          TIME CMD
14897 pts/4    00:00:00 bash
14910 pts/4    00:00:00 ps

În terminalul curent (indicat de coloana TTY din afișare, adică terminalul pts/4) sunt două procese:

  1. procesul shell (bash) în care rulăm comenzi care creează noi procese;
  2. procesul de listare (ps) pe care tocmai l-am lansat prin comanda ps; practic se afișează pe sine

Pentru a afișa toate procesele sistemului folosim opțiunea -e (pentru everything) a utilitarului ps ca în comanda de mai jos:

student@sde:~$ ps -e
  PID TTY          TIME CMD
    1 ?        00:00:19 systemd
    2 ?        00:00:00 kthreadd
    4 ?        00:00:00 kworker/0:0H
    6 ?        00:00:00 mm_percpu_wq
    7 ?        00:00:09 ksoftirqd/0
    8 ?        00:00:06 rcu_sched
    9 ?        00:00:00 rcu_bh
   10 ?        00:00:00 migration/0
   11 ?        00:00:00 watchdog/0
[...]

Ierarhia proceselor

Un proces este creat de un alt proces. De exemplu, mai sus, procesul ps a fost creat dintr-un proces shell (bash). Procesul shell a fost, la rândul său, creat de un alt proces. Un proces are un proces părinte; un proces poate avea mai multe procese copil. Procesele sunt, așadar, parte dintr-o ierarhie.

Pentru a vizualiza ierarhia de procese, folosim utilitarul pstree:

student@uso:~$ pstree
systemd-+-ModemManager---2*[{ModemManager}]
        |-NetworkManager-+-2*[dhclient]
        |                `-2*[{NetworkManager}]
        [...]
        |-acpid
        |-avahi-daemon---avahi-daemon
        |-boltd---2*[{boltd}]
        |-colord---2*[{colord}]
        |-cron
        [...]
        |-systemd-+-(sd-pam)
        |         |-gnome-terminal--+-bash
        |         |                 `-3*[{gnome-terminal-}]
        [...]

În vârful ierarhiei de procese este procesul numit clasic init. În listarea de mai sus vedem că procesul din vârful ierarhiei este systemd. systemd este implementarea de init prezentă în cea mai mare parte a distribuțiilor Linux curente.

Atributele proceselor

Utilitarul ps are o afișare tabelară a proceselor, fiecare coloană corespunzând unui atribut al proceselor. La o rulare simplă, așa cum am văzut mai sus sunt afișate patru coloane:

  • PID: reprezentând identificatorul procesului
  • TTY: terminalul în care rulează procesul (apare ? pentru un proces care nu are terminal - în general procesele de tip serviciu, numite și procese daemon nu au terminal)
  • TIME: timpul de rulare pe procesor (în ore, minute, secunde)
  • CMD: numele imaginii de proces (adică numele executabilului / programului din care a fost creat procesul)
Identificarea unui proces

PID (Process Id) este atributul esențial al procesului, un index care identifică procesul la nivelul sistemului. Un proces este identificat după PID, nu după numele executabilului (CMD). Putem avea mai multe procese create din același executabil, fiecare proces având PID-ul său.

Pentru a verifica existența mai multor procese, o să creăm mai multe procese shell. Pentru început, deschidem mai multe sesiuni de terminal, folosind, de exemplu, Alt+F2 în mediul grafic și introducând comanda gnome-terminal în promptul creat. Apoi vizualizăm doar procesele create din executabilul bash rulând comanda:

student@sde:~$ ps -e | grep bash
 2181 pts/1    00:00:00 bash
 2194 pts/2    00:00:00 bash
 2205 pts/3    00:00:00 bash
14750 pts/0    00:00:00 bash
14897 pts/4    00:00:00 bash

Obținem un rezultat precum cel de mai sus. Sunt cinci procese, toate create din executabilul bash, cu cinci PID-uri diferite: 2181, 2194, 2205, 14705, 14879.

Afișarea atributelor unui proces

Un proces are mai mult decât cele patru atribute afișate la o rulare simplă a utilitarului ps. Pentru a afișa mai multe atribute, folosim opțiunea -f (de la full format) sau opțiunea -F (de la extra full format), ca mai jos:

student@uso:~$ ps -f
UID        PID  PPID  C STIME TTY          TIME CMD
student  14897 14896  0 17:12 pts/4    00:00:00 -bash
student  15026 14897  0 17:46 pts/4    00:00:00 ps -f
 
student@uso:~$ ps -F
UID        PID  PPID  C    SZ   RSS PSR STIME TTY          TIME CMD
student  14897 14896  0  6056  5136   0 17:12 pts/4    00:00:00 -bash
student  15027 14897  0  9728  3340   0 17:46 pts/4    00:00:00 ps -F

Desigur, putem să combinăm aceste opțiuni cu opțiunea -e de afișare a tuturor proceselor:

student@uso:~$ ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 Aug18 ?        00:00:19 /lib/systemd/systemd --system --deserialize 39
root         2     0  0 Aug18 ?        00:00:00 [kthreadd]
root         4     2  0 Aug18 ?        00:00:00 [kworker/0:0H]
root         6     2  0 Aug18 ?        00:00:00 [mm_percpu_wq]
root         7     2  0 Aug18 ?        00:00:09 [ksoftirqd/0]
root         8     2  0 Aug18 ?        00:00:06 [rcu_sched]
root         9     2  0 Aug18 ?        00:00:00 [rcu_bh]
root        10     2  0 Aug18 ?        00:00:00 [migration/0]
root        11     2  0 Aug18 ?        00:00:00 [watchdog/0]
[...]

Opțiunile -f și -F afișează și alte atribute ale procesului, precum:

  • UID: numele utilizatorului care deține procesul
  • PPID: identificatorul procesului părinte
  • C: procentul de procesor ocupat
  • STIME: timpul de pornire (start time)
  • RSS: memoria RAM ocupată (resident set size)

Astfel de atribute sunt utile pentru a vedea care sunt procesele cele mai consumatoare de resurse (de exemplu procesor sau memorie).

Monitorizarea proceselor

Utilitarul ps afișează procesele și atributele acestora la un moment dat, un snapshot al proceselor sistemului. De multe ori ne interesează și evoluția în timp a proceselor: schimbarea în consumul de resurse, apariția de noi procese. Adică să monitorizăm procesele. Monitorizarea proceselor înseamnă obținerea periodică de informații despre procese.

Evident, un mod simplu de monitorizare este să rulăm utilitarul ps periodic. Există, însă, utilitare dedicate pentru monitorizare.

Monitorizarea folosind top

Utilitarul top este utilitarul de bază de monitorizare a proceselor în lumea Linux. Este echivalent Task Manager din Windows. Rularea top duce la afișarea, în terminal, a proceselor sistemului și reîmprospătarea informației periodic (implicit 2 secunde). Imaginea de mai jos este o fereastră de terminal cu rularea top:

top afișează informații periodice despre procese și despre starea sistemului: consum de procesor, de memorie. La fiecare perioadă (implicit 2 secunde) informația afișată este reîmprospătată.

La fel ca în cazul utilitarului less, ieșirea din utilitarul top se realizează folosind tasta q.

Utilitarul htop

Un utilitar similar top ceva mai prietenos este utilitarul htop8). La fel ca în cazul top, utilitarul htop rulează în linia de comandă prin introducerea comenzii htop și pornește, în terminal, o fereastră interactivă, ca în imaginea de mai jos:

htop este, de asemenea, un utilitar interactiv, un sumar al comenzilor ce pot fi folosite fiind prezentat în bara de jos a ferestrei sale. De exemplu, așa cum vedem și în imaginea de mai sus, cu ajutorul tastei F6 putem alege un atribut după care să sortăm procesele.

Oprirea proceselor. Semnale

Odată pornit, un proces rulează și consumă resursele sistemului. După ce execută codul din executabilul corespunzător, procesul își încheie execuția și eliberează resursele consumate. Dar anumite programe (de exemplu serverele) nu au un punct de oprire, ci rulează într-o buclă, teoretic la infinit. La fel, anumite programe (de exemplu un browser web) sunt interactive și își încheie execuția doar la acțiunea explicită a utilizatorului.

Deosebim astfel între următoarele tipuri de oprire a unui proces:

  1. Procesul ajunge la sfârșitul codului programului și își încheie execuția.
  2. Un comportament neașteptat sau o eroare în funcționarea programului cauzează încheierea execuției acestuia (crash).
  3. Utilizatorul execută o acțiune interactivă care trimite comanda de încheiere a execuției procesului: de exemplu folosirea tastei q pentru a încheia un proces top.
  4. Utilizatorul sau sistemul de operare decide că un proces nu rulează corespunzător și decide terminarea acestuia.

Ultimul punct din pasul de mai sus, numit și terminarea unui proces (sau, informal, omorârea unui proces) este realizat, în Linux, prin folosirea semnalelor.

Folosirea semnalelor pentru omorârea proceselor

Ca să terminăm forțat (omorâm) un proces folosim semnale. Un semnal este o notificare trimisă de utilizator sau de sistemul de operare către un proces. Nu este obligatoriu ca un semnal să omoare procesul care îl primește, dar este cel mai des întâlnit comportament, și principala utilizare a semnalelor.

Ca să trimitem un semnal unui proces trebuie să știm PID-ul acestuia și folosim utilitarul kill urmat de PID-ul procesului. Adică, dacă pornim într-un terminal un proces sleep folosind comanda de mai jos:

student@uso:~$ sleep 60

în alt terminal vom afla PID-ul său (folosind pidof):

student@uso:~$ pidof sleep
9486

și apoi îl vom omorî (folosind kill):

student@uso:~$ kill 9486

Comanda kill primește ca argument PID-ul procesului de omorât, adică 9486.

Verificăm din nou dacă există un proces sleep folosind pidof:

student@uso:~$ pidof sleep
student@uso:~$

Vedem din output că nu mai există procesul sleep, deci a fost omorât.

În terminalul inițial, în care am rulat comanda sleep, apare un mesaj care indică omorârea procesului:

student@uso:~$ sleep 60
Terminated
Folosirea semnalului SIGKILL

În anumite situații, folosirea utilitarului kill nu duce la omorârea procesului țintă. În această situație, vom transmite procesului țintă semnalul SIGKILL care este garantat că va omorî procesul. Adică, amuzant spus, SIGKILL este o bombă nucleară, un glonț care trece prin vesta anti-glonț, cianură de potasiu. Astfel, dacă pornim pe un terminal un proces sleep la fel ca mai sus, în alt terminal vom omorî procesul folosind semnalul SIGKILL ca mai jos:

student@uso:~$ pidof sleep
9834
student@uso:~$ kill -KILL 9834
student@uso:~$ pidof sleep
student@uso:~$

Secvența de comenzi este similară secvenței anterioare cu excepția folosirii opțiunii -KILL la comanda kill care înseamnă trimiterea semnalului SIGKILL.

Efectul este similar dar, pe terminalul în care am rulat comanda sleep, apare un mesaj de forma:

student@uso:~$ sleep 60
Killed

Mesajul Killed este afișat atunci când un proces primește semnalul SIGKILL.

Exerciții

  1. Afișați toate procesele din sistem care să conțină date legate de PID, PID-ul părintelui, user-ul care deține procesul și comanda care a pornit procesul
  2. Filtrați procesele după utilizatorul curent (student)
  3. Afișați procesele în mod interactiv (folosind top/htop)
Oprirea proceselor
  • Porniți în trei terminale diferite trei procese sleep. Omorâți-le pe toate cu o singură comandă. PID-ul shellului curent poate fi aflat folosind comanda:
student@uso:~$ echo $$
9477
  • Omorâți shellul curent.
  • Porniți o aplicație shutter. Porniți o aplicație libreoffice. Porniți o aplicație firefox. Porniți o aplicație transmission-gtk. Omorâți aceste procese folosind utilitarul kill.
Proces abuziv

Creați un fișier cpu_hog cu următorul conținut:

#!/bin/bash
 
(
nohup dd if=/dev/zero of=/dev/null bs=8M > /dev/null 2>&1 &
)

Folosiți scriptul cpu_hog pentru a porni un proces care consumă mult procesor. Îl porniți folosind o comandă de forma:

$ chmod +x cpu_hog
$ ./cpu_hog

Scriptul cpu_hog pornește un proces care execută o buclă infinită.

Observați, cu ajutorul comenzii top, că procesorul este încărcat. Identificați procesul cel mai consumator de resurse și omorâți-l. Observați, cu ajutorul comenzii top, că acum procesorul nu mai este încărcat.

Dăm chmod +x cpu_hog pentru a putea executa scriptul cpu_hog. NU trebuie să o dăm de fiecare dată când rulăm scriptul, o dată este suficient.

Solutii

sde2/laboratoare/05_microbit_ro.txt · Last modified: 2021/04/12 12:10 by ioana_maria.culic
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0