开源一个Linux进程内存内核管理模块源码
Linux它是一款开源的内核系统。本人也非常喜欢嵌入式Linux系统,特别是它的内核源码,书写的风格,都非常讨我心欢。
这个驱动是之前业余的时候写的用于嵌入式开发版,点亮LED灯时候留下的,现在代码里已删除ioremap。不过对于新手来说,至少还是有学习价值的。
源码仅供交流学习之用,不得用于非法用途。
同时为了避免不法分子将此驱动用在非法的用途,我后面也将附上侦测建议。如果您是游戏厂商,那么可以尝试听取我的侦测建议。
下面我将带大家分析这个驱动,看看驱动是怎么写出来的。
在Linux内核里,不区分进程与线程。统一按照线程来看待。那么每个线程都有一个对应的pid_t、pid、task_struct。
他们之间的关系是这样的:
pid_t <–> struct pid
nr为进程pid数值
#include <linux/pid.h>
pid_t pid_vnr(struct pid *pid)
{
return pid_nr_ns(pid, current->nsproxy->pid_ns);
}
EXPORT_SYMBOL_GPL(pid_vnr);
struct pid *find_pid_ns(int nr, struct pid_namespace *ns);
EXPORT_SYMBOL_GPL(find_pid_ns);
struct pid *find_vpid(int nr)
{
return find_pid_ns(nr, current->nsproxy->pid_ns);
}
EXPORT_SYMBOL_GPL(find_vpid);
struct pid *find_get_pid(int nr)
{
struct pid *pid;
rcu_read_lock();
pid = get_pid(find_vpid(nr));
rcu_read_unlock();
return pid;
}
EXPORT_SYMBOL_GPL(find_get_pid);
void put_pid(struct pid *pid);
EXPORT_SYMBOL_GPL(put_pid);
struct pid * –> struct task_struct *
#include <linux/pid.h>
struct pid *get_task_pid(sturct task_struct *task, enum pid_type);
EXPORT_SYMBOL_GPL(get_task_pid);
struct task_struct *pid_task(struct pid *pid, enum pid_type);
EXPORT_SYMBOL(pid_task);
struct task_struct *get_pid_task(struct pid *pid, enum pid_type)
{
struct task_struct *result;
rcu_read_lock();
result = pid_task(pid, type);
if (result)
get_task_struct(result);
rcu_read_unlock();
return result;
}
EXPORT_SYMBOL(get_pid_task);
#include <linux/sched.h>
#define get_task_struct(tsk) do { atomic_inc(&(tsk)->usage); } while (0)
static inline void put_task_struct(struct task_struct *t)
{
if (atomic_dec_and_test(&t->usage))
__put_task_struct(t);
}
void __put_task_struct(struct task_struct *t);
EXPORT_SYMBOL_GPL(__put_task_struct);
看完以上的逻辑。大家是不是柳暗花明又一村,心里开朗了许多,他们之间是可以相互转换的。
通过进程pid_t可以拿到pid,通过pid可以拿到task_struct。又可以反过来通过task_struct拿到进程pid。
驱动源码是使用put_pid将进程pid*的使用次数减去1。
在Linux内核源码/kernel/pid.c下可以看到:
void put_pid(struct pid *pid)
{
struct pid_namespace *ns;
if (!pid)
return;
ns = pid->numbers[pid->level].ns;
if ((atomic_read(&pid->count) == 1) ||
atomic_dec_and_test(&pid->count)) {
kmem_cache_free(ns->pid_cachep, pid);
put_pid_ns(ns);
}
}
EXPORT_SYMBOL_GPL(put_pid);
读、写进程内存接口
首先根据pid*用get_pid_task取出task_struct。再用get_task_mm取出mm_struct结构。因为这个结构包含了进程的内存信息。首先检查内存是否可读if (vma->vm_flags & VM_READ)。
如果可读。那么开始计算物理内存地址位置。由于Linux内核默认开启MMU机制,所以只能以页为单位计算物理内存地址。计算物理内存地址的方法有很多。
如pagemap、pgd pud pmd pte、get_user_pages驱动里演示pagemap。
其他方法可自行参考Linux内核源码/fs/proc/task_mmu.c。
知道了物理内存地址后,读、写物理内存地址,Linux内核也有演示:drivers/char/mem.c。写的非常详细。最后还要注意MMU机制的离散内存,即buffer不连续问题。
这个接口没什么技术含量,都是照抄Linux内核源码的代码,fs\proc\task_mmu.c。
核心思想是通过task_struct取出mm_struct,接下来在mm_struct中遍历取出vma。
struct mm_struct {
struct vm_area_struct * mmap; /* list of VMAs */
struct rb_root mm_rb;
struct vm_area_struct * mmap_cache; /* last find_vma result */
#ifdef CONFIG_MMU
unsigned long (*get_unmapped_area) (struct file *filp,
unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags);
void (*unmap_area) (struct mm_struct *mm, unsigned long addr);
#endif
unsigned long mmap_base; /* base of mmap area */
unsigned long mmap_legacy_base; /* base of mmap area in bottom-up allocations */
unsigned long task_size; /* size of task vm space */
unsigned long cached_hole_size; /* if non-zero, the largest hole below free_area_cache */
unsigned long free_area_cache; /* first hole of size cached_hole_size or larger */
unsigned long highest_vm_end; /* highest vma end address */
pgd_t * pgd;
atomic_t mm_users; /* How many users with user space? */
atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */
int map_count; /* number of VMAs */
spinlock_t page_table_lock; /* Protects page tables and some counters */
struct rw_semaphore mmap_sem;
struct list_head mmlist; /* List of maybe swapped mm's. These are globally strung
* together off init_mm.mmlist, and are protected
* by mmlist_lock
*/
unsigned long hiwater_rss; /* High-watermark of RSS usage */
unsigned long hiwater_vm; /* High-water virtual memory usage */
unsigned long total_vm; /* Total pages mapped */
unsigned long locked_vm; /* Pages that have PG_mlocked set */
unsigned long pinned_vm; /* Refcount permanently increased */
unsigned long shared_vm; /* Shared pages (files) */
unsigned long exec_vm; /* VM_EXEC & ~VM_WRITE */
unsigned long stack_vm; /* VM_GROWSUP/DOWN */
unsigned long def_flags;
unsigned long nr_ptes; /* Page table pages */
unsigned long start_code, end_code, start_data, end_data;
unsigned long start_brk, brk, start_stack;
unsigned long arg_start, arg_end, env_start, env_end;
unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */
mm_struct结构体里面有个arg_start变量,储存的地址值即进程命令行
为了避免不法分子将此驱动用在非法的用途, 在此给出侦测建议:
最后开源地址(含demo)
Github链接:https://github.com/abcz316/rwProcMem33
首先,编译此源码需要一定的技巧,再者,手机在出厂时本身已设置多重障碍用来阻止第三方驱动的加载(即使你拥有root权限也无法加载),此源码仅供交流学习Linux系统使用。
看雪ID:abcz316
https://bbs.pediy.com/user-892833.htm
推荐文章++++
好书推荐