Running and Loading of Process str1

Một phần của tài liệu The art of linux kernel design (Trang 335 - 340)

6.3 Complete Process of User Process from Creation to Exit

6.3.3 Running and Loading of Process str1

Generation of interrupt and OSs responding. After the generation of page fault inter- ruption, the response will be by the page_fault service. Eventually, the interruption will be processed by page fault handler _do_no_page by calling _do_no_page in _page_fault.

The code is as follows:

After entering the do_no_page() function, before loading str1, two detections shall be made. First is whether str1 has loaded its code and whether the linear address value caused page fault is out of the end of the code. Obviously, neither condition is true, so the code of str1 will be loaded from the hard disk.

The code is as follows:

Second, str1 possibly shares code with a current process, for example, has any other process already loaded str1? This is also obviously impossible in this case.

The code is as follows:

//code path: mm/page.s:

_page_fault:

testl $1,%eax jne 1f

1: call _do_no_page

……

//code path: mm/memory.c:

void do_no_page(unsigned long error_code,unsigned long address) {

……

address &= 0xfffff000;

tmp = address - current->start_code;

if (!current->executable || tmp>= current->end_data){ //executable is the i node of //str1’s code file

//end_data is the end of code get_empty_page(address);

return;

}

if (share_page(tmp)) return;

……

}

//code path: mm/memory.c:

void do_no_page(unsigned long error_code,unsigned long address) {

……

if (!current->executable || tmp > = current->end_data) { get_empty_page(address);

return;

}

if (share_page(tmp)) //detect whether it is sharing pages with other process return;

if (!(page = get_free_page())) oom();

……

}

The current situation is the same with the situation when we load shell, and the pro- cess needed to be loaded from the hard disk. The following comes allocating free pages in memory and load str1.

Allocate a memory page for str1. Allocate a free page in main memory, and load the first part of str1 to the allocated page. The code is as follows:

The procedure of allocating a free page and the registration of management structure mem_map is shown in Figure 6.14.

As we can infer from the former introduction, all pages allocated to the process have two mapping relationships, one is mapped to the kernel’s linear address space, and another is mapped to the process’ linear address space. The mapping relationship

ROM BIOS and VGA

Kernel

0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF

Kernel code area Kernel data area

Free page

mem_map[]

1 MB 6 MB 16 MB

Increase the reference count of page Figure 6.14 Allocate a memory page for str1.

//code path: mm/memory.c:

void do_no_page(unsigned long error_code,unsigned long address) {

……

if (share_page(tmp)) return;

if (!(page = get_free_page())) //allocate page for str1

oom(); //if the allocation fails, let str1 exit /* remember that 1 block is used for header */

block = 1 + tmp/BLOCK_SIZE;

……

}

between the kernel and the pages always exists. Consider the pages’ mapping to kernel is cut off after the pages are mapped to process, it means that the kernel will not be able to access these pages.

Loading the program of str1 to newly allocated pages. Now the program is loaded into the newly allocated pages from the hard disk, 4 KB content per loop.

The code is as follows:

In the above code, the function bmap() has been introduced in Chapter 5, Section 5.5.

The procedure of function bread_page() is same with bread() in essence.

The procedure is shown in Figure 6.15.

Because the page has already mapped to the kernel’s linear address space, when data are loaded in, they can be modified by the kernel at any time needed. This also suitable for the data loaded in the future.

Kernel

0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF

Free page

Load part of str1 program to the free page that applied Disk

ROM BIOS and VGA

Figure 6.15 Loading the initial part for process str1 to newly allocated page.

//code path: mm/memory.c:

void do_no_page(unsigned long error_code,unsigned long address) {

……

if (!(page = get_free_page())) oom();

/* remember that 1 block is used for header */

block = 1 + tmp/BLOCK_SIZE;

for (i = 0 ; i<4 ; block++,i++)

nr[i] = bmap(current->executable,block);

bread_page(page,current->executable->i_dev,nr); //read str1’s information //from hard disk

i = tmp + 4096 - current->end_data;

tmp = page + 4096;

……

}

Mapping the physical memory address process of str1 to its linear address space.

After str1 is loaded, we shall map it to str1’s linear address space.

The specified code is as follows:

The mapping procedure is illustrated in Figure 6.16; please notice that the correspon- dent page directory entry is set up in this procedure.

Only after the mapping, process will be able to execute the loaded code.

Loading complete content of str1 by repeating page fault. Given that the program is larger than the size of a page, when other parts of the program are needed during exe- cution, page fault will be invoked again and again in order to load the required program.

