4.8 进程管理

1. 创建进程

程序控制结构体 PCB(Process Control Block): task_struct (task.h)
thread负责在进程调度过程中保存或还原CR3控制寄存器的页目录基地址和通用寄存器值,
其他成员 变量基本属于进程调度策略的管理范畴。

struct task_struct
{
    struct List list;
    volatile long state;
    unsigned long flags;

    struct mm_struct *mm;
    struct thread_struct *thread;

    unsigned long addr_limit;   
/*0x0000,0000,0000,0000 - 0x0000,7fff,ffff,ffff user*/
/*0xffff,8000,0000,0000 - 0xffff,ffff,ffff,ffff kernel*/

    long pid;

    long counter;

    long signal;

    long priority;
};

List 双向链表 用于连接各个PCB (lib.h)

struct List
{
    struct List * prev;
    struct List * next;
};

内存空间分布结构体: mm_struct (task.h)

struct mm_struct
{
    pml4t_t *pgd;   //page table point
    
    unsigned long start_code,end_code;
    unsigned long start_data,end_data;
    unsigned long start_rodata,end_rodata;
    unsigned long start_brk,end_brk;
    unsigned long start_stack;  
};

第一个进程 init_task_union

代码布局

在这里插入图片描述

进程控制结构体struct task_struct与进程的内核层樵空间融为一体

// 32KB
#define STACK_SIZE 32768

union task_union
{
    struct task_struct task;
    unsigned long stack[STACK_SIZE / sizeof(unsigned long)];
}__attribute__((aligned (8)));  //8Bytes align


union task_union init_task_union 
__attribute__((__section__ (".data.init_task"))) 
= {INIT_TASK(init_task_union.task)};

在这里插入图片描述

后面开辟的子进程也和init_task_union 类似, 低地址处存放struct task_struct结构体, 而余下的高地址空间则作为进程的栈空间使用

	struct task_struct *tsk = NULL;
	struct thread_struct *thd = NULL;
	struct Page *p = NULL;
	
	color_printk(WHITE,BLACK,"alloc_pages,bitmap:%#018lx\n",*memory_management_struct.bits_map);
    // 开辟一个新的page
	p = alloc_pages(ZONE_NORMAL,1,PG_PTable_Maped | PG_Active | PG_Kernel);

	color_printk(WHITE,BLACK,"alloc_pages,bitmap:%#018lx\n",*memory_management_struct.bits_map);

	tsk = (struct task_struct *)Phy_To_Virt(p->PHY_address);
	color_printk(WHITE,BLACK,"struct task_struct address:%#018lx\n",(unsigned long)tsk);

	memset(tsk,0,sizeof(*tsk));
	// 复制当前的进程内容至新进程里面
	*tsk = *current;

	list_init(&tsk->list);
	list_add_to_before(&init_task_union.task.list,&tsk->list);	
	tsk->pid++;	
	tsk->state = TASK_UNINTERRUPTIBLE;

	thd = (struct thread_struct *)(tsk + 1);
	tsk->thread = thd;	

	memcpy(regs,(void *)((unsigned long)tsk + STACK_SIZE - sizeof(struct pt_regs)),sizeof(struct pt_regs));
    // 保留32k的 task_struct
	thd->rsp0 = (unsigned long)tsk + STACK_SIZE;
	thd->rip = regs->rip;
	// sizeof(struct pt_regs) == 192 Bytes 栈 用于存放 192字节的寄存器值
	thd->rsp = (unsigned long)tsk + STACK_SIZE - sizeof(struct pt_regs);

2. 切换进程

进程间的切换过程主要涉及页目录的切换和各寄存器值的保存与恢复等知识点。

/*
 函数_ switch_to首先将 next进程的内核层楼基地址设置到 TSS结构体对应的成员变量中 。 
 随 后,保存当前进程的 FS与 GS段寄存器值,再将 next进程保存的 FS与 GS段寄存器值还原 。
 */
inline void __switch_to(struct task_struct *prev,struct task_struct *next)
{
	/*
	RSPn Canonical型的战指针(特权级0~2 )
	在IA-32e模式下,处理器允许加载一个空段选择子NULL段选择子(第0个GDT项)到除cs以外的 段寄存器( 3特权级的SS段寄存器不允许加载NULL段选择子)。
	处理器加载NULL段选择子到段寄存 器的过程,并非i卖取GDT的第0项到段寄存器,而是以 一个无效的段描述符来初始化段寄存器 。 
	在发 生特权级切换时, 新的SS段寄存器将强制加载-个NULL段选择子, 而RSP将根据特权级被赋值为RSPn (n=O~2)o 
	把新SS段寄存器设置为NULL段选择子是为了完成远跳转( far CALL, INTn,中断或异常) 动作 ,而旧SS段寄存器和RSP将被保存到新技中 。
	IST ( Interrupt Stack Table,中断枝表)是IA-32e模式为任务状态段引人的新型战指针,其功能与 RSP相同,只不过IST切换中断棋指针时不会考虑特权级切换。
	*/
	init_tss[0].rsp0 = next->thread->rsp0;

	set_tss64(init_tss[0].rsp0, init_tss[0].rsp1, init_tss[0].rsp2, init_tss[0].ist1, init_tss[0].ist2, init_tss[0].ist3, init_tss[0].ist4, init_tss[0].ist5, init_tss[0].ist6, init_tss[0].ist7);
// 函数_ switch_to首先将 next进程的内核层楼基地址设置到 TSS结构体对应的成员变量中 。 
	//随 后,保存当前进程的 FS与 GS段寄存器值,再将 next进程保存的 FS与 GS段寄存器值还原 。
	__asm__ __volatile__("movq	%%fs,	%0 \n\t":"=a"(prev->thread->fs));
	__asm__ __volatile__("movq	%%gs,	%0 \n\t":"=a"(prev->thread->gs));

	__asm__ __volatile__("movq	%0,	%%fs \n\t"::"a"(next->thread->fs));
	__asm__ __volatile__("movq	%0,	%%gs \n\t"::"a"(next->thread->gs));

	color_printk(WHITE,BLACK,"prev->thread->rsp0:%#018lx\n",prev->thread->rsp0);
	color_printk(WHITE,BLACK,"next->thread->rsp0:%#018lx\n",next->thread->rsp0);
}

#define switch_to(prev,next)			\
do{							\
	__asm__ __volatile__ (	"pushq	%%rbp	\n\t"	\
				"pushq	%%rax	\n\t"	\
				"movq	%%rsp,	%0	\n\t"	\
				"movq	%2,	%%rsp	\n\t"	\
				"leaq	1f(%%rip),	%%rax	\n\t"	\
				"movq	%%rax,	%1	\n\t"	\
				"pushq	%3		\n\t"	\  /* kernel_thread_func */
				"jmp	__switch_to	\n\t"	\
/*

In simplified terms:

call address

This will push the updated program counter (which points to the instruction after the call) onto the stack then jump to the address indicated (addressing modes may apply).

ret

This instruction internally pops and address off the stack and jumps to it. This is nicely matched with call so it can return to the instruction after the prior call.

jmp address

This simply jumps to the given address (addressing modes may apply). It doesn't do anything with the stack at all.

So, you can also do this:
push address
ret
Which w
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值