本次要实现,指定进程pid,和某个字符串变量的地址,在linux内核中将字符串内容打印出来。测试程序的代码如下:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>char* p = "hello, can show this in driver";int main()
{printf("pid = %d\n", getpid());printf("%s ,addr: %p\n", p, p);getchar();return 0;}
执行结构如下:
pid = 8208
hello, can show this in driver ,addr: 0x400628
进程的PID为8208,该进程内字符串的地址是0x400628,字符串的内容是:hello, can show this in driver。
下面是内核部分的代码,主要逻辑是遍历进程列表,找到目标进程,再根据分页的各层级找到目录页表,获取对应的页框。最后根据虚拟地址在页框内的偏移,将字符串打印出来。
static int mem_print(struct inode* inode, struct file*filep)
{struct task_struct * p;// 变量系统中的每个进程for_each_process(p) {// 不是目标进程if (8208 != p->pid){continue;}printk("------\n");printk("state - %X\n", p->state);printk("pid - %X\n", p->pid);struct fs_struct *fs = p->fs;if (NULL != fs){struct path cur_path = fs->pwd;if (NULL != cur_path.dentry){struct dentry *dentry = cur_path.dentry;while (NULL != dentry){printk("d_iname = %s ,", dentry->d_iname);if (0 == strcmp("/", dentry->d_iname)){break;}dentry = dentry->d_parent; // 父目录}}}unsigned long addr = 0x400628; // 目标字符串的虚拟地址struct mm_struct *mm = p->mm;if (NULL != mm){struct vm_area_struct *vm_area = mm->mmap;pgd_t *pgd = pgd_offset(mm, addr);printk("pgd = %ld, %p\n", pgd, pgd);p4d_t *p4d = p4d_offset(pgd, addr);pud_t *pud = pud_offset(p4d, addr);printk("pud = %ld, %p\n", pud, pud);pmd_t *pmd = pmd_offset(pud, addr);printk("pmd = %d, %p\n", pmd, pmd);pte_t *pte = pte_offset_kernel(pmd, addr); // 页表的线性地址struct page *page = pte_page(*pte); // 页框的线性地址if (NULL != page){printk("page = %lx\n", page);printk("flags = %u\n", page->flags);// 地址在页框内的偏移int offset = addr & 0xFFF;printk("offset = %d\n", offset);unsigned long pfn = page_to_pfn(page);printk("pfn = %x\n", pfn);void *p_addr = page_address(page);printk("p_addr = %lx\n", p_addr);printk("data: %s\n", p_addr + offset);}}}return 0;
}
打印的内核日志如下:
[ 5167.042332] ------
[ 5167.042333] state - 1
[ 5167.042334] pid - 2010
[ 5167.042335] d_iname = mem_test ,
[ 5167.042335] d_iname = 网络 ,
[ 5167.042336] d_iname = linux笔记 ,
[ 5167.042336] d_iname = 桌面 ,
[ 5167.042337] d_iname = wang ,
[ 5167.042347] d_iname = home ,
[ 5167.042348] d_iname = / ,
[ 5167.042349] pgd = -122599419764736, 000000003c17b28f
[ 5167.042350] pud = -122596816502784, 00000000867a9350
[ 5167.042351] pmd = 830038032, 0000000005dcf8ab
[ 5167.042352] page = fffff08700184e80
[ 5167.042352] flags = 3221291048
[ 5167.042353] offset = 1576
[ 5167.042353] pfn = 613a
[ 5167.042354] p_addr = ffff907f0613a000
[ 5167.042354] data: hello, can show this in driver
d_iname是pid为8208的进程程序文件所在的各级目录,完整目录是:/home/wang/桌面/linux笔记/网络/mem_test。
日志的最后一行,将目标字符串打印了出来。