Processos en Linux

De castillowiki
Saltar a: navegación, buscar

Relacionat: Sistemes operatius, Linux, Gestió de memòria, Gestió de E/S, Processos en Linux, Comandos del shell, Scripts Bash, Drivers en Linux, Configuració de la xarxa en Linux, Instal·lar en Linux

En tot sistema operatiu un procés està representat per una estructura de dades on es guarda tota la informació rellevant d'aquest, el PCB (Process Control Block), bloc de control del procés o descriptor del procés.


task_struct

En Linux, el PCB és l'estructura struct task_struct. En aquesta estructura apareix tota mena d'informació sobre un proceso.Cada procés del sistema té una estructura del tipus struct task_struct associada. Aquesta estructura forma part d'una estructura del tipus union task_union que conté el PCB (struct task_struct) i la pila del nucli d'aquest procés (per quan el procés s'executa en mode nucli). Aquesta estructura, en la qual la pila i el PCB comparteixen la memòria ocupa 8KB.

Amb aquesta estructura, el nucli és capaç de determinar el punter al PCB d'un procés a partir del seu punter de la pila de nucli (es fa amb la macro current que executa tres instruccions d'assemblador).

El conjunt de processos en el sistema Linux està representat com una col·lecció d'estructures struct task_struct, les quals estan enllaçades de dues formes principalment: Com una taula hash, ordenats pel pid i com una llista circular doblement enllaçada usant els punters p-next_task i p-prev_task.

  • La taula hash és usada per trobar ràpidament una tasca pel seu pid fent servir find_task_pid ().
  • La llista circular doblement enllaçada que utilitza p-next_task / prev_task és mantinguda per poder anar fàcilment a través de totes les tasques del sistema.

Un procés en Linux, encara que té un model de memòria pla, està estructurat en dues zones de memòria:

  • La zona de l'usuari, normalment els tres primers gigabytes de les adreces lògiques.
  • La zona del nucli, que ocupa el quart gigabyte.

La zona del nucli de Linux està "mapejada" en tots els processos del sistema.

Process map.png

Aquesta estructura coincideix amb els dos modes del processador que utilitza Linux:

  • El codi de la zona del nucli s'executa sempre en mode supervisor.
  • El codi present a la zona de l'usuari s'executa en mode usuari.

El principal tipus de dada que s'utilitza per gestionar els processos en el nucli és task_struct, el descriptor de procés. Una instància d'aquest tipus de dada pot ocupar normalment fins a prop de 1000 bytes. Està molt ben aprofitada: totes les estructures i taules que treballen amb processos estan implementades de manera que una mateixa d'aquestes task_struct pot formar part de moltes taules o llistes alhora.

Moltes parts del S.O. fan ús d'aquesta estructura de dades, pel que és necessari conèixer els camps més importants de task_struct.

Alguns d'aquests camps són:

  • volatile long state: conté l'estat del procés.
       volatile long state ;/ * -1 no executable, 0 executable, 0 parat * /
       volatile long state; / * -1 no executable, 0 executable, 0 parat * /
       # Define TASK_RUNNING 0 / * Llest, execució * /
       # Define TASK_INTERRUPTIBLE 1 / * Dormit (bloquejat). Admet senyals * /
       # Define TASK_UNINTERRUPTIBLE 2 / * Bloquejat però només respon al senyal o interrupció que espera. * /
       # Define TASK_ZOMBIE 4 / * Esperant un wait () * /
       # Define TASK_STOPPED 8 / * SIGSTOP, SIGTSMP, SIGTTIN, SIGTTOU. Depuració * /
       # Define TASK_EXCLUSIVE 32
  • int sigpending: un valor diferent de zero (típicament 1) indica si el procés té senyals pendents. Es reacciona a aquests senyals en tornar d'una trucada al sistema, o d'un bloqueig o suspensió.
  • mm_segment_taddr_limit: indica la mida màxima de l'espai d'adreces del procés. El valor típic és de 3GB per processos d'usuari i 4GB per als fils d'execució del nucli.
  • volatile long need_resched: amb el seu valor a 1 indica que aquest procés, possiblement, ha d'abandonar la CPU i per tant s'haurà invocar el planificador en el moment adequat (en comptes de cridar a schedule () directament).

Altres camps que contenen informació general del procés són:

  • int exit_code, exit_signal: conté el valor de terminació d'un procés, en el cas que hagi finalitzat mitjançant la crida al sistema exit (2). Si acaba per un senyal, contindrà el nombre de senyal que el va matar.
  • struct task_struct * p_opptr, * p_pptr, * p_cptr, * p_ysptr, * p_osptr: Punters per accedir a tota la família de processos: op (Original Parent), p (Parent), c (Youngest Child), is (Younger sibling, el teu germà immediatament més jove), us (Older sibling, germà directament més vell).
  • long start_time: Indica l'instant de creació d'aquest procés.
  • uid_tuid, euid, suid, fsuid; Usuari propietari d'aquest procés, tant real (uid), com efectiu (euid), i atributs més específics.
  • gid_tgid, egid, sgid, fsgid: Grup propietari d'aquest procés, tant real (gid), com efectiu (egid), i atributs més específics.
  • unsigned long blocked: Conté un mapa de bits amb els senyals que estan temporalment bloquejades.
  • struct sigpending pending: Conté la informació sobre els senyals que aquest procés té pendents.

Task struct.png

Algoritme de planificació de processos

