3. Creation and Execution of Process 1 85
3.1.4 Set the Page Management of Process 1
The paging mechanism in the architecture of Intel 80x86 is based on the protection mode, which means pe should be opened before opening pg. Since the protection mode is based on segment, setting the segments of process 1 should be done before setting the page management.
The page that task_struct of process 1 resides
ROM BIOS and VGA
Kernel
Enable interrupt
0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF
task_struct of process 1
The page that task_struct of process 1 resides
Process status
Adjust every management number of process 1
Process 0 Process 1
Uninterruptible Ready
Current process
After copying task_struct of process 1 Before copying task_struct of process 1
Figure 3.6 Adjusting the task_struct of process 1.
Generally speaking, each process has to load its own code and data, of which the addressing mode is the algorithm of segment address plus offset, namely, the logical address. The hardware will automatically convert the logical address to the linear address, which will be converted automatically again to the physical address in pages, according to the settings of the page directory table and page table. Through this technical route, the OS sets the code and data segment in the 64 MB linear address space of process 1 and then in the page directory table and page table.
3.1.4.1 Set the Code Segment and Data Segment in the Linear Address Space of Process 1
By calling copy_mem(), the system sets the segment base address and segment limit length of the segment and data segment of process 1 at first. Extract information on the code segment, data segment, and segment limit length of the current process (process 0);
meanwhile, set the base address of the segment and data segment of process 1, which is its process number, nr*64MB, and set the base address in the segment descriptor in the local descriptor tables of the new process. It is shown as the first step in Figure 3.7.
The code is as follows:
//Code path:kernel/fork.c:
int copy_process(int nr,long ebp,long edi,long esi,long gs,long none, long ebx,long ecx,long edx,
long fs,long es,long ds, //
long eip,long cs,long eflags,long esp,long ss) {
……
if (last_task_used_math == current)
__asm__(“clts ; fnsave%0”::“m” (p->tss.i387));
if (copy_mem(nr,p)) { //set code segment and data segment of child process, //create and copy the first page
//table of child process
task[nr] = NULL; //now this situation doesn’t happen free_page((long) p);
return -EAGAIN;
}
for (i = 0; i<NR_OPEN;i++) //reference count of file corresponding to parent //process is plused by 1
//showing that parent and child process share the //same file
……
}
//Code path:include/linux/sched.h:
……
#define _set_base(addr,base) \ //set addr by base.Referring to 2.9.1 __asm__(“movw%%dx,%0\n\t” \
“rorl $16,%%edx\n\t” \
“movb%%dl,%1\n\t” \
“movb%%dh,%2” \ ::“m” (*((addr)+2)), \
“m” (*((addr)+4)), \
“m” (*((addr)+7)), \
“d” (base) \ :“dx”)
……
#define set_base(ldt,base) _set_base(((char *)&(ldt)), base)
……
#define _get_base(addr) ({\ //obtain base address addr. Referring to _set_base //and 2.9.1
unsigned long __base; \ __asm__(“movb%3,%%dh\n\t” \
“movb%2,%%dl\n\t” \
“shll $16,%%edx\n\t” \
“movw%1,%%dx” \ :“=d” (__base) \ :“m” (*((addr)+2)), \
“m” (*((addr)+4)), \
“m” (*((addr)+7))); \ __base;})
Kernel
Enable interrupt
0 64M 128M 192M 256M
Step 1:
set base address of code segment and data segment
Step 2: copy page table Step 3: set page directory item
CPU 4G-1
Segment limit: 640 K
0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF
16 0 159 1023
0 159 1023
task_struct of process 1
The page that task_struct of process 1 resides ROM BIOS
and VGA
Page table Page table Page table
Page table
Page table 1 page directory table
4 page tables
Page table of process 0:
the first 160 items valid
Step 4:
flush TLB cache
Process status
Process 0 Process 1
Uninterruptible Ready
Before setting page directory and page table of process 1 After setting page directory and page table of process 1 Current process
Pagedirectory Page table
0 159 1023
Figure 3.7 Set the linear address space of process 1.
3.1.4.2 Create the First Page Table for Process 1 and Set the Corresponding Page Directory Entry
In Linux 0.11, when the program code of every process is executed, its address needs to be specified based on its linear address and it needs to be mapped to the physical memory.
As shown in Figure 3.8, the linear address is 32 bits and the CPU parses this address into
#define get_base(ldt) _get_base(((char *)&(ldt)))
#define get_limit(segment) ({\
unsigned long __limit; \
__asm__(“lsll%1,%0\n\t //obtain segment limit length and pass it to __limit incl%0”
:“=r” (__limit) :“r” (segment)); \ __limit;})
//Code path:kernel/fork.c:
int copy_mem(int nr,struct task_struct * p)//set code segment and data segment of child //process, create and copy the first table of // child process
{
unsigned long old_data_base,new_data_base,data_limit;
unsigned long old_code_base,new_code_base,code_limit;
//obtain limit length of code and data segment of child process
code_limit = get_limit(0x0f); //0x0f is 1111:code segment, ldt, 3 privilege level data_limit = get_limit(0x17); //0x17 is 10111:data segment, ldt, 3 privilege level //obtain base address of code and data segment of parent process(Process 0) old_code_base = get_base(current->ldt[1]);
old_data_base = get_base(current->ldt[2]);
if (old_data_base ! = old_code_base)
panic(“We don’t support separate I&D”);
if (data_limit < code_limit) panic(“Bad data_limit”);
new_data_base = new_code_base = nr * 0x4000000; //now nr is 1, 0x4000000 means 64MB p->start_code = new_code_base;
set_base(p->ldt[1],new_code_base); //set base address of code segment of child //process
set_base(p->ldt[2],new_data_base); //set base address of data segment of child //process
if (copy_page_tables(old_data_base,new_data_base,data_limit)) { free_page_tables(new_data_base,data_limit);
return -ENOMEM;
} return 0;
}
Linear address Page directory item
Page directory item
Page table item
Page table item
CR3
Page directory table Page table Page
The physical address mapped
31 22 21 12 11 0
Page offset
Figure 3.8 Mapping process from linear address to physical address.
three parts: page directory entry, page table entry, and offset within page. The page direc- tory entry is in the page directory table and is used to manage the page table. The page table entry is in the page table and is used to manage the page and ultimately find the spec- ified address in the physical memory. There is only one page directory table in Linux 0.11.
We can find the corresponding page directory entry in the page directory table based on the data “page directory entry” provided by the linear address. Then, find the correspond- ing page table entry in the page table according to the data “page table entry” provided by the linear address. Then, find the corresponding physical page according to this page table item. Finally, find the actual value of the physical address based on the data “offset within page” provided by the linear address.
Calling the function copy_page_tables, setting the page directory table, and copying the page table are as shown in the second and third steps in Figure 3.7. The position where the page directory entry lies should be noticed.
The code is as follows:
Entering the function copy_page_tables, apply a free page for the new page table at first and copy the first 160 page table items in the first page table of process 0 to this page (1 page table entry manages a memory space of about 4 KB of a page, and 160 page table items manage a memory space of about 640 KB). Then, both process 0 and process 1 point to the same page, meaning that process 1 can manage the page of process 0. After that, set the page directory table of process 1. Last, refresh page transforming cache by resetting CR3. The setting of the page table and page directory table of process 1 is completed.
The code is as follows:
//Code path:kernel/fork.c:
int copy_mem(int nr,struct task_struct * p) {
……
set_base(p->ldt[1],new_code_base); //set base address of code segment of child //process
set_base(p->ldt[2],new_data_base); //set base address of data segment of child //process
//create first page table and copy page table of Process 0 for Process 1. Set page //directory entry of Process 1
if (copy_page_tables(old_data_base,new_data_base,data_limit)) { free_page_tables(new_data_base,data_limit);
return -ENOMEM;
} return 0;
}
//Code path:mm/memory.c:
……
#define invalidate() \
__asm__(“movl%%eax,%%cr3”::“a” (0)) //reset CR3 as 0
……
int copy_page_tables(unsigned long from,unsigned long to,long size) {
unsigned long * from_page_table;
unsigned long * to_page_table;
unsigned long this_page;
Now, process 1 is empty, and its page tables are copied from process 0. Hence, they have the same page and share the same managing structure of memory page temporarily, as shown in Figure 3.9.
When process 1 has its programs, the relation will be ceased and process 1 organizes its managing structure of memory.