• linux内核堆栈


      一:进程的堆栈

      内核在创建进程的时候,在创建task_struct的同时会为进程创建相应的堆栈。每个进程会有两个栈,一个用户栈,存在于用户空间,一个内核栈,存 在于内核空间。当进程在用户空间运行时,cpu堆栈指针寄存器里面的内容是用户堆栈地址,使用用户栈;当进程在内核空间时,cpu堆栈指针寄存器里面的内 容是内核栈空间地址,使用内核栈。

      内核为每个进程分配task_struct结构体的时候,实际上分配两个连续的物理页面,底部用作task_struct结构体,结构上面的用作堆栈。使用current()宏能够访问当前正在运行的进程描述符。

     注意:这个时候task_struct结构是在内核栈里面的,内核栈的实际能用大小大概有7K。

      当内核栈为8K时,Thread_info在这块内存的起始地址,内核栈从堆栈末端向下增长。所以此时,要通过thread_info结构体中的task_struct域来获得于thread_info相关联的task。更详细的参考相应的 current宏的实现;

      下面是struct thread_info的定义:

    50 struct thread_info {
     51     unsigned long       flags;      /* low level flags */
     52     int         preempt_count;  /* 0 => preemptable, <0 => bug */
     53     mm_segment_t        addr_limit; /* address limit */
     54     struct task_struct  *task;      /* main task structure */
     55     struct exec_domain  *exec_domain;   /* execution domain */
     56     __u32           cpu;        /* cpu */
     57     __u32           cpu_domain; /* cpu domain */
     58     struct cpu_context_save cpu_context;    /* cpu context */
     59     __u32           syscall;    /* syscall number */
     60     __u8            used_cp[16];    /* thread used copro */
     61     unsigned long       tp_value;
     62     struct crunch_state crunchstate;
     63     union fp_state      fpstate __attribute__((aligned(8)));
     64     union vfp_state     vfpstate;
     65 #ifdef CONFIG_ARM_THUMBEE
     66     unsigned long       thumbee_state;  /* ThumbEE Handler Base register */
     67 #endif
     68     struct restart_block    restart_block;
     69 };

    通过上面定义我们可以看出在thread_info结构体中有指向task_struct 的指针task;在struct task_struct 结构体中包含着进程的相关信息,下面是相关参数说明;

    (1) unsigned short used_math;

      是否使用FPU。

      (2) char comm[16];

      进程正在运行的可执行文件的文件名。

      (3) struct rlimit rlim[RLIM_NLIMITS];

      结 构rlimit用于资源管理,定义在linux/include/linux/resource.h中,成员共有两项:rlim_cur是资源的当前最大 数目;rlim_max是资源可有的最大数目。在i386环境中,受控资源共有RLIM_NLIMITS项,即10项,定义在 linux/include/asm/resource.h中,见下表:

      (4) int errno;

      最后一次出错的系统调用的错误号,0表示无错误。系统调用返回时,全程量也拥有该错误号。

      (5) long debugreg[8];

      保存INTEL CPU调试寄存器的值,在ptrace系统调用中使用。

      (6) struct exec_domain *exec_domain;

      Linux可以运行由80386平台其它UNIX操作系统生成的符合iBCS2标准的程序。关于此类程序与Linux程序差异的消息就由 exec_domain结构保存。

      (7) unsigned long personality;

      Linux 可以运行由80386平台其它UNIX操作系统生成的符合iBCS2标准的程序。 Personality进一步描述进程执行的程序属于何种UNIX平台的“个性”信息。通常有PER_Linux、PER_Linux_32BIT、 PER_Linux_EM86、PER_SVR3、PER_SCOSVR3、PER_WYSEV386、PER_ISCR4、PER_BSD、 PER_XENIX和PER_MASK等,参见include/linux/personality.h。

      (8) struct linux_binfmt *binfmt;

      指向进程所属的全局执行文件格式结构,共有a。out、script、elf和java等四种。结构定义在include/linux /binfmts.h中(core_dump、load_shlib(fd)、load_binary、use_count)。

      (9) int exit_code,exit_signal;

      引起进程退出的返回代码exit_code,引起错误的信号名exit_signal。

      (10) int dumpable:1;

      布尔量,表示出错时是否可以进行memory dump。

      (11) int did_exec:1;

      按POSIX要求设计的布尔量,区分进程是正在执行老程序代码,还是在执行execve装入的新代码。

      (12) int tty_old_pgrp;

      进程显示终端所在的组标识。

      (13) struct tty_struct *tty;

      指向进程所在的显示终端的信息。如果进程不需要显示终端,如0号进程,则该指针为空。结构定义在include/linux/tty.h中。

      (14) struct wait_queue *wait_chldexit;

      在进程结束时,或发出系统调用wait4后,为了等待子进程的结束,而将自己(父进程)睡眠在该队列上。结构定义在include/linux /wait.h中。

      13. 进程队列的全局变量

      (1) current;

      当前正在运行的进程的指针,在SMP中则指向CPU组中正被调度的CPU的当前进程:

      #define current(0+current_set[smp_processor_id()])/*sched.h*/

      struct task_struct *current_set[NR_CPUS];

      (2) struct task_struct init_task;

      即0号进程的PCB,是进程的“根”,始终保持初值INIT_TASK。

      (3) struct task_struct *task[NR_TASKS];

      进 程队列数组,规定系统可同时运行的最大进程数(见kernel/sched.c)。NR_TASKS定义在include/linux/tasks.h 中,值为512。每个进程占一个数组元素(元素的下标不一定就是进程的pid),task[0]必须指向init_task(0号进程)。可以通过 task[]数组遍历所有进程的PCB。但Linux也提供一个宏定义for_each_task()(见 include/linux/sched.h),它通过next_task遍历所有进程的PCB:

      #define for_each_task(p)

      for(p=&init_task;(p=p->next_task)!=&init_task;)

      (4) unsigned long volatile jiffies;

      Linux的基准时间(见kernal/sched.c)。系统初始化时清0,以后每隔10ms由时钟中断服务程序do_timer()增1。

      (5) int need_resched;

      重新调度标志位(见kernal/sched.c)。当需要Linux调度时置位。在系统调用返回前(或者其它情形下),判断该标志是否置位。置位的话,马上调用schedule进行CPU调度。

      (6) unsigned long intr_count;

      记 录中断服务程序的嵌套层数(见kernal/softirq.c)。正常运行时,intr_count为0。当处理硬件中断、执行任务队列中的任务或者执 行bottom half队列中的任务时,intr_count非0。这时,内核禁止某些操作,例如不允许重新调度。

    以上内容参考:http://www.cnblogs.com/hongzhunzhun/

    下面我们要做的是写一个模块,打印当前进程的名字:

     1 #include <linux/init.h>
     2 #include <linux/module.h>
     3 #include <linux/thread_info.h>
     4 #include <linux/sched.h>
     5 
     6 MODULE_LICENSE("GPL");
     7 MODULE_AUTHOR("bunfly");
     8 
     9 
    10 int bunfly_init()
    11 {
    12     
    13     struct thread_info *tmp = NULL;
    14     tmp = (struct thread_info *)((unsigned long )&tmp & ~0x1fff);//摸除低13位(8k对齐)获取thread_info首地址    
    15     struct task_struct *find = tmp->task;//得到对应的task_strcut地址
    16     printk("name is: %s
    ", find->comm);//获取当前进程名
    17 
    18     return 0;    
    19 }
    20 
    21 void bunfly_exit()
    22 {
    23     printk("goodbye bunfly_exit
    ");
    24 }
    25 
    26 module_init(bunfly_init);
    27 module_exit(bunfly_exit);

    在linux内核中,struct_task是以一个双向循环链表的形式存在:

    因此,我们可以循环打印所有的进程名,实现简易的ps命令:

    下面是具体代码:

     1 #include <linux/init.h>
     2 #include <linux/module.h>
     3 #include <linux/thread_info.h>
     4 #include <linux/sched.h>
     5 
     6 MODULE_LICENSE("GPL");
     7 MODULE_AUTHOR("bunfly");
     8 
     9 int bunfly_init()
    10 {    
    11     struct thread_info *tmp = NULL;
    12     struct task_struct *next = NULL;
    13     struct task_struct *find = NULL;
    14     struct list_head *list = NULL;
    15 
    16     tmp = current_thread_info();//获得thread_info的首地址
    17     find = tmp->task;//找到task_struct的首地址
    18 
    19     next = find;
    20     do {
    21         printk("next is: %s
    ", next->comm);            
    22         list = next->tasks.next;
    23         /*通过tasks的地址找到它所在的结构体(task_struct)的首地址,从而得到comm*/
    24         next = container_of(list, struct task_struct, tasks);
    25         
    26     } while (next != find);//循环打印
    27 
    28     return 0;    
    29 }
    30 
    31 void bunfly_exit()
    32 {
    33     printk("goodbye bunfly_exit
    ");
    34 }
    35 
    36 module_init(bunfly_init);
    37 module_exit(bunfly_exit);

    在linux内核中要经常用到宏container_of,这个必须掌握:它的原型如下:

    #define container_of(ptr, type, member) ({const typeof( ((type *)0)->member ) *__mptr = (ptr);  (type *)( (char *)__mptr - offsetof(type,member) );})

  • 相关阅读:
    整理牙刷
    color 圆盘染色
    数论の一波流[长期更新]
    生成树
    一维黑白棋
    Factorials
    平面分割问题
    poj1183 反正切函数
    烽火传递
    校门外的树
  • 原文地址:https://www.cnblogs.com/wenqiang/p/4802619.html
Copyright © 2020-2023  润新知