Linux kernel internals Introduction to process descriptors Upon

  • Slides: 18
Download presentation
Linux kernel internals Introduction to process descriptors

Linux kernel internals Introduction to process descriptors

Upon entering the kernel… A user process will enter ‘kernel-mode’: • When it decides

Upon entering the kernel… A user process will enter ‘kernel-mode’: • When it decides to execute a system-call • When it is interrupted (e. g. by the timer) • When ‘exception’ occurs (e. g. divide by 0)

Switch to kernel-mode stack • Entering kernel involves a stack-switch • Necessary for robustness:

Switch to kernel-mode stack • Entering kernel involves a stack-switch • Necessary for robustness: e. g. , user-mode stack might be exhausted • Desirable for security: e. g, illegal parameters might be supplied

Location of user-mode stack • Each task has a private user-mode stack • The

Location of user-mode stack • Each task has a private user-mode stack • The user-mode stack grows downward from the highest address in user space (i. e. , 0 x. BFFFFFFF) • A program’s exit-address is on user stack • Command-line arguments on user stack • Environment variables on user stack • Upon entering ‘main()’:

What’s on the user stack? Upon entering ‘main()’: • A program’s exit-address is on

What’s on the user stack? Upon entering ‘main()’: • A program’s exit-address is on user stack • Command-line arguments on user stack • Environment variables are on user stack During execution of ‘main()’: • Function parameters and return-addresses • Storage locations for ‘automatic’ variables

What’s on the kernel stack? Upon entering kernel-mode: • task’s registers are saved on

What’s on the kernel stack? Upon entering kernel-mode: • task’s registers are saved on kernel stack (e. g. , address of task’s user-mode stack) During execution of kernel functions: • Function parameters and return-addresses • Storage locations for ‘automatic’ variables

And also something else! • Linux uses part of a task’s kernel-stack to store

And also something else! • Linux uses part of a task’s kernel-stack to store that task’s ‘process descriptor’ • The stack and descriptor are overlayed: union task_union { unsigned long stack[ 2048 ]; struct task_struct task; };

Union of descriptor and stack Kernel-mode stack Room here for stack to expand as

Union of descriptor and stack Kernel-mode stack Room here for stack to expand as needed Process descriptor 8 K

The kernel is ‘task manager’ • So each task has a ‘process descriptor’: struct

The kernel is ‘task manager’ • So each task has a ‘process descriptor’: struct task_struct { volatile long state; unsigned long flags; int sigpending; mm_segment_t addr_limit; /* plus many other fields */ };

The ‘task_union’ object • • Linux stores stack with process descriptor It allocates 8

The ‘task_union’ object • • Linux stores stack with process descriptor It allocates 8 KB to these combined objects Task’s process descriptor is 1696 bytes So kernel stack can grow to about 6. 5 KB 8192 bytes – 1696 bytes = 6496 bytes • Each ‘task_union’ object is ‘ 8 KB-aligned’

Finding the descriptor info • During task-execution in kernel-mode: • It’s quick for a

Finding the descriptor info • During task-execution in kernel-mode: • It’s quick for a process to find its descriptor by using two assembly-language instructions: movl andl %esp, %ebx $0 x. FFFFE 000, %ebx (Now %ebx = descriptor’s base-address)

The ‘current’ process • Kernel-headers define useful macros • static inline struct task_struct *

The ‘current’ process • Kernel-headers define useful macros • static inline struct task_struct * get_current( void ) { struct task_struct *current; __asm__( “ andl %%esp, %0 ; “ : “=r” (current) : “ 0” (~0 x 1 FFF) ); return current; } • #define current get_current()

Parenthood • • • New tasks get created by calling ‘fork()’ Old tasks get

Parenthood • • • New tasks get created by calling ‘fork()’ Old tasks get terminated by calling ‘exit()’ When ‘fork()’ is called, two tasks return One task is known as the ‘parent’ process And the other is called the ‘child’ process The kernel keeps track of this relationship

A parent can have many children • If a user task calls ‘fork()’ twice,

A parent can have many children • If a user task calls ‘fork()’ twice, that will create two distinct ‘child’ processes • These children are called ‘siblings’ • Kernel keeps track of all this with pointers struct task_struct *p_ptr, // parent *p_cptr, // youngest child *p_ysptr, // younger sibling *p_osptr, // older sibling

Parenthood relationbships P 1 P 2 P 3 See “Linux Kernel Programming” (Chapter 3)

Parenthood relationbships P 1 P 2 P 3 See “Linux Kernel Programming” (Chapter 3) for additional details P 4 P 5

The kernel’s ‘process-list’ • • • Kernel keeps a list of process descriptors A

The kernel’s ‘process-list’ • • • Kernel keeps a list of process descriptors A ‘doubly-linked’ circular list is used The ‘init_task’ serves as a fixed header Other tasks inserted/deleted dynamically Tasks have forward & backward pointers: struct task_struct *next_task; struct task_struct *prev_task;

Doubly-linked circular list next_task init_task (pid=0) prev_task … newest task

Doubly-linked circular list next_task init_task (pid=0) prev_task … newest task

Tasks have ’states’ • From kernel-header: <linux/sched. h> • • • #define TASK_RUNNING #define

Tasks have ’states’ • From kernel-header: <linux/sched. h> • • • #define TASK_RUNNING #define TASK_INTERRUPTIBLE #define TASK_UNINTERRUPTIBLE #define TASK_ZOMBIE #define TASK_STOPPED 0 1 2 4 8