<div class="page">
<div class="cover text-center">
<img class="mx-auto" src=/itb/images/logo_mislata.png alt="logo">
# Progamació de processos
<div class="text-end fit-content ms-auto my-3 mt-auto pt-3">
<p><strong>Autor:</strong> Joan Puigcerver Ibáñez</p>
<p><strong>Correu electrònic:</strong> j.puigcerveribanez@edu.gva.es</p>
<p><strong>Curs:</strong> 2024/2025</p>
</div>
<div>
<p class="fw-bold mb-0">Llicència: BY-NC-SA</p>
<p class="d-none d-md-block">(Reconeixement - No Comercial - Compartir Igual)</p>
<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.ca" target="_blank">
<img class="mx-auto" src="/itb/images/license.png" alt="Licence"/>
</a>
</div><!--license-->
</div><!--cover-->
</div><!--page-->
{:toc}
## Conceptes bàsics
__Programa:__ Conjunt comprès per codi i dades que es troba al disc que resolen
una necessitat concreta dels usuaris.
__Procés:__ Quan un programa s'executa, es crea un procés, que controla
la seua execució, els recursos que consumeix i el seu estat. Inclou:
- __Comptador del programa:__ Indica el punt o la instrucció que esta executant.
- __Imatge de memòria:__ Conté tot el espai en memòria utilitzat i les dades
emmagatzemades.
- __Estat del processador:__ Valor dels registres del processador que s'utilitzen.
- __Usuari:__ Usuari que ha iniciat el procés, que marcarà els permisos dels
quals disposa el procés (recursos del sistema que pot accedir...)
Quan s'interromp l'execució d'un procés per alguna causa, es veuran més endavant,
es desa una còpia dels tres elements esmentats a dalt per poder restaurar
l'execució del procés al mateix punt en què es va interrompre.
Els processos són entitats independents del programa que s'executa.
Dos processos que executen el mateix programa poden coexistir.
Per exemple, es poden tenir oberts dos fitxers pdf alhora amb el mateixa
aplicació. Un altre exemple és el navegador Chrome. Chrome
crea un procés diferent per cada pestanya que es té oberta.
__Executable:__ És un fitxer que conté el programa en __binari__ (llenguatge
màquina) i que es pot executar en el dispositiu.
__Sistema operatiu:__ Peça de programari encarregada de gestionar de
manera eficient tots els recursos de màquinari i programari del dispositiu,
proporcionat un entorn amigable i fàcil d'utilitzar per l'usuari,
i una interfície comuna per tot el programari instal·lat en aquest.
<img src="/itb/DAM-PSP/UD1/img/os.png" alt="" class="center" height="400"/>
__Dimoni (daemon) o servei:__ És un procés que s'executa en segon plà i
no és accessible per l'usuari. La seua funció és proporcional alguna funcionalitat o servei
al sistema i a la resta de processos. Exemples en Linux:
- __systemd__: Dimoni d'administració de sistema dissenyat exclusivament
per l'API del nucli Linux.
- __networking__: Gestionar les interfícies de xarxa del dispositiu.
- __crond__: El dimoni del planificador per a accions basades en el temps,
com ara actualitzacions de programari o comprovacions del sistema.
- __logind:__ Dimoni que gestiona els inicis de sessió.
## Programació concurrent
La __computació concurrent__ permet que multiples programes s'estiguen executant
alhora. Per exemple, es pot estar escoltant música, mentre s'utilitza un
sistema de compartició de fitxers p2p i es redacta un document de text.
Per poder implementar la computació concurrent, cal tindre en compte
diferents escenaris:
- __Dispositius amb un únic processador__ amb un únic nucli i sense fils:
En aquest cas, només es pot executar un procés a la vegada. Per donar
la sensació que diversos procesos s'executen a la vegada, el sistema
operatiu va canviant de procés en execució en poc de temps (mil·lisegons).
- __Diversios nuclis en un mateix processador__: Hui en dia és molt normal
que els processadors continguen diversos nuclis (també en mòbils).
Cada nucli pot executar un programa diferent. Com tots els processos
comparteixen memòria, la comunicació entre ells és molt ràpida i senzilla
d'implementar.
També es pot donar el cas que cada nucli execute una instrucció diferent
del mateix programa. Aquestà tècnica rep el nom de __programació paral·lela__,
que permet millorar considerablement el rendiment programa. Per exemple,
quan s'edita un document de text, un nucli s'encarrega de gestionar
l'edició del text i un altre de revisar la correcció gramatical del text.
- __Sistemes distribuïts:__ Sistema que consisteix en diferents equips
distribuits en xaxa. Cada equip disposa del seu propi processador i memòria
i la gestió conjunta de tots els equips s'anomena __programació distribuïda__.
Millora el rendiment considerablement, però la la gestió i comunicació entre
processos és més complicada, ja que no comparteixen mèmoria i requereixen
d'esquemes de comunicació especiífics i costosos a través de la xarxa.
El __cloud computing__ estaria dins d'aquesta categoria.
## Funcionament bàsic del sistema operatiu
El __sistema operatiu__ (SO), com qualsevol altre programa, necessita estar
en memòria per poder ser executat. Hi ha una part del sistema operatiu que
s'encarrega de gestionar tota la resta i que ha d'estar sempre em mèmoria:
__el nucli o kernel__.
El kernel gestiona els recursos de l'ordinador, permetent l'accés a aquests
mitjançant les trucades al sistema.
El kernel és una part molt xicoteta del SO, sobretot si es compara,
amb tot el necessari per implementar la interfície gràfica.
A la resta de SO se'n diu programes del sistema.
El nucli del sistema generalment treballa sobre la base
d'__interrupcions__ (IRQ – Interrupt ReQuest).
Una interrupció és la suspensió temporal d'un procés per executar
la rutina que gestiona aquesta interrupció. Mentre s'atén una interrupció,
es deshabilita l'arribada d'altres interrupcions. Les interrupcions solen
estar generades per trucades al sistema.
Les trucades al sistema es programen amb __llenguatges de baix nivell__,
com C o C++, pel fet que permeten l'accés a nivells més baixos del maquinari.
En els __lenguatges d'alt nivell__, els desenvolupadors fan ús d'__APIs__ del
sistema. Les més comunes són Win32 (Windows), API POSIX (per a sistemes
Unix en què s'inclou GNU Linux i Mac OS).
## Processos
Com hem dit anteriorment, un procés és l'estat d'un programa en un moment donat
de la seva execució amb tot el que això significa: comptador, memòria i
registres de la CPU.
És el SO l'encarregat de posar en execució i gestionar els processos.
En els sistemes que suporten programació concurrent, els processos poden
passen per diferents estats al llarg del seu cicle de vida.
Els canvis d'estat també són gestionats pel sistema operatiu.
Hi ha diversos models per implementar el cicle de vida d'un procés.
### Estats d'un procés
- __Model de tres estats__ (blanc en la imatge):
- _A punt_ o _esperant_: Programa preparat per entrar a la CPU. Ja se li
ha assignat zona en memòria.
- _Execució_: La CPU està executant el procès.
- _Bloquejat_: Sense possibilitat d'entrar a la CPU, per exemple,
per què està esperant una operació d'E/S.
- __Model de cinc estats__: Afegeix dos estats al model anterior (blanc a la imatge).
- _Creat_: S'està creant o s'acaba de crear, però encara no esta llest
per ser executat. Per exemple, si s'ha arribat al límit de la memòria.
- _Acabat_: El procès ha fintalitzat i allibera els recursos i la memòria
utilitzada. També passa a aquest estat si ocurreix un error i deixa
d'executar-se.
- __Model de set estats__: Afegeix dos estats més (tota la imatge).
Hi ha moments en què es poden trobar molts processos bloquejats, ocupant
memòria i no deixen que altres processos puguen entrar en execució. En
aquests casos, és important que es permitisca realitzar un __intercanvi (swap)__
i que aquests processos bloquejats passen a emmagatzermar-se en disc,
alliberant memòria principal.
- _A punt i suspés_: Procesos preparats per entrar en execució però
que es troben en memòria secundària.
- _Bloquejat i suspés_: Procesos bloquejats però que es troben en
memòria secundària.
<img src="/itb/DAM-PSP/UD1/img/process_states.png" alt="" class="center" height="400"/>
### Cues de processos
Una de les funcions del SO és la gestió de processos,
i en concret, gestionar els diferents estats dels processos
i determinar quins processos passen a executar-se.
Per realitzar aquesta tasca, fa ús de diferents cues.
- _Cua general de processos_: conté tots els processos del sistema.
- _Cua de processos preparats_: conté aquells processos llestos per executar-se.
- _Cues de dispositiu_: conté els processos que esperen alguna operació E/S.
### Planificació de processos
El SO necessita un __planificador de processos__ que s'encarrega de
gestionar les cues de processos. Hi ha dos tipus de planificació:
- __Curt termini:__ S'encarrega de triar quin és el següent procés en entrar
a la CPU, aquesta operació s'executa moltes vegades i cada poc temps
(mil·lisegons), per la qual cosa l'algorisme ha de ser senzill.
Hi ha tres tipus d'algorismes per a aquesta planificació.
- _Sense desallotjament_: Un procés que entra passa a execució no
desallotja la CPU fins que no acaba o es bloqueja.
Exemples: FIFO (First-In First-Out) i SJF (Shortest Job First).
- _Amb desallotjament_: Permeten desallotjar el procès del processador
encara que aquest no haja acabat. Examples: Round Robin o SRT (Shortest
Remaining Time) sense prioritat, o planificacions que permeten
que els processos tinguen diferents nivells de prioritat, com el
Multilevel feedback queue.
- __Llarg termini__: S'encarrega de gestionar els processos que passen
a la cua de preparats, que s'invoca amb poca frequència.
Cada procés és únic i impredictible. Els algorismes de planificació necessiten
saber de quin tipus és per planificar els processos de la millor manera possible.
Podem trobar processos:
- __Orientats a CPU (CPU-bound)__: Normalment sols utilitza el processador
per la seua execució.
- __Orientats a E/S (IO-bound)__: L'execució depen del sistemà E/S i els seus
recursos, com els discs durs o perifèrics.
### Arbre de processos
Tots els processos han segut iniciats d'alguna manera. Alguns, s'han
llançat per el SO, alguns altres els ha executat l'usuari, però
ningút ha aparagut espontàneament. Això significa que qualsevol procés,
ha segut iniciat per un altre procés.
- Quan el SO arranca, inicia un procés amb la interfície gràfica
i el gestor de finestres.
- Quan l'usuari clica sobre l'accés directe del navegador, el gestor gràfic
inicia el navegador en una nova finestra.
- Quan l'usuari obri una nova pestanya, el navegador crea un procés per gestionar
aquesta pestanya.
Qualsevol procés en execució depen del procès que l'ha creat i estableix
un enllaç entre ells. El procés creador s'anomena __pare__ i el creat __fill__.
Quan s'inicia l'ordinador, el gestor d'arrancada s'encarrega d'executar el
__kernel__ del SO i es crea el procés principal, sobre el qual aniran creant-se
la resta de processos de manera jeràrquica. En Linux, aquest procés s'anomena
__init__. Podem utilitzar la comanda __pstree__ per veure l'abre de processos.
Tots els processos s'identifiquen mitjançant el __PID__ (Process Identifier),
que és únic per a cada procés.
## Operacions bàsiques amb processos
Les operacions amb processos es realitzen mitjançant trucades al sistema,
ja que l'encarregat de gestionar-los és el sistema operatiu, per tant,
aquestes operacions poden ser realitzades de diferent maneres depenent
del sistema operatiu en el qual es trobeu.
Les operacions que es poden invocar des d'un programa son:
- __Crear process__: Crea un nou procés. Aquesta operació pot ser:
- _Iniciar un programa diferent_: Cal indicar el programa que es desitja
executar i els arguments.
- _Fork_: Realitza un duplicat del procés que crea el procés, que contínua
l'execució des del mateix punt, però continuen per separat.
Els processos podern fer ús de __recursos compartits__. La __memòria compartida__
és una regió de la memòria a la qual poden accedir diferents processos que
col·laboren. El SO s'encarrega de crear i establir els permisos
dels processos que poden accedir a aquest espai de mèmoria.
- __Esperar (i bloquejar)__: En alguns casos, els processos necessiten
coordinar-se i esperar a que un altre procés acabe la tasca que està
realitzant per poder continuar la seua execució. Això es fa mitjançant
la operació __wait__.
- __Acabar process__: Un procès pot acabar de diferents maneres. Per ell mateix,
el procés acaba si arriba a la última instrucció o si es crida a la funció
__exit(return_code)__. Aquesta funció rep el paràmetre __return_code__, que
és un codi intern per identificar el motiu pel qual ha acabat.
Si acaba perquè arriba a la última instrucció, es crida a aquesta funció
de manera automàtica amb un valor de 0. Qualsevol altre valor distint
a 0, indica que ha acabat amb algun error i el programa ha fallat.
El desenvolupador és el responsable de especificar els codis d'error
del seu programa.
El procés pare té "control" sobre el proces fill, per tant,
també pot acabar amb la seua execució. Per fer-ho, existeix
l'operació __destroy__, que acaba abruptament l'execució del fill.
Es podria donar el cas que el procés pare acabe abans que el procés fill.
Si aixó ocurreix, es diu que el procés fill és un __procés orfe__.
Aquesta situació es gestionada de diferent manera per cada sistema operatiu.
Alguns SO no permeten aquesta situació, i realitzen una __terminació en cascada__,
que termina tots els processos fills si el procés pare acaba.
### Creació de processos
En Java, existeix la classe [__Process__](https://docs.oracle.com/javase/8/docs/api/java/lang/Process.html)
que representa un procés.
Existeixen dos métodes que serveixen per crear processos i retornen
un objecte de la clase Process, que serveix per gestionar aquest nou
procés.
- [`public Process ProcessBuilder.start()`](https://docs.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html#start()):
Aquest mètode inicia el programa especificat en la creció del objecte `ProcessBuilder` i l'inicia.
```java
ProcessBuilder pb = new ProcessBuilder("myCommand", "myArg1", "myArg2");
Process p = pb.start();
```
- [`public Process Runtime.exec(String[] cmdarray, String[] envp, File dir)`](https://docs.oracle.com/javase/7/docs/api/java/lang/Runtime.html#exec(java.lang.String[],%20java.lang.String[],%20java.io.File)):
Aquest mètode inicia el programa especificat en els paràmetres de la funció.
- `String[] cmdarray`: Comanda i arguments de la comanda a executar.
- `Sring[] envp`: Variables d'entorn que es dessitjen utilitzar.
- `File dir`: Directori de treball del programa
```java
Runtime runtime = Runtime.getRuntime();
String[] cmdarray = {"myCommand", "myArg1", "myArg2"};
String[] envp = {"VAR=value"};
File dir = new File("path/to/working_directory")
Process p = runtime.exec(cmdarray, envp, dir);
```
Els dos mètodes anteriors poden llançar una exepció d'E/S:
[`IOException`](https://docs.oracle.com/javase/7/docs/api/java/io/IOException.html).
Això pot ocurrir per diferent motius, com que no es trobe la comanda o
que l'usuari no tinga permisos per executar eixe programa, entre altres motius.
Per tant, cal crear el procés dins d'un bloc `try{ ... } catch { ... }`.
### Wait
El procés pare pot esperar que acabe el procés fill.
En Java, aquesta es realitza mitjançant la classe `Process` amb el mètode:
- [`public int Process.waitFor()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Process.html#waitFor--):
El procés actual espera a que el procés representat per `Process` acabe.
Retorna el __codi__ de exida del procés. El valor 0 indica una
acabada normal.
```java
args = {"myCommand", "myArg1", "myArg2"};
ProcessBuilder pb = new ProcessBuilder(args);
Process p = pb.start();
int returnCode = p.waitFor();
System.out.println(
"L'execució de " + Arrays.toString(args)
+ " retorna " + returnCode
);
```
### Destroy
El procés pare pot terminar abruptment (destroy o kill) el procés fill.
En Java, aquesta es realitza mitjançant la classe `Process` amb el mètodes:
- [`public void Process.destroy()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Process.html#destroy--):
Acaba el procés de una manera normal i ordenada. Això pot provocar que el procés
no acabé inmediatament.
- [`public void Process.destroyForcibly()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Process.html#destroyForcibly--):
Acaba el procés de manera forçada. Això pot provocar que el procés
no acabe correctament.
En els dos casos, és pot utilitzar els mètodes `isAlive()` o `waitFor()`
per comprovar si ha acabat o esperar que acabe respectivament.
```java
args = {"myCommand", "myArg1", "myArg2"};
ProcessBuilder pb = new ProcessBuilder(args);
Process p = pb.start();
p.destroy();
System.out.println(
"El procés: " + Arrays.toString(args)
+ p.isAlive() ? " està viu." : " ha acabat."
);
```
## Exemples en Java
### RunProcess
Aquest programa crea un nou programa a partir dels arguments passats al programa __RunProcess__.
El procés és creat mitjançant la classe `ProcessBuilder`.
Una vegada creat, el procés pare espera a que acabe el procés fill i imprimeix per pantalla
el _codi de retorn_ del procés fill.
- <a href="/itb/DAM-PSP/files/ud1/examples/RunProcess.java" download="RunProcess.java">RunProcess.java</a>
```java
package ud1.examples;
import java.io.IOException;
import java.util.Arrays;
public class RunProcess {
public static void main (String[] args) {
// Indica la comanda que utilitza aquest programa per iniciar un nou procés
String[] program = {"notepad"};
ProcessBuilder pb = new ProcessBuilder(program);
try {
// Inicia el procés fill
Process process = pb.staet();
// El procés Java (pare) Espera a que el procés fill finalitze
int codiRetorn = process.waitFor();
System.out.println("L'execució de "+ Arrays.toString(program) +" retorna "+ codiRetorn);
} catch (IOException ex) {
System.err.println("Excepció d'E/S.");
System.out.println(ex.getMessage());
System.exit(-1);
} catch (InterruptedException ex) {
System.err.println("El procés fill ha finalitzat de manera incorrecta.");
System.exit(-1);
}
}
}
```
_Exmeple d'execució_:
- `String[] program = {"notepad"};`: El programa `RunProcess` executa la comanda `notepad`, que crea un process amb l'editor de notes del sistema.
Desrpés, espera a que acaba i retorna el codi 0: __èxit__.
- `String[] program = {"powershell", "sleep", "5"};`: El programa `RunProcess` executa la comanda `sleep 5` en la línia de comandes `Powershell`.
Aquest procés esperarà 5 segons i acabarà amb el codi 0: __èxit__.
- `String[] program = {"powershell", "noexisteix"};`: El programa `RunProcess` intenta executar la comanda `noexisteix` en la línia de comandes `Powershell`,
que evidentment no existeix. Això fa que el procés acabe en el codi d'error 1: __error__.
### DestroyProcess
Aquest programa crea un nou programa a partir dels arguments passats al programa __DestroyProcess__.
El procés és creat mitjançant la classe `Runtime`.
Una vegada creat, el procés pare termina el procés fill abans de que acabe i comprova si ha acabat.
Per últim, espera a que acabe i torna a mostrar si ha acabat.
- <a href="/itb/DAM-PSP/files/ud1/examples/DestroyProcess.java" download="DestroyProcess.java">DestroyProcess.java</a>
```java
package ud1.examples;
import java.io.IOException;
import java.util.Arrays;
public class DestroyProcess {
public static void main (String[] args) {
// Indica la comanda que utilitza aquest programa per iniciar un nou procés
String[] program = {"powershell", "sleep", "5"};
try {
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(program);
System.out.println(
"El procés: " + Arrays.toString(program)
+ (process.isAlive() ? " està viu." : " ha acabat.")
);
System.out.println("Destruint...");
process.destroy();
process.waitFor();
System.out.println(
"El procés: " + Arrays.toString(program)
+ (process.isAlive() ? " està viu." : " ha acabat.")
);
} catch(IOException ex) {
System.err.println("Excepció d'E/S");
System.err.println(ex.getMessage());
System.exit(-1);
} catch(InterruptedException ex) {
System.err.println("El procés fill ha finalitzat de manera incorrecta.");
System.exit(-1);
}
}
}
```
_Exmeple d'execució_:
- `String[] program = {"powershell", "sleep", "5"};`: El programa `DestroyProcess` executa la comanda `sleep 5` en la línia de comandes `Powershell`,
que hauria d'esperar 5 segons. Acte seguit, comprova si està viu amb `isAlive()`, el destrueix i espera a que el fll acabe.
Com es pot observar, el procés fill acaba abans d'hora perquè s'ha terminat.
## Bibliografia
- https://ca.wikipedia.org/wiki/Gesti%C3%B3_de_processos
- https://www.baeldung.com/cs/cpu-io-bound