Lecture 05
Lecture 05
Linux Processes
• Linux also refers to a process as a “task”
• Linux represents each process as a process
descriptor of type task_struct
– Contains all information related to a single process
– Not all information is contained directly in the
task_struct, instead it includes pointers to other data
structures, which may point to other data structures, and so
on
• Each process has its own process descriptor
– Because of this strict one-to-one relationship, process
descriptor addresses uniquely identify process (process
descriptor pointer)
1
Linux Process Descriptor
state
tty_struct
flags
next_task fs_struct
prev_task
next_run current directory
prev_run
p_optr
files_struct
p_pptr
...
pointers to file descriptors
tty
mm_struct
tss
pointers to memory area descriptors
fs
files signal_struct
mm
signal_lock signals received
sig
...
2
Linux Task Array
• All process descriptors are contained a global task
array in kernel address space, called task
– The elements of the task array are pointers to process
descriptors; null indicates an unused entry
– As a result using an array of pointers, process descriptors are
stored in dynamic memory rather than permanent kernel
memory
• Each task array entry actually contains two
different data structures in a single 8 KB block for
each process
– A process descriptor and the kernel mode stack
– These are cached after use to save allocation costs
%esp 0x015fa878
0x015fa3cb
process descriptor
current macro 0x015fa000
union task_union {
struct task_struct task;
unsigned long stack[2048];
};
3
Linux Task Array Entry
• The pairing of the processor descriptor and the kernel
mode stack offers some benefits
– The kernel can easily obtain the process descriptor pointer of
the currently executing process from the value of the %esp
register
• The memory block is 8 KB or 213 bytes long, so all the kernel has
to do is mask out the least significant 13 bits of %esp to get the
process descriptor pointer, this is done by the current macro
• You might see the macro used inline like, current->pid
– The pairing is also beneficial when using multiple processors
since the current process for each is determined similarly
4
Linux Process Lists
• Running list
– The OS often looks for a new process to run on the CPU
– It is possible to scan the entire process list for processes in
the TASK_RUNNING state, but this is inefficient
– The OS maintains a runqueue of all TASK_RUNNING
processes
– This list is a circular doubly linked list like the process list
and has the init_task process descriptor as its head also
– add_to_runqueue()/del_from_runqueue()
modify the list
– wake_up_process() makes a process runnable
5
Linux Process Lists
• List of free task entries
– The task array entries are used and freed every time a
process is created or destroyed
– A list of free task array entries is maintained for efficiency
starting with the tarray_freelist variable
– Each free entry in the task array points to another free
entry, while the last entry points to null
– Destroying a process puts its entry at the head of the list
– Each process descriptor also contains a pointer to its entry in
the task array to make deletion more efficient
descriptor
tarray_freelist descriptor
6
Linux Process Lists
• Parent/child relationships
– Each process descriptor maintains a pointer to its parent,
sibling, and child process descriptors
• p_opptr (original parent) points to the creating process or
the init process (process 1) if the parent has terminated
• p_pptr (parent) coincides with p_opptr except in some
cases, such as when another process is monitoring the child
process
• p_cptr (child) points to the process’ youngest child
• p_ysptr (younger sibling) points to the process’ next
younger sibling
• p_osptr (older sibling) points to the process’ next older
sibling
P0
P0 P0 P0
p_pptr
p_cptr
p_ysptr P0
p_osptr
7
Linux Process Lists
• The runqueue groups processes in state TASK_RUNNING
• Processes in state TASK_STOPPED or TASK_ZOMBIE
are not linked in specific lists since there is no need
• Processes in TASK_INTERRUPTIBLE and
TASK_UNINTERRUPTIBLE are divided into many
classes of list, these lists are wait queues
struct wait_queue {
struct task_struct *task;
struct wait_queue *next;
};
8
Linux Process Lists
• Wait queues are somewhat complex, because they use
a dummy pointer for efficiency
dummy pointer
task field
next field
P1
P1 P1
9
Linux Process Usage Limits
• All processes have an associated set of usage limits
• Linux recognizes the following limits
– RLIMIT_CPU, RLIMIT_FSIZE, RLIMIT_DATA,
RLIMIT_STACK, RLIMIT_CORE, RLIMIT_RSS (page
frames), RLIMIT_NPROC, RLIMIT_NOFILE,
RLIMIT_MEMLOCK, and RLIMIT_AS (address space)
• Process limits are stored in the rlim field of the process
descriptor; rlim is an array of rlimit
struct rlimit {
long rlim_cur;
long rlim_max;
};
– To check a limit, current->rlim[RLIMIT_CPU].rlim_cur
– Most limits are set to RLIMIT_INFINITY
10
Linux Process Creation
• Linux creates lightweight processes using the
__clone() function
– Is actually a wrapper for a hidden clone() function
– It takes four parameters, a function to execute, an argument
pointer, sharing flags, and the child stack
– Both fork() and vfork() are implemented in Linux using
clone() using different parameters
11
Linux Process Creation
• Kernel threads
– kernel_thread() is used to create a kernel thread
int kernel_thread(int (*fn)(void *), void *arg,
unsigned long flags)
{
pid_t p;
p = clone(0, flags | CLONE_VM);
if (p)
return p;
else {
fn(arg);
exit();
}
}
12
Linux Process Creation
• Process 1 (init process)
– The init process initially shares all per-process data
structures with the swapper process
– The init process, once scheduled, starts executing the
init() function
– init() process creates four more kernel threads to flush
dirty disk buffers, swap out pages, and reclaim memory
– Then init() invokes execve() to load the executable
init program; at this point the init process becomes a regular
process
– The init process never terminates
13
Linux Process Switching
• Hardware context
– Linux must save/reload CPU registers when switching processes
– Some information is stored in the kernel mode stack, other
information is stored in the Task State Segment (TSS)
• Hardware support
– The Intel 80x86 architecture includes the TSS used specifically to
store the hardware context
• Linux code
– The switch_to macro actually performs the process switch
• Saving floating point registers
– There is hardware support to lazily save floating point registers,
i.e., they are only saved when necessary
14