250 likes | 385 Views
Linux kernel internals. Introduction to process descriptors. 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.
E N D
Linux kernel internals Introduction to process descriptors
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: 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 user-mode stack grows downward from the highest address in user space (i.e., 0xBFFFFFFF) • 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 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 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 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 Room here for stack to expand as needed Kernel-mode stack 8K Process descriptor
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 8KB to these combined objects • Task’s process descriptor is 1696 bytes • So kernel stack can grow to about 6.5KB 8192 bytes – 1696 bytes = 6496 bytes • Each ‘task_union’ object is ‘8KB-aligned’
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 %esp, %ebx andl $0xFFFFE000, %ebx (Now %ebx = descriptor’s base-address)
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” (~0x1FFF) ); return current; } • #define current get_current()
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, 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 P1 P2 P3 P4 P5 See “Linux Kernel Programming” (Chapter 3) for additional details
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) newest task … prev_task
Tasks have ’states’ • From kernel-header: <linux/sched.h> • #define TASK_RUNNING 0 • #define TASK_INTERRUPTIBLE 1 • #define TASK_UNINTERRUPTIBLE 2 • #define TASK_ZOMBIE 4 • #define TASK_STOPPED 8