Do obsługi wątków w jądrze od wersji 2.6 stosuje się implementację NPTL (Native POSIX Thread Library). Zgodnie ze standardem POSIX (man pthreads) z punktu widzenia użytkownika istnieje rozróżnienie pomiędzy procesem a wątkiem - jeden proces wykonujący program może składać się z wielu wątków. Każdy proces posiada swój unikatowy numer PID (Process IDentifier), który jest jednak wspólny dla wszystkich jego wątków. Wątki z kolei posiadają unikatowe numery TID (Thread IDentifier). Funkcjonuje również pojęcie grup wątków posiadających identyfikatory TGID (Thread Group IDentifier). W NPTL wszystkie wątki należące do jednego procesu należą też do tej samej grupy wątków.
Numery PID i TID można uzyskać za pomocą funkcji systemowych getpid() i gettid():
#include <stdio.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <pthread.h>
void* thread(void *arg){
printf("child thread: getpid = %ld gettid = %ld\n", syscall(SYS_getpid), syscall(SYS_gettid));
sleep(20);
return NULL;
}
int main(){
pthread_t threads[2];
printf("main thread: getpid = %ld gettid = %ld\n", syscall(SYS_getpid), syscall(SYS_gettid));
pthread_create(&threads[0], NULL, thread, NULL);
pthread_create(&threads[1], NULL, thread, NULL);
pthread_join(threads[0], NULL);
pthread_join(threads[1], NULL);
return 0;
}
Powyższy program wypisze na przykład:
main thread: getpid = 6482 gettid = 6482
child thread: getpid = 6482 gettid = 6483
child thread: getpid = 6482 gettid = 6484
Wywołanie pstree -p również pokaże numerki TID:
└─thr(6482)─┬─{thr}(6483)
└─{thr}(6484)
Z drugiej strony, z punktu widzenia jądra Linuksa, zamiast mówić o procesach i wątkach mówi się o zadaniach, czyli "obiektach" reprezentowanych przez metryczkę procesu, czyli strukturę task_struct. Spośród wyżej wymienionych identyfikatorów, w strukturze task_struct znajdują się tylko dwa:
pid_t pid;
pid_t tgid;
Gdzie zatem zapisane są TID-y wątków? Okazuje się, że pole pid z task_struct nie jest tym samym co PID opisywany w standardzie POSIX, lecz unikatowym identyfikatorem każdego zadania w systemie. Nadaje się tym samym na bycie TID-em i wygląda na to, że tak w istocie jest.
Oto kod prostego modułu jądra, który iteruje po wszystkich procesach w systemie i ich procesach potomnych, oraz wyciąga ich pola pid i tgid:
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/smp_lock.h>
int init_module(void){
struct list_head *i;
struct task_struct *task, *act;
lock_kernel();
for_each_process(task){
printk(KERN_CRIT "name: %s, pid: %d, tgid: %d\n", task->comm, task->pid, task->tgid);
list_for_each(i, &task->children){
act = (struct task_struct*) list_entry(i, struct task_struct, sibling);
printk(KERN_CRIT "name: %s, pid: %d, tgid: %d\n", act->comm, act->pid, act->tgid);
}
}
unlock_kernel();
return 0;
}
void cleanup_module(void){
}
Załadowanie powyższego modułu podczas gdy w tle uruchomiony jest program podany na górze strony (o nazwie thr) powoduje wypisanie między innymi:
name: thr, pid: 6482, tgid: 6482
name: thr, pid: 6483, tgid: 6482
name: thr, pid: 6484, tgid: 6482
Pole pid pokrywa się z tym, co użytkownik może zobaczyć wywołując gettid(), natomiast pole tgid z tym co zwraca getpid().
Identyfikatory odczytane z task_struct są również zgodne z tym co można odczytać z /proc:
# ls /proc/6482/task
6482 6483 6484
# cat /proc/6482/task/6482/status
Name: thr
State: S (sleeping)
Tgid: 6482
Pid: 6482
...
# cat /proc/6482/task/6483/status
Name: thr
State: S (sleeping)
Tgid: 6482
Pid: 6483
...
# cat /proc/6482/task/6484/status
Name: thr
State: S (sleeping)
Tgid: 6482
Pid: 6484
...
(Programy uruchamiane były na jądrze 2.6.29.6)
Janina Mincer-Daszkiewicz |