一:進程的堆棧
內核在創建進程的時候,在創建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) );})