printf
ex3.c
:
int x;
int y;
void main(){
y=x+3;
printf("x:%p y:%p main:%p\n", &x, &y, main);
for(;;);
}
Compile:
$ gcc -o ex3 ex3.c
run:
$ ./ex3&
x:0x804a01c y:0x804a020 main:0x80483b4
[1] 4693
readelf -S ex3
: .text is code section, .data is initialized data section, .bss is uninitialized data section)example)
$ ./ex3 &
$ ps
PID TTY TIME CMD
4601 tty1 00:00:00 bash
4693 tty1 00:02:24 ex3
4694 tty1 00:00:00 ps
$ cat /proc/4693/maps
Start end perm offset dev inode file
08048000-08049000 r-xp 00000000 08:03 614956 /root/ex3
08049000-0804a000 r--p 00000000 08:03 614956 /root/ex3
0804a000-0804b000 rw-p 00001000 08:03 614956 /root/ex3
b7de7000-b7de8000 rw-p b7de7000 00:00 0
b7de8000-b7f12000 r-xp 00000000 08:03 113693 /lib/libc-2.6.1.so
b7f12000-b7f14000 r--p 0012a000 08:03 113693 /lib/libc-2.6.1.so
b7f14000-b7f15000 rw-p 0012c000 08:03 113693 /lib/libc-2.6.1.so
..........................
ex1.cpp
). What are the starting addresses of the code, data, heap, stack segment of this program and how many pages each segment occupies? What is the address of main function, the addresses of the global variables and local variables?ex1.cpp
:
#include <stdio.h>
int x;
int y[10000];
int main(){
int k;
int *pk;
pk=new int;
printf("ex1. &main:%p &x:%p &y:%p &y[9999]:%p &k:%p &pk:%p pk:%p\n", main,&x,&y,&y[9999],&k,&pk,pk);
for(;;); // to see memory map of this process
return 0;
}
printf
์ ์ถ๋ ฅ ๊ฒฐ๊ณผ๋ Logical ์ฃผ์๋ฅผ ์๋ฏธํ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ์ค์ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅ๋๋ ์์น๋ฅผ ์๊ธฐ ์ํด์๋ Process Table์ ์ด์ด Physical ์ฃผ์๋ฅผ ํ์ธํด์ผ ํ๋ค.
$ g++ -o ex1 ex1.cpp
$ ./ex1 &
$ cat /proc/(pid of ex1)/maps > x1
$ vi x1
Segment | Address | Identifier |
---|---|---|
Code | 08048000-08049000 | &main |
Read-Only Data | 08049000-0804a000 | ย |
Data | 0804a000-0804b000 | &x , &y |
Heap | 0804b000-08075000, b7e22000-b7e23000 | &y[9999] , pk |
C Library Code | b7e23000-b7f4d000 | ย |
C Library Data | b7f4d000-b7f50000 | ย |
Library Loader | b7f58000-b7f74000 | ย |
Stack | bfc5f000-bfc74000 | &k , &pk |
ex2.cpp
(see below), and run ex2
, ex1
at the same time. Confirm they have the same address for main function. How can they run at the same location at the same time?ex2.cpp
:
#include <stdio.h>
int x1;
int main(){
int *pk1;
pk1 = new int;
printf("ex2. &main:%p &x1:%p\n", main,&x1);
for(;;); // to see memory map of this process
return 0;
}
$ g++ -o ex2 ex2.cpp
$ ./ex2 &
...........
$ ./ex1 &
...........
$ ps
.............. ex2
.............. ex1
ex1๊ณผ ex2์ main ์ฃผ์๊ฐ 0x80484f4๋ก ๋๊ฐ์ด ์ถ๋ ฅ๋๋ค. ํ์ง๋ง ์ด๋ ์ปดํ์ผ๋ฌ๊ฐ ์์ฑํ logical address์ด๋ค.
ps
๋ช
๋ น์ด๋ก PID๋ฅผ ํ์ธํด๋ณด๋ฉด, ์ค์ memory์ load๋ physical address๋ ์๋ก ๋ค๋ฅด๋ค๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๋ฐ๋ผ์ ex1๊ณผ ex2๋ ๋์์ ์คํ์ด ๊ฐ๋ฅํ๋ค.
The goal is similar, and the basic solution is also similar. For a file, all information about a particular file is stored at the corresponding inode{}; for a process, all information is stored at the corresponding task_struct{}. For a file, the block location of this file is stored in inode->i_private->i_block[] table; for a process, the page location of this process is stored in task_struct->mm->pgd table.
example)
i
and j
, and the address of A[0]
, A[1]
, .., A[4]
.ex4.c
:
#include <stdio.h>
int A[5][1024];
int main()
{
int i, j;
printf("ex4. &main:%p &i:%p &j:%p\n", main, &i, &j);
for (i = 0; i < 5; i++)
{
printf(" A[%d][0]:%p\n", i, &A[i][0]);
printf("A[%d][1023]:%p\n", i, &A[i][1023]);
for (j = 0; j < 1024; j++)
A[i][j] = 3;
}
for (;;);
}
$ ./ex4 &
Segment | Page | Identifier |
---|---|---|
Code | 8048-8049 | &main |
Data | 804a-804b | &A |
Heap | 804b-8050 | &A[4][1024] |
Stack | bfc5d-bfc72 | &i , &j |
๊ธฐ๋ณธ์ ์ผ๋ก Page์ ํฌ๊ธฐ๋ 4KB์ด๊ธฐ ๋๋ฌธ์ โ/proc/pid/mapsโ์์ 0x1000๋น ํ Page๋ผ๊ณ ๋ณผ ์ ์๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์์๊ณผ ๋ ๋ฉ๋ชจ๋ฆฌ ์ฃผ์์์ ๋ค 3์๋ฆฌ๋ฅผ ์ ํ๋ฉด Page ๋ฒํธ๋ฅผ ๊ตฌํ ์ ์๋ค.
ํ๋ก๊ทธ๋จ์ Code(main
) -> Data(์ ์ญ๋ณ์) -> Heap(์ ์ญ๋ณ์) -> Stack(์ง์ญ๋ณ์) ์์ผ๋ก ์ ๊ทผํ๋ค.
x=open("/aa/bb", ...); // assume /aa/bb is at disk block 100, 101
lseek(x, 4098, SEEK_SET);
read(x, buf, 10);
y = x + 3; // assume x is at 0x804a040 (logical address) and stored at
// 0x7000040 (physical address)
struct vm_area_struct {
unsigned long vm_start, vm_end;
struct vm_area_struct *vm_next;
struct file * vm_file;
......
}
example of VMA list:
xx.c
:
#include <stdio.h>
#include <stdlib.h>
int i=1; int j=10; int k[10000];
int main(){
char * x;
j=i+5;
x = (char *)malloc(1024*1024);
printf("x: %p &k[0]:%p &k[9999]:%p &i:%p &j:%p\n", x, &k[0],&k[9999], &i, &j);
for(;;);
return 0;
}
Compile above to get an executable file xx
.
$ gcc -o xx xx.c
$ ./xx &
.................
[1] 23277
Get the process image of xx
.
$ cat /proc/23277/maps
08048000-08049000 r-xp 00000000 03:06 614954 /home/kchang/xx
08049000-0804a000 r--p 00000000 03:06 614954 /home/kchang/xx
0804a000-0804b000 rw-p 00000000 03:06 614954 /home/kchang/xx -- i, j, k[]
0804b000-08054000 rw-p 00001000 00:00 0 -- k[]
b7cc7000-b7dc9000 rw-p 0804b000 00:00 0 -- x[]
.........................
Now the VMA list is
Page Faults๋ ์์ง frame์ ํ ๋น๋์ง ์์ ์๋ก์ด page์ ์ ๊ทผํ ๋๋ง๋ค ๋ฐ์ํ๋ค. ๋ฐ๋ผ์ A[][]
๋ฅผ ์ ์ธ ์ 1๋ฒ, main
์ 1๋ฒ, i
&j
์ 1๋ฒ, loop ์์์ A[][]
์ ์ ๊ทผํ ๋ 5๋ฒ, ์ด 8๋ฒ์ Page Faults๊ฐ ๋ฐ์ํ๋ค.
my_sys_show_pfcnt()
, in mm/mmap.c
, which displays the number of page faults generated so far. The pfcnt
should be increased by one whenever there is a page fault. Remember a page fault will raise INT 14 which will be processed by page_fault()
in arch/x86/kernel/entry_32.S
, which in turn calls do_page_fault()
in arch/x86/mm/fault.c
. Define โint pfcnt=0
โ in this file and increase it inside do_page_fault()
. Now you call this system call before and after the double loop in hw 2) and see the difference.arch/x86/kernel/syscall_table_32.S
:
58๋ฒ์ my_sys_show_pfnt
๋ฅผ ํ ๋นํ์๋ค.
mm/mmap.c
:
......
extern int pfcnt;
void sys_show_pfcnt() {
printk("page fault count so far: %d\n", pfcnt);
}
......
arch/x86/mm/fault.c
:
ํ์ผ๋ค์ ์์ ํ์ ์ปดํ์ผ ๋ฐ ์ฌ๋ถํ ํ๋ค.
$ make bzImage
$ cp arch/x86/boot/bzImage /boot/bzImage
$ reboot
ex5.c
:
Page fault count๋ loop๊ฐ ๋๋ ํ, 5 ์ฆ๊ฐํ ๊ฒ์ ํ์ธํ ์ ์๋ค. ์ ์ญ๋ณ์๋ฅผ ๋ถ๋ฌ์ค๊ธฐ ์ํด A[0]
๊ฐ ์์นํ 804a Page๋ฅผ ์ ์ธํ๊ณ A[4][1024]
๊น์ง ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๊ธฐ ์ํด์๋ 804f๊น์ง์ 5๋ฒ์ Page Fault๊ฐ ํ์ํ๊ธฐ ๋๋ฌธ์ด๋ค.
ex3.c
and insert following code in arch/mm/fault.c
:do_page_fault()
. When you run ex3
, the kernel will display the page fault addresses generated by ex3
. Explain the result. Also confirm your answer in hw 3) again by examining page fault addresses.ex3.c
:
int x;
void main(){
x=3;
}
arch/x86/mm/fault.c
:
void do_page_fault(...........){
...........
/* get the address */
address = read_cr2();
if (strcmp(tsk->comm, "ex3")==0){
printk("pg fault for ex3 at:%p\n", address);
}
..............
ํ์ผ๋ค์ ์์ ํ์ ์ปดํ์ผ ๋ฐ ์ฌ๋ถํ ํ๋ค.
$ make bzImage
$ cp arch/x86/boot/bzImage /boot/bzImage
$ reboot
do_page_fault
ํจ์์ ํ์ฌ ์คํํ๋ ํ๋ก๊ทธ๋จ ์ด๋ฆ์ด โex3โ์ด๋ฉด Page Fault ์ฃผ์๋ฅผ ์ถ๋ ฅํ๋๋ก ์ปค๋ ์ฝ๋๋ฅผ ์์ ํ๋ค. ํ๋ก๊ทธ๋จ์ด ์์๋๋ฉด Code(804a) Page๋ฅผ ๋จผ์ ์ฝ๊ณ , libc ๋ฑ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ ์ฅ๋ ํ์ด์ง๋ฅผ ์ฝ๋๋ค. ๊ทธ๋ฆฌ๊ณ Heap ์์ญ์ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ฆฐ๋ค. ํ๋ก๊ทธ๋จ์ ์ฝ๋์ ๋นํด ๋ง์ Page Fault๊ฐ ์์ฑ๋ ์ด์ ๋ mainํจ์๊ฐ ์ด๊ธฐํ๋์ด ํธ์ถ๋๊ธฐ๊น์ง์ ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ์ด ํฌํจ๋์๊ธฐ ๋๋ฌธ์ด๋ค.
pfcnt
increased?ex6.c
:
#include <stdio.h>
int A[5][1024];
int main()
{
int i, j;
syscall(58);
for (i = 0; i < 5; i++)
{
printf("A[%d][0]:%p\n", i, &A[i][0]);
for (j = 0; j < 1024; j++)
A[i][j] = 3;
}
syscall(58);
for (;;);
}
5์์ 722483-722459=24๋ก ๊ฐ์ด ์ฆ๊ฐํ ์ด์ ๋ printf
ํธ์ถ์์ ์ถ๊ฐ์ ์ธ Page Fault๊ฐ ์ผ์ด๋ฌ๊ธฐ ๋๋ฌธ์ด๋ค.
sys_get_VMAlist
.
arch/x86/kernel/syscall_table_32.S
:sys_get_VMAlist()
in mm/mmap.c
(not arch/x86/mm/mmap.c
). This function will display all vmaโs of the current process.
ex7.c
that invokes system call 31
ํ์ผ๋ค์ ์์ ํ์ ์ปดํ์ผ ๋ฐ ์ฌ๋ถํ ํ๋ค.
$ make bzImage
$ cp arch/x86/boot/bzImage /boot/bzImage
$ reboot
VMA List๋ Linked List ํํ๋ก ๊ตฌํ๋์๊ธฐ ๋๋ฌธ์ temp = temp->vm_next
๋ก ๋ค์ ๊ฐ์ ์ง์ ํ๋ฉฐ NULL
์ด ๋ ๋๊น์ง ์ด๋ฅผ ๋ฐ๋ณตํ๋ค. vm_file
๊ฐ์ด NULL
์ผ ๊ฒฝ์ฐ์๋ name
์ ์ถ๋ ฅํ์ง ์๋๋ค.
์ถ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด vDSO๋ถ๋ถ์ ์ ์ธํ๊ณ โ/proc/ex7โs_pid/mapsโ์ ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฌ์ฃผ๋ ๊ฒ์ ํ์ธํ ์ ์์๋ค.
ex1
and ex2
by using sys_show_pfcnt()
. Explain the results. Also compare the running time of each code (use gettimeofday()
function) and explain why they differ. Run several times and compute the average.ex8.c
:
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
int A[8192][8192];
double getUnixTime()
{
struct timeval tv;
gettimeofday(&tv, (void *)NULL); // get current time
return (tv.tv_sec + tv.tv_usec / 1.0e6); // return it in seconds
}
int main()
{
int i, j;
double stime, etime, diff;
stime = getUnixTime(); // starting time
syscall(58); // display pfcnt
for (i = 0; i < 8192; i++)
for (j = 0; j < 8192; j++)
A[i][j] = 3;
syscall(58); // display pfcnt again
etime = getUnixTime(); // ending time
diff = etime - stime; // the difference
printf("The elapsed time: %f\n", diff);
return 0;
}
ex9.c
:
// same as `ex8.c` except
// change A[i][j]=3; to A[j][i]=3;
// (If your vm dies, reduce the array size)
Program | Page Fault | Elapsed Time |
---|---|---|
ex8 | 65,541 | 0.669330 |
ex9 | 74,125 | 1.765722 |
ex8
๊ณผ ex9
์ ์ฐจ์ด์ ์ A[i][j]
์ A[j][i]
์ด๋ค.
ex9
์์ ๋ ๋ง์ Page Fault๊ฐ ๋ฐ์ํ๋๋ฐ, ex8
์ A
์ ๋ฉ๋ชจ๋ฆฌ ์ ์ฌ๋ ์์๋๋ก ์ ๊ทผํ ๋ฐ๋ฉด, ex9
๋ ์์๋๋ก ์ ๊ทผํ์ง ์์๊ธฐ(์ฐธ์กฐ์ ์ง์ญ์ฑ์ ๋ง์กฑํ์ง ์์๊ธฐ) ๋๋ฌธ์ LRU ์๊ณ ๋ฆฌ์ฆ์ ์ํด ์ฌ์ฉํ์ง ์์ Page๋ฅผ ์ญ์ ํ๊ณ ๋ฉ๋ชจ๋ฆฌ์์ ๋ด๋ ค๊ฐ Page๋ฅผ ์๋ก ์ฝ์ด์ค๋ ์์
์ด ๋ฐ๋ณต๋์ด ๋ ๋ง์ Page Fault๊ฐ ์ผ์ด๋ฌ๋ค.
Page Fault ๊ณผ์ ์ ๋ฐ์ํ ํ์๋งํผ ํ๋ก๊ทธ๋จ์ ์ฑ๋ฅ์ ์ ํ์ํค๋ฏ๋ก ex9
์ด ex8
๋ณด๋ค ๋๋ฆฌ๊ฒ ์คํ์ ๋๋ง์ณค๋ค.
Suppose we have a process which has only two pages(page 1 and 2099) stored in the memory as below:
The page table should look like as the left picture in below:
Since storing this entire page table into memory is inefficient, we divide the page table into a number of pages(which are called directories), store only the active directories, and store the directory table which shows which directory is stored in which frame. In above, we can see only dir0 and dir2 are stored in frame 14 and frame 74 respectively. The final memory that contains our process and page table looks as below:
1-level paging requires 2+1024=1026 frames while 2-level paging requires only 5 frames.
98% hit-rate in Intel 486 | pg # | fr # | |โ |โ | | 34 | 42 | | 33 | 51 | | โฆโฆ. | โฆโฆโฆ.. |
struct mm_struct{
.............
struct vm_area_struct *mmap;
..........
};
struct vm_area_struct{
.........
unsigned long vm_start;
unsigned long vm_end;
struct file * vm_file;
struct vm_area_struct *vm_next;
.........
}
pgd is a pointer to Page Global Directoy.
struct mm_struct{
.............
pgd_t * pgd;
..........
};
pgd[x]
has the physical address of the frame where page table x
resides.
sys_get_phyloc()
, which will display the physical address of main()
.arch/x86/kernel/syscall_table_32.S
:
main()
.hw7-1.c
:
sys_get_phyloc(main)
in this program which passes the address of main
.hw7-2.c
:
sys_get_phyloc(addr)
is a system call that performs following steps in order:PGDIR_SHIFT
, PTRS_PER_PGD
, PAGE_SHIFT
, PTRS_PER_PTE
PGDIR_SHIFT=22
: number of shifting to extract directory number from a logical address. Logical address 0x080484a4 = (dir 20h, page 48h, offset 4a4h) pgd_index=20h, pte_index=48hPAGE_SHIFT=12
: number of shifting to extract page number from a logical address.PTRS_PER_PGD=1024
: number of directory entries in a directory tablePTRS_PER_PTE=1024
: number of frame pointer entries in a directoryx
unsigned int *x = current->mm->pgd; // virtual
printk("x: %x\n", x);
main()
: y
unsigned int *y= &x[dir]; // virtual
main()
: pdir
unsigned int pdir = *y & 0xfffff000; // the physical address should be at frame boundary
vdir
unsigned int *vdir = pdir + 0xc0000000; // physical to virtual mapping for kernel address
// read about kernel address space in Section 7.4.
main()
: k
// this is not working!
unsigned int *k = &pdir[pg]; // We can't access physical memory directly
// Compute vdir, corresponding virtual address for pdir, and use it to k.
unsigned int *k = &vdir[pg]; // virtual
main()
: pfr
unsigned int pfr = *k & 0xfffff000; // the physical address should be at frame boundary
main()
: pmain
main()
: vmain
vmain
and compare them with the first 4 bytes of main
in the original executable file(use โobjdump -d program-name
โ to see the first 4 bytes of main in the original program). If they are same, you have the correct physical address of main
.mm/mmap.c
:
ํ์ผ์ ์์ ํ์ ์ปดํ์ผ ๋ฐ ์ฌ๋ถํ ํ๋ค.
$ make bzImage
$ cp arch/x86/boot/bzImage /boot/bzImage
$ reboot
๋ฐ๋ก ์ง์ 7-2 ๋ฌธ์ ์์ ๋ง๋ hw7-2
ํ๋ก๊ทธ๋จ์ ์คํ์์ผฐ๋ค.
$ echo 8 > /proc/sys/kernel/printk
$ ./hw7-2
$ objdump -d ./hw7-2
main
์ ์ฒซ ์ฃผ์์ objdump
๋ก ์ถ๋ ฅํ ์ฃผ์ ๊ฐ์ด 0x80483f4๋ก ๊ฐ์ ๊ฒ์ ํ์ธํ ์ ์๋ค.
mem_map
struct page * mem_map; // array of struct page
page{}
is a page descriptor: it has the information of the corresponding page frame.
Each descriptor is 32 bytes. For 4GB ram, we have 232/212=220 frames. This means we need 220 memory descriptors, which will require 2*_20 _ 32 bytes=32 MB.
struct page {
unsigned long flags; // property of this frame
atomic_t _count; // page frame's reference number. -1 if free
.....
}
struct mm_struct{
unsigned long nr_ptes; // number of page tables(directories) this process is using
.........
}
user-mode address space(3GB)+ kernel-mode address space(1GB)
kernel-mode address space starts at 0xc0000000, and each page there maps each frame in the physical memory. So we can read the physical memory via kernel-mode address space.
ex1.c
:
void main(){
write(...);
....
}
$ ./ex1
=> write(โฆ..) => โฆ. => sys_write(โฆ.) => call 0xc015d04b (assuming the address of sys_write is 0xc015d04b)
Now cpu has to jump to 0xc015d04b which is a virtual address. The cpu looks at ex1โs page table since โcurrentโ is still ex1. How ex1 knows the physical location of sys_write? Because of this problem, Linux maps all the physical address of the memory page frames to the virtual address starting at 0xc0000000. For example, the above virtual address 0xc015d04b maps to physical address 0xc015d04b - 0xc0000000 = 0x015d04b.