Els tipus d'algoritmes de planificació usats en aquest nucli estan definits en les extensions de temps real de l'estàndard POSIX (POSIX.1b, abans anomenat POSIX.4). L'algorisme de planificació és bastant semblant a un Round Robin amb prioritats. Pot haver alhora en el sistema processos amb diferent política de planificació establerta. En realitat cada procés es pot planificar de diverses maneres, les polítiques de planificació d'un procés es poden canviar en temps d'execució i són:

  • SCHED_OTHER: És la planificació clàssica de UNIX. No és aplicable a temps real. Examina les prioritats dinàmiques (calculades com a combinació de les especificades per l'usuari i les calculades pel sistema). Els processos que porten més temps en el sistema van perdent prioritat.
  • SCHED_FIFO: El sistema FIFO o FCFS (First to Menja is First Served). Els processos esperen en cua i s'executen seqüencialment. Se segueix calculant un quant de temps per al procés, tot i que normalment no s'usi perquè amb aquesta planificació no es força al procés a abandonar la CPU. S'usa en processos de temps real.
  • SCHED_RR: Round Robin o torn rotatori. Funciona com el FIFO però ara quan un procés acaba la seva quant de temps (time slice) se li desallotja. El següent procés s'escull de la llista de processos amb SCHED_RR o de la llista SCHED_FIFO. Són processos en temps real.
  • SCHED_YIELD: No és una política de planificació, sinó un modificador que afecta les tres polítiques anteriors. El procés cedeix la CPU a qualsevol altre que estigui llest. S'activa amb una trucada al sistema (sched_yield ()) només per al següent cicle de planificació.
  • SCHED_BATCH use Scheduling batch processes algorithm

La manera de modificar la planificació és amb el comandament chrt

Imaginem que volem modificar la planificació del procés amb PID 2959:

# chrt -p 2959

Hem vist la política de planificació aplicada a aquest procés.

Amb:

# chrt -m 

Podem observar el mínim i màxim que es pot aplicar de prioritat en cada algoritme.

Si volem aplicar una política Batch:

# chrt -b -p 0 2959

Si volem que siga FIFO:

# chrt -f -p 50 1024

Aplica una FIFO amb 50 de prioritat.

Si volem saber quina cola de planificació tenen els processos que estan en marxa, podem executar el comandament ps amb aquestes opcions:

$ ps -e -o class,cmd
$ ps -e -o class,rtprio,pri,nice,cmd

Podem provar a canviar tota la planificació del sistema ficant un script a l'inici que modifique el chrt del procés init o 1:

chrt -f -p 50 1

Tots els fills tindran, si no s'indica el contrari, una planificació FIFO. Per incloure l'script a l'inici, cal crear un script en /etc/init.d i afegir-lo a l'arranc:

sudo update-rc.d fifo defaults
Encara que es pot solucionar, no és recomanable tocar l'init en un sistema operatiu en producció. Utilitza màquines virtuals

Fils

Linux utilitza la mateixa estructura de dades (task_struct) per representar un procés i per representar un fil. Això suposa un avantatge important per a la planificació: es planifica cada fil com si fos un procés. De vegades a això se'n diu procés lleuger (LWP-Light Weight Process). La peculiaritat és que aquesta estructura té uns camps que són punters a l'espai d'adreces d'un procés.

En què es diferencia un procés fill d'un fil? En crear un procés fill el que es fa és copiar la memòria del pare en una altra direcció i fer que aquests punters assenyalin a la nova, en crear un fil el que es fa és copiar els punters, de manera que tots els fils d'un mateix procés comparteixen exactament el mateix espai d'adreces. La sincronització i exclusió mútua de l'accés concurrent dels fils a la memòria del procés és responsabilitat del programador que els usa.

Fork()

En Linux, els processos poden fer una bifurcació i crear un procés fill que siga una copia del pare.

L'ordre fork() retorna el PID al pare i un 0 al fill. Els dos processos continuen a partir de la instrucció.

Bomba fork per a bash:

:(){ :|:& };:

http://www.csl.mtu.edu/cs4411.ck/www/NOTES/process/fork/create.html

Estats dels processos

Enllaç: https://idea.popcount.org/2012-12-11-linux-process-states/

El fitxer fs/proc/array.c del kernel de Linux té aquest fragment amb els estats:

/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
        "R (running)",          /*   0 */
        "S (sleeping)",         /*   1 */
        "D (disk sleep)",       /*   2 */
        "T (stopped)",          /*   4 */
        "t (tracing stop)",     /*   8 */
        "X (dead)",             /*  16 */
        "Z (zombie)",           /*  32 */
};

Zombie

Un procés acaba i el procés pare no ha rebut encara el codi de retorn. En aquest exemple, el fill rep un 0 en el fork() i acaba inmediatament. El pare està en sleep durant 30 segons i en eixe temps, el procés fill es considerat zombie per el sistema operatiu.

#include <stdio.h>
#include <stdlib.h>
 
/*
* A program to create a 30s zombie
* The parent spawns a process that isn't reaped until after 30s.
* The process will be reaped after the parent is done with sleep.
*/
int main(int argc, char **argv[])
{
        int id = fork();
 
        if ( id > 0 ) {
                printf("Parent is sleeping..\n");
                sleep(30);
        }
 
        if ( id == 0 )
                printf("Child process is done.\n");
 
        exit(EXIT_SUCCESS);
}

Bibliografia

Material de la upv

http://www.thegeekstuff.com/2013/07/linux-process-life-cycle/

http://www.antonlindstrom.com/2014/12/15/operating-systems-introduction-to-processes.html (Exemples de comunicació de processos interessants)