编写内核模块:实现打印进程地址空间
进程地址空间相关的知识在博客:【linux】进程地址空间 ,内核模块的相关介绍与基本操作在:【linux】内核模块的插入与删除,建议看完基础知识再来学习打印进程地址空间。
首先我们写一个程序(为打印进程地址空间做准备):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int A;
int B=0;
int C=2;
static int D;
static int E=0;
static int F=4;
const int G=5;
static char H=6;
int main(void)
{
int a;
int b=0;
int c=2;
static int d;
static int e=0;
static int f=4;
static int g=5;
char char1[]="abcde";
char *cptr="123456";
int *heap=malloc(sizeof(int)*4);
printf("PID is: %d \n\n",getpid());
printf("int A A_addr=%p\n",&A);
printf("int B=0 B_addr=%p\n",&B);
printf("int C=2 C_addr=%p\n",&C);
printf("static int D; D_addr=%p\n",&D);
printf("static int E=0 E_addr=%p\n",&E);
printf("static int F=4 F_addr=%p\n",&F);
printf("const int G=5 G_addr=%p\n",&G);
printf("static char H=6 H_addr=%p\n",&H);
printf("\n");
printf("int a A_addr=%p\n",&a);
printf("int b=0 B_addr=%p\n",&b);
printf("int c=2 C_addr=%p\n",&c);
printf("static int d; D_addr=%p\n",&d);
printf("static int e=0 E_addr=%p\n",&e);
printf("static int f=4 F_addr=%p\n",&f);
printf("const int g=5 G_addr=%p\n",&g);
printf("\n");
printf("char char1[] = 'abcde'\t\t\tchar1_addr = %p\n",char1);
printf("char char1[] = 'abcde'\t\t\t&char1_addr = %p\n",&char1);
printf("char **cptr = '1'\t\t\tcptr_addr = %p\n",&cptr);
printf("value of the cptr\t\t\tcptr_value = 0x%p\n",cptr);
printf("value of %p\t\t\tvalue_0x%p = %d\n",cptr,cptr,*cptr);
printf("int *heap=malloc(sizeof(int)*4)\theap_addr = %p\n",heap);
printf("int *heap=malloc(sizeof(int)*4)\t&heap_addr = %p\n",&heap);
pause();
free(heap);
return 0;
}
该程序有未初始化或初值为0的全局变量和静态局部变量(对应bss段),已初始化且初值非0的全局变量和静态局部变量(对应数据段),局部变量(对应栈区),malloc()函数生成的空间(堆动态分配)等。
运行程序结果:
该程序主要将代码中的各种数据的存放地址、进程的PiD进行了打印
程序运行先不要关闭,保持这个界面,打开另一个终端
编写内核模块:print_vma.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/sched.h>
static pid_t pid;
//向模块传递参数,文件的权限为0644
module_param(pid,int,0644);
int print_vma(void)
{
struct task_struct *task;
struct mm_struct *mm;
struct vm_area_struct *vma;
printk("\n\n\n\n\n\n\n\n");
printk("begin to print virtual address space... \n");
printk("\n");
task=pid_task(find_vpid(pid),PIDTYPE_PID);
mm=task->mm;
//打印进程的名字(comm)与进程号(pid)
printk("executable name:%s pid:%d\n",task->comm,task->pid);
printk("\n");
//打印mm_struct结构体中的一些内容
//代码段的开始地址、结束地址
printk("start_code:0x%lx end_code:0x%lx\n",mm->start_code,
mm->end_code);
//数据段的开始地址、结束地址
printk("start_data:0x%lx end_data:0x%lx\n",mm->start_data,
mm->end_data);
printk("\n");
//堆的开始地址、结束地址
printk("start_brk:0x%lx end_code:0x%lx\n",mm->start_brk,
mm->brk);
printk("\n");
//栈的开始地址、结束地址
printk("start_stack:0x%lx\n",mm->start_stack);
printk("\n");
down_read(&mm->mmap_sem);
//打印每个vma的权限
for(vma=task->mm->mmap;vma;vma=vma->vm_next){
printk("0x%lx - 0x%lx ",vma->vm_start,vma->vm_end);
if(vma->vm_flags & VM_READ)
printk("r");
else
printk("-");
if(vma->vm_flags & VM_WRITE)
printk("w");
else
printk("-");
if(vma->vm_flags & VM_EXEC)
printk("x");
else
printk("-");
if(vma->vm_flags & VM_SHARED)
printk("s");
else
printk("p");
printk("\n");
}
up_read(&mm->mmap_sem);
return 0;
}
static int __init print_vma_init(void){
print_vma();
return 0;
}
static void __exit print_vma_exit(void){
printk("good bey,kernel!\n");
}
module_init(print_vma_init);
module_exit(print_vma_exit);
MODULE_LICENSE("GPL");
Makefile文件:
obj-m:=print_vma.o #产生print_vma模块的目标文件
#目标文件 文件 要与模块名字相同
CURRENT_PATH:=$(shell pwd) #模块所在的当前路径
LINUX_KERNEL:=$(shell uname -r) #linux内核代码的当前版本
LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)
all:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules #编译模块
#[Tab] 内核的路径 当前目录编译完放哪 表明编译的内核模块
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean #清理模块
插入模块:
查看打印信息: