linux内核进程详解
2015-12-23 10:03
691 查看
一、什么是进程?
Robert Love’s 在《linux内核开发》一书中写道,“在UNIX开源系统中,进程是基本抽象之一,其他基本抽象是文件”。简单了说,进程是程序运行时,它包含了程序运行代码、一组资源(如一组打开的文件)、相关内核数据、一个地址空间、执行和含有全局变量数据的一个或者多个线程。
二、进程描述符
每一个进程都有自己的进程描述符,是在存储器中用于跟踪进程的信息,其中包括进程的各种信息:PID,状态,父进程,子进程,兄弟进程,存储器和列表等。
linux内核使用一个循环双向链表(struct task_structs)来存储进程描述符,这种结构定义在linux/sched.h中。下面是linux内核 2.6.15-1.2054_FC5版本的701行后的部分数据:
以上第一行字段状态定义为volatile long,该变量用于跟踪过程中执行状态,通过以下宏定义:
关键字volatile是值得研究的,可以查看http://www.kcomputing.com/volatile.html查看更多信息。
三、链表
在我们讨论进程在内核中如何存储之前,需要了解内核如何循环链表。以下链表实现是所有内核源代码的一个标准,链表在linux/list.h中声明,数据结构非常简单:
该文件还定义了几个现成的宏和函数,可以用于操作链表。此链表定义实现标准确保
不会出现用户异想天开造成bug的情况。关于链表详细信息可以产看Suman Adak的博客http://sumanadak.blogspot.com/2006/09/linux-kernel-linked-list.html。
四、内核任务列表
那么,linux内核是如何使用循环双向链表存储进程的记录呢?struct list_head 为我们定义了内部结构:
这个结构书名内核采用循环链表存储进程,这意味着可以使用标准内核链表、宏和函数遍历所有进程列表。
linux系统中,初始化从“所有进程母进程”开始,因此,它被放在链表的开头,尽管循环链表没有真正意义的头部。初始化进程描述符是静态分配:
下图表示了内存中的进程链表:
其他几个宏和函数能用于遍历链表:for_each_process()是遍历整个任务列表的宏。它被定义在linux/ sched.h中,如下所示:
next_task()是在linux/ sched.h中定义的一个实现返回列表下一个任务的宏:
list_entry() 是在linux/ list.h定义的宏:
因此,通过整个进程列表的遍历,我们可以查看所有系统上运行的进程。这需要宏for_each_process(task)来完成,其中,task是结构task_struct的指针。下面是一个linux内核中的例子:
当前的宏是正在执行的进程的进程描述符(一个task_struct的指针),在x86中这是由asm/thread_info.h中的函数current_thread_info()完成:
最后,是当前取消引用的任务成员thread_info结构部件,它由asm/thread_info.h转载current_thread_info()->task:
另外,Makefile 文件用来编译内核模块:
all:
END!
PS:本文翻译修改自http://linuxgazette.net/133/saha.html。
Robert Love’s 在《linux内核开发》一书中写道,“在UNIX开源系统中,进程是基本抽象之一,其他基本抽象是文件”。简单了说,进程是程序运行时,它包含了程序运行代码、一组资源(如一组打开的文件)、相关内核数据、一个地址空间、执行和含有全局变量数据的一个或者多个线程。
二、进程描述符
每一个进程都有自己的进程描述符,是在存储器中用于跟踪进程的信息,其中包括进程的各种信息:PID,状态,父进程,子进程,兄弟进程,存储器和列表等。
linux内核使用一个循环双向链表(struct task_structs)来存储进程描述符,这种结构定义在linux/sched.h中。下面是linux内核 2.6.15-1.2054_FC5版本的701行后的部分数据:
701 struct task_struct { 702 volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ 703 struct thread_info *thread_info; 767 /* PID/PID hash table linkage. */ 768 struct pid pids[PIDTYPE_MAX]; 798 char comm[TASK_COMM_LEN]; /* executable name excluding path
以上第一行字段状态定义为volatile long,该变量用于跟踪过程中执行状态,通过以下宏定义:
#define TASK_RUNNING 0 #define TASK_INTERRUPTIBLE 1 #define TASK_UNINTERRUPTIBLE 2 #define TASK_STOPPED 4 #define TASK_TRACED 8 /* in tsk->exit_state */ #define EXIT_ZOMBIE 16 #define EXIT_DEAD 32 /* in tsk->state again */ #define TASK_NONINTERACTIVE 64
关键字volatile是值得研究的,可以查看http://www.kcomputing.com/volatile.html查看更多信息。
三、链表
在我们讨论进程在内核中如何存储之前,需要了解内核如何循环链表。以下链表实现是所有内核源代码的一个标准,链表在linux/list.h中声明,数据结构非常简单:
struct list_head { struct list_head *next, *prev; };
该文件还定义了几个现成的宏和函数,可以用于操作链表。此链表定义实现标准确保
不会出现用户异想天开造成bug的情况。关于链表详细信息可以产看Suman Adak的博客http://sumanadak.blogspot.com/2006/09/linux-kernel-linked-list.html。
四、内核任务列表
那么,linux内核是如何使用循环双向链表存储进程的记录呢?struct list_head 为我们定义了内部结构:
struct list_head tasks;
这个结构书名内核采用循环链表存储进程,这意味着可以使用标准内核链表、宏和函数遍历所有进程列表。
linux系统中,初始化从“所有进程母进程”开始,因此,它被放在链表的开头,尽管循环链表没有真正意义的头部。初始化进程描述符是静态分配:
extern struct task_struct init_task;
下图表示了内存中的进程链表:
其他几个宏和函数能用于遍历链表:for_each_process()是遍历整个任务列表的宏。它被定义在linux/ sched.h中,如下所示:
#define for_each_process(p) \ for (p = &init_task ; (p = next_task(p)) != &init_task ;)
next_task()是在linux/ sched.h中定义的一个实现返回列表下一个任务的宏:
#define next_task(p) list_entry((p)->tasks.next, struct task_struct, tasks)
list_entry() 是在linux/ list.h定义的宏:
/* * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. */ #define list_entry(ptr, type, member) \ container_of(ptr, type, member) The macro container_of() is defined as follows: #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})
因此,通过整个进程列表的遍历,我们可以查看所有系统上运行的进程。这需要宏for_each_process(task)来完成,其中,task是结构task_struct的指针。下面是一个linux内核中的例子:
/* ProcessList.c Robert Love Chapter 3 */ #include < linux/kernel.h > #include < linux/sched.h > #include < linux/module.h > int init_module(void) { struct task_struct *task; for_each_process(task) { printk("%s [%d]\n",task->comm , task->pid); } return 0; } void cleanup_module(void) { printk(KERN_INFO "Cleaning Up.\n"); }
当前的宏是正在执行的进程的进程描述符(一个task_struct的指针),在x86中这是由asm/thread_info.h中的函数current_thread_info()完成:
/* how to get the thread information struct from C */ static inline struct thread_info *current_thread_info(void) { struct thread_info *ti; __asm__("andl %%esp,%0; ":"=r" (ti) : "0" (~(THREAD_SIZE - 1))); return ti; }
最后,是当前取消引用的任务成员thread_info结构部件,它由asm/thread_info.h转载current_thread_info()->task:
struct thread_info { struct task_struct *task; /* main task structure */ struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ unsigned long status; /* thread-synchronous flags */ __u32 cpu; /* current CPU */ int preempt_count; /* 0 => preemptable, <0 => BUG */ mm_segment_t addr_limit; /* thread address space: 0-0xBFFFFFFF for user-thread 0-0xFFFFFFFF for kernel-thread */ void *sysenter_return; struct restart_block restart_block; unsigned long previous_esp; /* ESP of the previous stack in case of nested (IRQ) stacks */ __u8 supervisor_stack[0]; }; Using the current macro and init_task we can write a kernel module to trace from the current process back to init. /* Traceroute to init traceinit.c Robert Love Chapter 3 */ #include < linux/kernel.h > #include < linux/sched.h > #include < linux/module.h > int init_module(void) { struct task_struct *task; for(task=current;task!=&init_task;task=task->parent) //current is a macro which points to the current task / process { printk("%s [%d]\n",task->comm , task->pid); } return 0; } void cleanup_module(void) { printk(KERN_INFO "Cleaning up 1.\n"); }
另外,Makefile 文件用来编译内核模块:
obj-m +=ProcessList.o obj-m +=traceinit.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clear:make -C /lib/modules/(shelluname−r)/buildM=(PWD) clean`
END!
PS:本文翻译修改自http://linuxgazette.net/133/saha.html。
相关文章推荐
- Unix 即将迎来 50 岁
- Linux VS Unix:Linux欲一统天下 Unix不死
- 看UNIX高手的10个习惯第1/2页
- 通过PHP修改Linux或Unix口令的方法分享
- mysql unix准换时间格式查找指定日期数据代码
- php strtotime 函数UNIX时间戳
- 安全检测Unix和Linux服务器安全设置入门精讲
- 分享20个Unix/Linux 命令技巧
- PHP使用gmdate实现将一个UNIX 时间格式化成GMT文本的方法
- Mac OS下配置远程Linux 服务器SSH密钥认证自动登录
- C的温故知新
- 使用golang和docker守护进程交互
- Alice梦游UNIX仙境
- LINUX之前UNIX的历史
- 关于GNU计划
- Unix 调试的瑞士军刀:lsof
- Linux/Unix 新手和专家教程
- oracle大型数据库系统在AIX/unix上的实战详解 讨论76 Oracle备份问题
- oracle大型数据库系统在AIX/unix上的实战详解 讨论76 Oracle备份问题
- UNIX操作系统的优势总结