操作系统课程设计Record

First Post:

Last Update:

用户程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <stdio.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h> /* open */
#include <stdint.h> /* uint64_t */
#include <stdlib.h> /* size_t */
#include <unistd.h> /* pread, sysconf */

typedef struct {
uint64_t pfn : 54;
unsigned int soft_dirty : 1;
unsigned int file_page : 1;
unsigned int swapped : 1;
unsigned int present : 1;
} PagemapEntry;


int pagemap_get_entry(PagemapEntry *entry, int pagemap_fd, uintptr_t vaddr)
{
size_t nread;
ssize_t ret;
uint64_t data;
uintptr_t vpn;

vpn = vaddr / sysconf(_SC_PAGE_SIZE);
nread = 0;
while (nread < sizeof(data)) {
ret = pread(pagemap_fd, &data, sizeof(data) - nread,
vpn * sizeof(data) + nread);
nread += ret;
if (ret <= 0) {
return 1;
}
}
entry->pfn = data & (((uint64_t)1 << 54) - 1);
entry->soft_dirty = (data >> 54) & 1;
entry->file_page = (data >> 61) & 1;
entry->swapped = (data >> 62) & 1;
entry->present = (data >> 63) & 1;
return 0;
}
int virt_to_phys_user(uintptr_t *paddr, pid_t pid, uintptr_t vaddr)
{
char pagemap_file[BUFSIZ];
int pagemap_fd;

//读取对应进程地址映射
snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%ju/pagemap", (uintmax_t)pid);
pagemap_fd = open(pagemap_file, O_RDONLY);
if (pagemap_fd < 0) {
return 1;
}
PagemapEntry entry;
//条目获取
if (pagemap_get_entry(&entry, pagemap_fd, vaddr)) {
return 1;
}
close(pagemap_fd);
*paddr = (entry.pfn * sysconf(_SC_PAGE_SIZE)) + (vaddr % sysconf(_SC_PAGE_SIZE));
return 0;
}

int main(void) {
setbuf(stdout, 0);
int a = 0;
pid_t pid = fork();
if (pid == 0)
{
//子进程
printf("Pid:%d\n",getpid());
printf("child: \n\tvirtual address:\t%llx\n\tphysical address:\t%llx\n", &a, syscall(335, &a));
sleep(2);
}
else
{
//父进程
printf("Pid:%d\n",getpid());
printf("parent: \n\tvirtual address:\t%llx\n\tphysical address:\t%llx\n", &a, syscall(335, &a));
uintptr_t aptr = &a;
uintptr_t aphy = NULL;
//子进程中该变量的物理地址
virt_to_phys_user(&aphy, pid, aptr);
printf("child:\n\tpagemap approach:\t%llx\n", aphy);
sleep(2);
}
return 0;
}

系统调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/mm.h>
#include <linux/hugetlb.h>
#include <asm/current.h>
#include <asm/pgtable_types.h>

SYSCALL_DEFINE1(phy_addr_at, unsigned long, addr) {
resource_size_t res = 0;
pte_t *pte;
spinlock_t *ptlp;

if (current->mm == NULL) {
printk("error: current process is anonymous.");
return -1;
}

/**
* 通过mm_struct获取pgd,即第一级页表,然后根据addr逐级深入,
* 最终获得pte,即第四级页表(page table entries)的页表项,
* 注意这个过程需要锁ptlp
*/
follow_pte(current->mm, (unsigned long)addr, &pte, &ptlp);

// 从pte表项中提取pfn,即物理页地址
unsigned long pfn;
pfn = pte->pte;
pfn ^= (pfn && !(pfn & _PAGE_PRESENT)) ? ~0ull : 0;
pfn = (pfn & PTE_PFN_MASK) >> PAGE_SHIFT;

// 由物理页地址以及当前虚拟页偏移得到实际物理地址
res = (pfn << PAGE_SHIFT) (addr & ~(~0 << PAGE_SHIFT));

// 注意释放资源
pte_unmap_unlock(pte, ptlp);

return (unsigned long long)res;
}

参考M4tsuri师傅的作业(加个系统调用在别的学校是小作业,在我这就是课程设计了……)。


插画ID:96984236