Until now, the loading process of str1 has been completed. Next, we are going to intro- duce the situation after str1 is executing.

The program str1 begins to push stack. Once the program begins to execute, push- ing stack actions begin.

Kernel

ROM BIOS and VGA

0x00000 0x9FFFF 0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF

Page directory table The new page table

of str1 process The page that the content of str1 process resides

0 16 32 48 64 0 159 1023

Set page directory item Linear address

corresponds to physical address

0 256 MB 320 MB 4 GB-1

Linear address space Figure 6.16 Mapping str1’s physical address to its linear address.

//code path: mm/memory.c:

void do_no_page(unsigned long error_code,unsigned long address) {

……

while (i— — > 0) { tmp— —;

*(char *)tmp = 0;

}

if (put_page(page,address)) //mapping to str1’s linear address space return;

free_page(page);

oom();

}

The foo function in the str1 program is called recursive. In this case, we set up a char array, namely “text,” with the length of 2048 bytes to accelerate the increasing speed of str1’s stack space. Therefore, page fault will be invoked more quickly (after only two stack pushing, a page fault will be invoked). Each time the foo function is called, the stack of str1 (ESP) increases by 2048 bytes.

Stack pushing when process str1 calls foo for the first time. The first time foo func- tion is called, ESP increases by 2048 bytes. Before increasing, the stack ESP pointed to has taken up some place in the page to store the process’s parameters and environment variables. So after expanding 2048 bytes, adding to the space already in use, the ESP is still within 4 KB—the size of a page. In other words, the program is still within the capacity of one physical page. As illustrated in Figure 6.17, the more black part in the lower right corner stands for parameters and environmental variables already in the page, and we can see that they are contained in the same page with part of the stack data.

Page fault invoked when str1 pushing stack for the second time. The second time that function foo is called, it will be a different case. Add with the data already in the physical page, another 2048 bytes will exceed the capacity of the page. When MMU is mapping the linear address value, the P-bit of a new page table entry is 0, and page fault is invoked again, making preparations for allocating a new page.

Handling the page fault invoked in the second stack pushing. A new physical page will be mapped to str1’s linear address space eventually in order to support addressing. The function do_no_page is again called when handling the page fault this time, but the code to execute will be different, and the following code will be executed:

Kernel

0x00000 0x9FFFF

ROM BIOS and VGA

0xFFFFF 0x3FFFFF 0x5FFFFF 0xFFFFFF

ESP pointer Figure 6.17 The first time str1 pushing stack.

//code path: mm/memory.c:

void do_no_page(unsigned long error_code,unsigned long address) {

……

address & = 0xfffff000;

tmp = address - current->start_code;

if (!current->executable || tmp > = current->end_data) {//this conditions is true get_empty_page(address); //allocate free space when pushing stack return;

} ……

}

This is because the condition tmp > = current->end_data becomes true this time.

The program is executing to a linear address that exceeds the end_data of the process.

Therefore, call function get_empty_page(), and no extra data are loaded into the page this time. Pushing stack also invokes page fault, having nothing with peripheral.

After entering get_empty_page(), the required page will be allocated and mapped to the linear address space of process str1.

The code is as follows:

Process str1 continues to process, repeating pushing stack, and invoking page fault. The process continues to run, and such procedure is repeated: “pushing stack → if the P-bit of the page table entry is 0 → invoke page fault → allocating physical memory → pushing stackããã”. When the function foo is called for the nth time, the mapping relation- ship between the user process stack and physical memory is shown in Figure 6.18. Please notice the memory page change of stack data.

Clear the stack after str1 is finished. After the program is finished, the recursion of function foo comes to an end (if (n = =0) return 0). At this time, the function return will lead to the clearing of the process stack. ESP shrinks up to the higher address direction, and the space for the user process to use is actually reduced. Thus, the for- mer physical page mapped to linear space in the stack should be freed. Nevertheless, in our analysis and test to the Linux 0.11 source code, such procedure is not actually conducted. The reason is as follows: When the process is working, the kernel is not at work, and the pages discarded by the process during its processing can’t be detected by the kernel in time. Besides, no circuit in the CPU is designed for such staff; there is no mechanism for discarded page detection. Even if the function is implemented by the kernel, there is no chance to carry out such a function. Thereby, pages are not freed after clearing the stack.

The result is shown in Figure 6.19.

Một phần của tài liệu The art of linux kernel design (Trang 335 - 340)

Tải bản đầy đủ (PDF)

(524 trang)