Download/traduzioni/LinuxGazette-dic-2006-saha-it-old
MAN
BootPrompt-HOWTO.sgml.txt
Configuring_mkinitcpio.txt
LinuxGazette-dic-2006-saha-it-old.html
LinuxGazette-dic-2006-saha-it.html
Network-boot-HOWTO.sgml.txt
Pine-Exchange.sgml.txt
SMP-HOWTO.sgml.txt
kernel-list-it.html
linked-list.txt
nomachineNXserver.html
wireless.txt
Parlando di Processi in Linux
By Amit Saha - trad. Cardelli Sandro
Allora, cosa e' un processo?
Riporto dal libro di Robert Love Linux Kernel Development, "Il Processo e' una delle astrazioni fondamentali nei sistemi operativi Unix, essendo i file l'altra fondamemtale astrazione." Un processo e' un programma in esecuzione. Consiste nell'esecuzione di codice di programma, un insieme di risorse come i file aperti, dati interni del kernel, e spazio indirizzato, uno o piu; thread di esecuzione e una sezione di dati contenenti le variabili globali.
Descrittori di processo
Ogni processo ha associati a se dei descrittori di processo. Questi tengono le informazioni usate per tenere traccia di un processo nella memoria. Tra le vari parti delle informazioni memorizzate per un processo sono: il suo PID, lo stato, i processi parenti, i figli, fratelli, i valore dei registri del processore, la lista dei file aperti e altre informazioni sullo spazio indirizzato.
Il Kernel Linux usa una lista circolare concatenata doppiamente della struttura struct task_struct per memorizzare questi descrittori di processi. Questa struttura e' dichiarata in linux/sched.h. Ecco alcuni campi dal kernel 2.6.15-1.2054_FC5, iniziando dalla riga 701:
701 struct task_struct { 702 volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ 703 struct thread_info *thread_info; . . 767 /* PID/PID hash table linkage. */ 768 struct pid pids[PIDTYPE_MAX]; . . 798 char comm[TASK_COMM_LEN]; /* executable name excluding path
La prima riga della struttura definisce il campo state come volatile long. Questa variabile e' usata per tenere traccia dello stato dell'esecuzione del processo, definito dalle seguenti macro:
#define TASK_RUNNING 0 #define TASK_INTERRUPTIBLE 1 #define TASK_UNINTERRUPTIBLE 2 #define TASK_STOPPED 4 #define TASK_TRACED 8 /* in tsk->exit_state */ #define EXIT_ZOMBIE 16 #define EXIT_DEAD 32 /* in tsk->state again */ #define TASK_NONINTERACTIVE 64
In caso la parolachiave volatile non sia chiara - vedere http://www.kcomputing.com/volatile.html per maggiori informazioni.
Liste collegate
Prima di guardare a come i task/processi (useremo le due parole come sinonimi) sono memorizzati dal kernel, occorre capire come il kernel implementa le liste circolari collegate. L'implementazione che segue e' uno standard che e' usato in tutti i sorgenti del kernel. La lista collegata e' dichiarata in linux/list.h e la struttura dei dati e' semplice:
struct list_head { struct list_head *next, *prev; };
Questo file definisce anche altre macro gia' pronte e funzioni che e' possibile usare per manipolare le liste collegate. Questo standardizza l'implementazione delle liste di collegamenti per prevenire che le persone "reinventno la ruote" e introducano nuovi bug.
Ecco qui alcuni riferimenti a liste collegate del kernel:
- Suman Adak ha un eccellente presentazione nel proprio blog a questo link
- Linux kernel source: <include/linux/list.h>, <include/linux/sched.h>
- "Linux Kernel Development", di Robert Love (Appendice A)
- http://www.win.tue.nl/~aeb/linux/lk/lk-2.html
La lista dei task del Kernel
Guardiamo ora come il kernel utiliza le liste circolari doppiamente collegate per memorizzare la struttura dei processi. Cercando la struttura list_head dentro la definizione della struttura task_struct abbiamo:
struct list_head tasks;
Questa riga ci mostra che il kernel usa una lista collegata circolare per memorizzare i task. Questo significa che e' possibile usare le macro e funzioni standard della lista collegata del kernel per passare attraverso la lisya dei task completa.
Init e' il padre di tutti i processi su in sistema Linux. Pertanto esso e' rappresentato all'inizio della lista, anche se in senso stretto non c'e' una testa poiche' questa e' una lista circolare. Il descrittore del processo del task di init e' allocato staticamente:
extern struct task_struct init_task;
Di seguito si mostra la rappresentazione della lista collegata dei processi in memoria:
Sono disponibili diverse altre macro e funzioni per aiutare a scorrere questa lista:
for_each_process() e' una macro che itera sull'intera lista dei task. E' definita come segue in linux/sched.h:
#define for_each_process(p) \ for (p = &init_task ; (p = next_task(p)) != &init_task ; )
next_task() e' una macro definita in linux/sched.h la quale ritorna il prossimo task nella lista:
#define next_task(p) list_entry((p)->tasks.next, struct task_struct, tasks)
list_entry() e' una macro definita in linux/list.h:
/* * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. */ #define list_entry(ptr, type, member) \ container_of(ptr, type, member)
La macro container_of() e' definita come segue::
#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})
Pertanto se possiamo passare attraverso l'intera lista dei task e' possibile avere l'esecuzione di tutti i processi nel sistema. Questo puo' essere fatto con la macro for_each_process(task) , dove il task e' un puntatore della struttura tipo task_struct. Ecco un esempio di modulo del kernel, dal Linux Kernel Development:
/* ProcessList.c Robert Love Chapter 3 */ #include < linux/kernel.h > #include < linux/sched.h > #include < linux/module.h > int init_module(void) { struct task_struct *task; for_each_process(task) { printk("%s [%d]\n",task->comm , task->pid); } return 0; } void cleanup_module(void) { printk(KERN_INFO "Cleaning Up.\n"); }
L'attuale macro e' un link al descrittore di processo (un puntatore a un task_struct) del processo in esecuzione corrente. Come l'attuale ottiene i suoi e' dipendente dall'architettura. Su un x86 questo e' fatto dalla funzione current_thread_info() dentro asm/thread_info.h
/* how to get the thread information struct from C */ static inline struct thread_info *current_thread_info(void) { struct thread_info *ti; __asm__("andl %%esp,%0; ":"=r" (ti) : "0" (~(THREAD_SIZE - 1))); return ti; }
In fine le attuali dereferenze il membro del task della struttura thread_info che e' riprodotta sotto da asm/thread_info.h dal current_thread_info()->task;
struct thread_info { struct task_struct *task; /* main task structure */ struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ unsigned long status; /* thread-synchronous flags */ __u32 cpu; /* current CPU */ int preempt_count; /* 0 => preemptable, <0 => BUG */ mm_segment_t addr_limit; /* thread address space: 0-0xBFFFFFFF for user-thread 0-0xFFFFFFFF for kernel-thread */ void *sysenter_return; struct restart_block restart_block; unsigned long previous_esp; /* ESP of the previous stack in case of nested (IRQ) stacks */ __u8 supervisor_stack[0]; };
Usando l'attuale macro e init_task e' possibile scrivere un modulo del kernel per la tracciatura dall'attuale processo in dietro fino a init.
/* Traceroute to init traceinit.c Robert Love Chapter 3 */ #include < linux/kernel.h > #include < linux/sched.h > #include < linux/module.h > int init_module(void) { struct task_struct *task; for(task=current;task!=&init_task;task=task->parent) //current is a macro which points to the current task / process { printk("%s [%d]\n",task->comm , task->pid); } return 0; } void cleanup_module(void) { printk(KERN_INFO "Cleaning up 1.\n"); }
Bene, abbiamo appena iniziato nella conoscenza di una delle astrazioni fondamentali di un sistema linux — il processo. In un (possibile) futuro verra' ampliato, daremo uno sguardo ad altri aspetti.
'Till then, Happy hacking!
Altre risorse:
- The Linux Kernel Module Programming Guide for 2.6.x kernels
- Makefile usato per compilare i moduli del kernel:
obj-m +=ProcessList.o obj-m +=traceinit.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Talkback: Discuss this article with The Answer Gang
L'autore e' al 3 anno di Computer Engineering Undergraduate in Haldia Institute of Technology, Haldia. Nei suoi interessi include Protocolli di rete, Network Security, Sistemi Operativi, e Microprocessori. E' un fan di Linux e amante hacker del kernel Linux.