3.5.3. Linux内核API之进程管理
3.5.3.1. 数据结构
enum pid_type
{
PIDTYPE_PID, //进程的进程号
PIDTYPE_PGID, //进程组领头进程的进程号
PIDTYPE_SID, //会话领头进程的进程号
PIDTYPE_MAX
};
struct pid_namespace
{
struct kref kref; //引用计数器,进程每引用一次加1
struct pidmap pidmap[PIDMAP_ENTRIES]; //记录当前系统的PID使用情况
struct rcu_head rcu;
int last_pid; //记录上一次分配给进程的PID值
unsigned int nr_hashed;
struct task_struct *child_reaper; //保存指向该进程的task_struct值
struct kmem_cache *pid_cachep; //指向该进程的cache中分配的空间
unsigned int level; //表示当前命名空间在命名空间层次结构中的深度
struct pid_namespace *parent; //指向父命名空间的指针
#ifdef CONFIG_RPOC_FS
struct vfsmount *proc_self;
struct dentry *proc_self;
struct dentry *proc_thread_self;
#endif
#ifdef CONFIG_BSD_PROCESS_ACCT
struct bsd_acct_struct *bacct;
#endif
struct user_namespace *user_ns;
struct work_struct proc_work;
kgid_t pid_gid;
int hide_pid;
int reboot;
struct ns_common ns;
};
struct pid
{
atomic_t count; //使用此进程的任务数量
unsigned int level;
struct hlist_head tasks[PIDTYPE_MAX];
struct upid numbers[1];
};
struct mm_struct
{
struct vm_area_struct *mmap; //指向线性区对象的链表头
struct rb_root mm_rb;
u32 vmacache_seqnum; //每一个进程的vmacache大小
#ifdef CONFIG_MMU
//在进程地址空间中搜索有效线性地址区间的方法
unsigned long (*get_unmapped_area)(struct file *filp, unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags);
#endif
unsigned long mmap_base; //标识第一个分配的匿名线性区或文件内存映射的线性地址
unsigned long mmap_legacy_base; //mmap区域自下而上的分配区
unsigned long task_size; //在vm空间任务大小
unsigned long highest_vm_end; //最大vma的结束地址
pgd_t *pgd; //指向页全局目录
atomic_t mm_users; //次使用计数器
atomic_t mm_count; //主使用计数器
atomic_long_t nr_ptes; //页表所在的页地址
int map_count; //线性区vma的个数
spinlock_t page_table_lock; //线性区的自旋锁
struct rw_semaphore mmap_sem; //线性区的读写信号量
struct list_head mmlist; //指向内存描述符链表中的相邻元素
unsigned long hiwater_rss;
unsigned long hiwater_vm;
/*total_vm指进程地址空间的大小(页数),locked_vm指锁住而不能换出的页的个数
shared_vm值共享文件内存映射中的页数,exec_vm指可执行内存映射中的页数*/
unsigned long total_vm, locked_vm, pinned_vm, shared_vm, exec_vm;
//stack_vm指用户堆栈中的页数
unsigned long stack_vm, def_flags;
//start_code指可执行代码的起始地址,end_code指可执行代码的最后地址,
//start_data指已初始化数据的地址,end_data指已初始化数据的最后地址
unsigned long start_code, end_code, start_data, end_data;
//start_brk指堆的起始地址,brk指堆的当前最后地址,start_stack指用于堆栈的起始地址
unsigned long start_brk, brk, start_stack;
//命令行参数起止地址,环境变量的起止地址
unsigned long arg_start, arg_end, env_start, env_end;
unsigned long saved_auxv[AT_VECTOR_SIZE]; //开始执行ELF程序时使用
struct mm_rss_stat rss_stat;
struct linux_binfmt *binfmt;
cpumask_var_t cpu_vm_mask_var;
mm_context_t context; //mm上下文
unsigned long flags;
struct core_state *core_state;
#ifdef CONFIG_AIO
spinlock_t ioctx_lock;
struct kioctx_tabl __rcu *ioctx_table;
#endif
#ifdef CONFIG_MEMCG
struct task_struct __rcu *owner;
#endif
struct file *exe_file;
#ifdef CONFIG_MMU_NOTIFIER
struct mmu_notifier_mm *mmu_notifier_mm;
#endif
}
3.5.3.2. 函数接口
__task_pid_nr_ns
此函数用于获取进程的进程号,此进程应满足如下约束条件
如果参数type不等于PIDTYPE_PID,则参数task用其所属任务组中的第一个任务赋值,否则保持task不变
此进程是参数task任务描述符中的进程
保证进程进程描述符pid_namespace和参数ns相同
#include <linux/sched.h>
/*
* task: 保存任务的基本信息
* type: pid类型
* ns: 对进程命名空间信息的描述
* return: 返回符合条件的进程的进程号
*/
pid_t __task_pid_nr_ns(struct task_struct *task, enum pid_type type,
struct pid_namespace *ns)
测试代码
static int __init __task_pid_nr_ns_init(void)
{
//获取当前进程的进程描述符
struct pid *kpid = find_get_pid(current->pid);
//获取进程所属任务的任务描述符
struct task_struct *task = pid_task(kpid, PIDTYPE_PID);
//获取任务对应进程的进程描述符
pid_t ret = __task_pid_nr_ns(task, PIDTYPE_PID, kpid->numbers[kpid->level].ns);
printk("the pid of find_get_pid is : %d\n", kpid->numbers[kpid->level].nr);
printk("the result of the __task_pid_nr_ns is : %d\n", ret);
}
find_get_pid
该函数根据提供的进程号获取对应的进程描述符,并使进程描述符中的字段count的值加1,即此进程的用户数加1
#include <linux/pid.h>
/*
* nr: 进程对应的进程号
* return: 返回与提供的进程号对应的进程描述符
*/
struct pid *find_get_pid(int nr)
find_pid_ns
该函数获取进程的进程描述符,此进程应满足如下约束条件
进程的进程号和参数nr相同
保证进程描述符的pid_namespace和参数ns相同
#include <linux/pid.h>
/*
* nr: 进程描述符对应的进程号
* ns: 进程命名空间信息描述
* return: 保存符合条件的进程描述符信息
*/
struct pid *find_pid_ns(int nr, struct pid_namespace *ns)
测试代码
static int __init find_pid_ns_init(void)
{
//获取当前进程的描述符
struct pid *kpid = find_get_pid(current->pid);
//获取进程的描述符
struct pid *fpid = find_pid_ns(kpid->numbers[kpid->level].nr,
kpid->numbers[kpid->level].ns);
printk("the find_pid_ns result's count is: %d\n", fpid->count);
printk("the find_pid_ns result's level is: %d\n", fpid->level);
printk("the find_pid_ns result's pid is: %d\n", fpid->numbers[fpid->level].nr);
printk("the pid of current thread is: %d\n", current->pid);
}
find_vpid
此函数根据提供的局部进程号获取对应的进程描述符
#include <linux/pid.h>
/*
* nr: 进程对应的局部进程号,一般与进程号相同
* return: 返回进程描述符信息
*/
struct pid *find_vpid(int nr)
get_task_mm
此函数根据提供的任务描述符信息,获取其对应的内存信息
#include <linux/sched.h>
/*
* task: 进程的任务描述符信息
* return: 返回任务描述符对应的内存信息
*/
struct mm_struct *get_task_mm(struct task_struct *task)
测试代码
static int __init get_task_mm_init(void)
{
//获取当前进程的描述符信息
struct pid *kpid = find_get_pid(current->pid);
//获取进程任务描述符信息
struct task_struct *task = pid_task(kpid, PIDTYPE_PID);
//获取任务的内存描述符
struct mm_struct *mm_task = get_task_mm(task);
printk("the mm_users of the mm_struct is: %d\n", mm_task->mm_users);
printk("the mm_count of the mm_struct is: %d\n", mm_task->mm_count);
printk("the tgid of the mm_struct is: %d\n", mm_task->owner->tgid);
}
ns_of_pid
此函数用于获取进程的命名空间信息
#include <linux/pid.h>
/*
* pid: 进程描述符
* return: 返回进程命名空间信息
*/
static inline struct pid_namespace *ns_of_pid(struct pid *pid)
pid_task
此函数用于获取任务的任务描述符信息,此任务在进程pid的使用链表中,并且搜索的链表的起始元素的下标为参数type的值
#include <linux/pid.h>
/*
* pid: 进程描述符
* pid_type: pid类型
* return: 返回任务描述
*/
struct task_struct *pid_task(struct pid *pid, enum pid_type)