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\n", find->comm);//獲取當前進程名
17 
18     return 0;    
19 }
20 
21 void bunfly_exit()
22 {
23     printk("goodbye bunfly_exit\n");
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\n", 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\n");
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) );})

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM