Вы находитесь на странице: 1из 38

Linux Processes

CNS 4510

Processes

When created is almost identical to parent process


Copy of parents address space Executes same code as parent Separate copies of stack and heap

Threads

Modern Unix systems support multithreaded applications In modern Unix systems a process is a collection of threads Each thread represents an execution flow of the process Most multi-threaded application are written using Posix threads

Threads

Older versions of the Linux kernel offered no support for multithreaded applications. From the kernel point of view a multi threaded application was simply a process Threads were just in user space

Threads

Consider a chess program


One controlling a graphical chess board And waiting for user input Another computing the computers next move

If the chess program is just one process, the first thread cannot simply issue a blocking system call Why?

Lightweight Processes

Linux uses lightweight processes to offer better support for multithreaded applications Two lightweight processes may share some resources A good way to implement multithreaded applications is to associate a lightweight process to each thread Two examples of POSIX-compliant pthread libraries
LinuxThreads IBMs Next Generation Posix Threading Package (NGPT)

Process Descriptor

Stores needed information for each process Implemented through the task_struct structure

Process State

There are 5 states a process can be in


Task_Running Task_Interruptible

Task_uninterruptible

Sleeping until some condition is true device driver probing for a certain hardware state Signals to this process leaves it unchanged

Kernel uses set_task_state and set_current_state macros to set state.

Task_Stopped Task_Zombie

Process Identification

Each process has a unique pid However, most of the time a process is referred to by its PDP
(Process Descriptor pointer)

Max Pid is 32,767

Thread Groups

Unix programmers expect threads in the same group to have a common PID Linux 2.4 has the notion of a thread group All descriptors in the same group are collected in a linked list implemented through the thread_group field of the task_struct The shared PID is the PID for the first thread. It is stored in the tgid field getpid( ) returns current->tgid if process is part of a thread group

Handling process descriptors

Linux stores two different data structures in a single 8kb memory area
Process Kernel mode stack Process Descriptor

esp register
CPU stack pointer stack grows up (towards low memory)

Task Union
union task_union {
struct task_struct task; unsigned long stack[2048];

};

The current macro

Can obtain the process descriptor pointer from the value of the esp register The current macro masks out the 13 least significant bits of esp to obtain the address of the process descriptor Dont need a separate current process pointer

The process list

Kernel has several lists of process


The prev and next pointers are part of the process descriptors structure Process list is a circular doubly linked list. process 0 (or swapper) is the head of the list
for (p=&init_task;(p=p->next_task)!=&init_task;) contain pdps

#define for_each_task(p)

The Run Queue

Kernel also contains a list of TASK_RUNNING processes the RUN_LIST variable contains the head and tail of this List. Convenient functions
add_to_runqueue() delete_from_runqueue() move_first_runqueue() move_last_runqueue()

What should be included in the Wake_up_process() procedure

PID Hash table

Kernel also includes a hash table


pid -> pdp

Chaining is used to handle collisions Better than an array


why?

Parenthood Relationships

Process descriptor includes the following fields


p_opptr (Original parent)

p_pptr (parent)

points to 1 if parent no longer exists points to the current parent points to youngest child

p_cptr(child)

p_ysptr(younger sibling)

p_osptr (older sibling)

points to pdp of process created immediately after p by ps current parent

Process Organization

TASK_RUNNING
has its own list

TASK_STOPPED, TASK_ZOMBIE
no special structure

TASK_INTERRUPTIBLE, TASK_UNINTERRUPTIBLE
subdivided into many classes each of which corresponds to a specific event. wait queues

Wait Queues

Used for
interrupt handling process synchronization timing

Each wait-queue head includes a lock for the specific resource corresponding to the particular queue Only wake up one process in order to avoid the thundering herd problem

Handling wait queues

Useful functions
add_wait_queue( ) add_wait_queue_exclusive( ) remove_wait_queue() wait_queue_active()

is this queue empty? new wait queue

DELCLARE_WAIT_QUEUE_HEAD(name)

Wait_queue_t
struct __wait_queue{ unsigned int flags; struct task_struct * task; struct list_head task_list; }; typedef struct __wait_queue wait_queue_t; Elements of a wait queue list are of type wait_queue_t Each element in the wait queue list represents a sleeping process.

__wait_queue_head
struct __wait_queue_head { spinlock_t lock; struct list_head task_list; }; typedef struct __wait_queue_head wait_queue_head_t;

