Linux内核设计与实现--学习笔记--第三章- 1(task_struct等)

1,Linux系统的线程非常特别,他对线程与进程并不特别区分。对Linux而言,线程是一种特殊的进程。

2,在Linux系统中,调用fork()创建进程,该系统调用通过复制一个现有进程来创建一个全新的进程,调用fork()的进程称为父进程,新生的进程称为子进程,在调用结束时,在返回点这个位置上,父进程恢复执行,子进程开始执行,fork()系统调用从内核返回两次:一次回到父进程,一次回到新生的子进程。

3,在OSE系统(ENEA公司出品,该公司成立于1968年,总部位于瑞典斯得哥尔摩,与爱立信共同成长)中,PCB与进程栈在内存中是相邻的,PCB在低地址处,进程栈(从高向低地址增长)在紧接着PCB的高地址存放(PCB+栈:在systemPool中分配)。而在Linux系统中并不直接存储PCB,取代PCB的是一个小的结构体:struct thread_info, 这个结构中并没有直接包含与进程相关的字段,而是通过task字段指向具体某个进程描述符(struct task_struct),这是因为现在用slab分配器动态生成task_struct(通过预先分配或重复使用task_struct,优点是进程创建迅速),所以只需在栈定(栈的低地址处)创建一个struct thread_info结构体,并指向task_struct。通过下图可知内核的栈(真正的栈)顶指针存储在esp寄存器中。


\include\asm-x86_64:
struct thread_info {
	struct task_struct	*task;		/* main task structure */
	struct exec_domain	*exec_domain;	/* execution domain */
	__u32			flags;		/* low level flags */
	__u32			status;		/* thread synchronous flags */
	__u32			cpu;		/* current CPU */
	int 			preempt_count;

	mm_segment_t		addr_limit;	
	struct restart_block    restart_block;
};

<em><span style="font-family:FangSong_GB2312;font-size:14px;">/**
 * 内核栈与thread_info 是个联合体, 在\include\linux\sched.h中定义
 */
union thread_union {
	struct thread_info thread_info;
	unsigned long stack[THREAD_SIZE/sizeof(long)];
};</span></em>

如在i386处理器中:

#ifdef CONFIG_4KSTACKS
#define THREAD_SIZE            (4096)
#else
#define THREAD_SIZE (8192)
#endif

即内核stack数组大小为2048,如果THREAD_SIZE = 8192

由于thread_union是联合体,我们可知stack[0]的地址与thread_info地址相同,也就是说每个thread的thread_info保存在进程内核栈的最低端(这与OSE系统不一样,OSE系统的PCB与栈是分开的,PCB并不占用栈的空间)

4,内核访问进程需要获取进程的task_struct,通过current宏可以得到当前正在运行的进程的进程描述符,硬件体系结构不同,该宏的实现也不同,有的硬件体系结构可以用专门的寄存器存放指向task_struct的指针,但像x86,其寄存器并不富裕,只能通过栈尾端的thread_info结构,计算偏移得到task_struct。对于i386(32位 x86)的current_thread_info定义如下:

<span style="font-size:14px;"><em>\include\asm-i386\thread_info.h
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;
}</em></span>

上述代码,如果THREAD_SIZE=8192,则~(THREAD_SIZE - 1)=0xE000,即低13位为0,这样esp寄存器的LSB 13bit被清零,得到了内核堆栈thread_info结构的地址。

current_thread_info()->task就得到了进程task_struct。

通过current宏可以直接得到task_struck:

<span style="font-size:14px;">#ifndef _I386_CURRENT_H    \include\asm-i386
#define _I386_CURRENT_H

#include <linux/thread_info.h>

struct task_struct;

static inline struct task_struct * get_current(void)
{
	return current_thread_info()->task;
}
 
#define current get_current()

#endif /* !(_I386_CURRENT_H) */</span><span style="font-size: 18px;">
</span>

5,OSE系统怎么得到current_process?

<span style="font-size:14px;">PROCESS current_process(void)
{
    D_DIV( debug_printf("current_process *\n"); );
    return odo_sys.current->pid;
}
</span>
对于多核处理器系统,OSE在每个CPU都维护一个结构:odo_sys,odo_sys.current就是当前正在运行进程的PCB。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值