Linux内核API完全参考手册(第2版)
上QQ阅读APP看书,第一时间看更新

3.6 函数:get_task_mm( )

文件包含:

        #include <linux/sched.h>

函数定义:

在内核源码中的位置:linux-3.19.3/kernel/fork.c

函数定义格式:struct mm_struct *get_task_mm(struct task_struct *task)

函数功能描述:

此函数根据提供的任务描述符信息,获取其对应的内存信息,此内存信息保存在mm_struct结构体类型的变量中。

输入参数说明:

此函数的返回结果是struct task_struct结构体类型的变量,保存符合条件的任务描述符信息,其定义参见内核源码文件linux-3.19.3/include/linux/sched.h,其内核注释比较详细,请读者自行分析。

返回参数说明:

此函数的返回结果是任务描述符对应的内存信息,即某任务对应的内存信息,是一个struct mm_struct类型的变量,其定义在文件linux-3.19.3/include/linux/mm_types.h中,如下所示:

        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;

            /*arg_start指命令行参数起始地址,arg_end指命令行参数的最后地址,env_start指环境变
              量的起始地址,env_end指环境变量的最后地址*/
           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_table __rcu *ioctx_table;
       #endif
       #ifdef CONFIG_MEMCG
           struct task_struct __rcu *owner;
       #endif
           /* 存储符号链接/proc/<pid>/exe指向的文件*/
           struct file *exe_file;
       #ifdef CONFIG_MMU_NOTIFIER
           struct mmu_notifier_mm *mmu_notifier_mm;
       #endif
       #if defined(CONFIG_TRANSPARENT_HUGEPAGE) && ! USE_SPLIT_PMD_PTLOCKS
           pgtable_t pmd_huge_pte; //被page_table_lock锁保护
       #endif
       #ifdef CONFIG_CPUMASK_OFFSTACK
           struct cpumask cpumask_allocation;
       #endif
       #ifdef CONFIG_NUMA_BALANCING
           /* numa_next_scan是下一次扫描标记,它被PTEs中的pte_numa所标记 */
           unsigned long numa_next_scan;
           /* 扫描和设置pte_numa的重启点*/
           unsigned long numa_scan_offset;
           /* numa_scan_seq阻止两个线程设置pte_numa */
           int numa_scan_seq;
       #endif
       #if defined(CONFIG_NUMA_BALANCING) || defined(CONFIG_COMPACTION)
           /*
               * 一个带有批量TLB刷新操作正在运行的情况。
               * 当移动PROT_NONE或PROT_NUMA映射页时,任意的能够移动存储处理的事项都需要刷新
               */
           bool tlb_flush_pending;
       #endif
           struct uprobes_state uprobes_state;
       #ifdef CONFIG_X86_INTEL_MPX
           void __user *bd_addr; //地址范围目录
       #endif
       };
  

实例解析:

编写测试文件:get_task_mm.c

头文件引用:

        #include <linux/module.h>
        #include <linux/sched.h>
        #include <linux/pid.h>
        #include <linux/mm_types.h>
        MODULE_LICENSE("GPL");

模块加载函数定义:

        static int __init get_task_mm_init(void)
        {
            printk("into get_task_mm_init.\n");
            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);           //获取任务的内存描述符

            /*显示mm_task字段mm_users和字段mm_count的值*/
            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);

            /*显示与此mm_task相关进程的父进程的TGID和PID号*/
            printk("the tgid of the mm_strcut is:%d\n", mm_task->owner->tgid);
            printk("the pid of the mm_struct is:%d\n", mm_task->owner->pid);
            printk("the current PID is:%d\n", current->pid);         //显示当前进程的PID号
            printk("out get_task_mm_init.\n");
            return 0;
        }

模块退出函数定义:

        static void __exit get_task_mm_exit(void)
        {
            printk("Goodbye get_task_mm\n");
        }

模块加载、退出函数调用:

        module_init(get_task_mm_init);
        module_exit(get_task_mm_exit);

实例运行结果及分析:

首先编译模块,执行命令insmod get_task_mm.ko插入模块,然后执行命令dmesg -c查看内核输出信息,会出现如图3-6所示的结果。

图3-6 插入get_task_mm模块系统输出信息

结果分析:

由图3-6可以看出函数get_task_mm( )能够获得当前进程的信息,在当前进程的内存信息中其父进程的进程号和组进程号都是13384,与显示的当前进程号13384相同,说明函数get_task_mm( )能够成功获取相应进程的内存信息,其中字段mm_users的值是2,代表当前任务的内存空间的用户数量,字段mm_count的值为1,代表此任务的内存空间被引用的次数。