Memory-related work done by <#2013#> fork()<#2013#>:
- Memory allocation
- 1 page for the <#2016#> task_struct<#2016#>.
- 1 page for the kernel stack.
- 1 for the <#2017#> pg_dir<#2017#> and some for <#2018#> pg_table<#2018#>s
(copy_page_tables)
- Other changes
- <#2021#> ss0<#2021#> set to kernel stack segment (0x10) to be sure?
- <#2022#> esp0<#2022#> set to top of the newly allocated <#2023#> kernel_stack_page<#2023#>
- <#2024#> cr3<#2024#> set by <#2025#> copy_page_tables()<#2025#> to point to newly
allocated page directory.
- <#2026#> ldt = _LDT(task_nr)<#2026#> creates new ldt descriptor.
- descriptors set in gdt for new tss and <#2027#> ldt[]<#2027#>.
- The remaining registers are inherited from parent.
The processes end up sharing their code and data segments (although
they have separate local descriptor tables, the entries point
to the same segments). The stack and data pages will be copied when
the parent or child writes to them (copy-on-write).
Memory-related work done by <#2030#> exec()<#2030#>:
- memory allocation
- 1 page for exec header entire file for omagic
- 1 page or more for stack (MAX_ARG_PAGES)
- <#2034#> clear_page_tables()<#2034#> used to remove old pages.
- <#2035#> change_ldt()<#2035#> sets the descriptors in the new <#2036#> LDT[]<#2036#>
- <#2037#> ldt[1]<#2037#> = code base=0x00, limit=TASK_SIZE
- <#2038#> ldt[2]<#2038#> = data base=0x00, limit=TASK_SIZE
These segments are DPL=3, P=1, S=1, G=1. type=a (code) or 2 (data)
- Up to <#2039#> MAX_ARG_PAGES<#2039#> dirty pages of argv and envp are
allocated and stashed at the top of the data segment for the newly
created user stack.
- Set the instruction pointer of the caller <#2040#> eip = ex.a_entry<#2040#>
- Set the stack pointer of the caller to the stack just created
(esp = stack pointer)
These will be popped off the stack when the caller resumes.
- update memory limits
<#2041#> end_code = ex.a_text<#2041#>
<#2042#> end_data = end_code + ex.a_data<#2042#>
<#2043#> brk = end_data + ex.a_bss<#2043#>
Interrupts and traps are handled within the context of the current task.
In particular, the page directory of the current process is used in
address translation. The segments, however, are kernel segments so that all
linear addresses point into kernel memory. For example, assume a user
process invokes a system call and the kernel wants to access a
variable at address 0x01. The linear address is 0xc0000001 (using
kernel segments) and the physical address is 0x01. The later is
because the process' page directory maps this range exactly as <#2045#>
page_pg_dir<#2045#>.
The kernel space (0xc0000000 + <#2046#> high_memory<#2046#>) is mapped by the
kernel page tables which are themselves part of the RESERVED memory.
They are therefore shared by all processes. During a fork <#2047#>
copy_page_tables()<#2047#> treats RESERVED page tables differently. It sets
pointers in the process page directories to point to kernel page
tables and does not actually allocate new page tables as it does
normally. As an example the <#2048#> kernel_stack_page<#2048#> (which sits
somewhere in the kernel space) does not need an associated <#2049#>
page_table<#2049#> allocated in the process' <#2050#> pg_dir<#2050#> to map it.
The interrupt instruction sets the stack pointer and stack segment from
the privilege 0 values saved in the tss of the current task.
Note that the kernel stack is a really fragmented object~--- it's not
a single object, but rather a bunch of stack frames each allocated when a
process is created, and released when it exits. The kernel stack should
never grow so rapidly within a process context that it extends below the
current frame.