sleep_on( ) function
void sleep_on(wait_queue_head_t *q) {
unsigned long flags; wait_queue_t wait; wait.flags = 0; wait.task = current; current->state = TASK_UNINTERRUPTIBLE; add_wait_queue(q,&wait); schedule(); remove_wait_queue(q,&wait);

More wait functions


wait_event_interruptible(wq, condition) { wait_queue_t __wait; init_waitqueue_entry(&__wait, current); add_wait_queue(&wq, &__wait); for(;;) { set_current_state(TASK_INTERRUPTIBLE); if(condition) break; schedule();

} current->state = TASK_RUNNING; remove_wait_queue(&wq, &__wait);

Even more wait functions


void wake_up(wait_queue_head_t *q) {
struct list_head *tmp; wait_queue_t *curr; list_for_each(tmp, &q->task_list){
curr=list_entry(tmp, wait_queue_t, task_list); wake_up_process(curr->task); if(curr->flags) break;

Process Resource Limits

RLIMIT_AS

RLIMIT_CORE RLIMIT_CPU

max address space max coredump size max cputime for the process in seconds max heap size

RLIMIT_DATA
max file size

RLIMIT_FSIZE

RLIMIT_LOCKS

max # of locks

Process Resource Limits

RLIMIT_MEMLOCK
RLIMIT_NOFILE

max non-swappable memory max open file descriptors number of child processes max # page frames max stack size

RLIMIT_NPROC
RLIMIT_RSS

RLIMIT_STACK

Process Switch

Hardware context
Task State Segment (TSS) Needs TSS because
value of all registers and flags for process specific segment type to store hardware contexts switches from user mode to kernel mode fetch the address of kernel mode stack from TSS Access of IO port requires access of IO permission bitmap stored in TSS on process switch the hardware context of the process being replaced is saved Cant be saved on the TSS cuz dont know when process will wake up and which CPU will wake it. each process descriptor has a thread field which stores the hardware context

Thread Field

Performing the context switch

Two main steps


Switching the page global directory to install a new address space switching the kernel mode stack and hardware context

Two pointers in schedule() function


prev (process being saved) next (process being activated)

switch_to macro

1. save the values of prev and next in eax and edx registers 2. save another copy of prev in ebx 3. save the contents of esi, edi, and ebp registers in the prev kernel mode stack 4. save content of esp in prev->thread.esp 5. load next->thread.esp in esp
6. Save current address in prev->thread.eip 7. push next->thread.eip to next kernel stack 8. jumps to the switch_to( ) function
From now on we are use nexts kernel mode stack

takes the value of prev and next from registers not from stack

The __switch_to function

save contents of FPU, MMX, and XMM registers loads next->esp0 to TSS
Stores fs and gs segmentation registers in prev loads fs and gs segment registers from next loads the six debug registers from next Updates the IO bitmap in the TSS Terminates
Issues a ret value was previously pushed by the switch to macro privilege level of next process

Creating Processes

Much time is wasted when a fork() call is issued to copy the parent context to the child process This problem is solved by

copy on write allows both parent and child to read the same physical pages` Lightweight process allow both the parent and child to share many process kernel data structures vfork() command creates a process that shares the memory address space of its parent

parents execution is blocked until child exits

do_fork()

when either a clone(), fork() or vfork() is called the kernel invokes do_fork() does several things including:

get space for process descriptor get parent processs info check resources to see if process will fit update descriptor fields in child that are different than parents copys the parents file descriptors etc. initialize the kernel thread of the child process if process is lightweight inserts process into thread group calls hash_pid to put process in hash table put process in processes list set status to task_running suspends parent process if necessary returns pid of child

Kernel Threads

Each kernel thread executes a single specific kernel C function


regular processes execute kernel functions only through system calls

Kernel threads run only in Kernel Mode


regular processes execute in user mode or in kernel mode

They use only linear addresses greater than PAGE_OFFSET


regular processes use the whole address space

Creating a Kernel thread


int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) { p = clone(0, flags | CLONE_VM); if ( p ) // parent return p; else { // child fn(arg); exit(); }

Some special processes

Process 0
swapper process started from scratch in start_kernel() runs cpu_idle( ) when no other processes are running

Process 1
init process shares all per-process kernel data with process 0 executes the init() function which continues initialization of the kernel

Destroying processes

handled by do_exit() function performs the following

sets the PF_EXITING flag in process descriptor removes the process from any wait queues closes any open resources

sets the exit_code field in the process descriptor to process termination code calls exit_notify() to notify parent of childs demise calls schedule( )

files, signal handles etc.

Removing a process

Parents often create children to perform certain tasks, and know the task is complete by the exit code given by the child the ZOMBIE status is given to those process that are terminated but not yet removed to give time for the process to notify the parent. If a process is terminated before its children, the children get process 1 as their parent.

Вам также может понравиться