linux0.11内核分析之进程创建

linux0.11内核分析之进程创建

概述

linux0.11内核分析之进程创建_第1张图片
对用户而言,进程创建常用的是fork函数,对于进程创建的分析也主要围绕着该函数执行的一系列操作进行。

源码分析阅读

1、fork

翻libc太麻烦了,所以从大佬的文章中嫖来了下面这段代码

int fork(void) 
{ 
long __res; 
__asm__ volatile ("int $0x80" 
    : "=a" (__res)       //将__res赋给eax
    : "0" (__NR_fork));  //将2赋值给eax
if (__res >= 0) //int 0x80中断返回就执行这句
    return (type) __res; 
errno = -__res; 
return -1; 
}

分析这段源码,其实就是普通的系统调用中的用户态调用函数,就是将fork函数的系统调用号压入eax寄存器,随后传入system_call函数转到核心态调用sys_fork函数。此处不再赘述系统调用过程,感兴趣的可以点这里。

2、sys_fork
sys_fork:
	call find_empty_process
	testl %eax,%eax
	js 1f
	push %gs
	pushl %esi
	pushl %edi
	pushl %ebp
	pushl %eax
	call copy_process
	addl $20,%esp
1:	ret

sys_fork函数的实现逻辑较为清晰,首先在操作系统的全局进程表中找到一个可用的进程号,随后将需要复刻的进程信息复制到该可用进程的进程控制块中

3、find_empty_process
int find_empty_process(void)
{
	int i;
	repeat:
	if ((++last_pid)<0) last_pid=1;
	for(i=0 ; i<NR_TASKS ; i++)
		if (task[i] && task[i]->pid == last_pid) goto repeat;
    for(i=1 ; i<NR_TASKS ; i++)
        if (!task[i])
            return i;
    return -EAGAIN;  // -11
}

函数遍历全局变量task数组,筛选出未被占用的进程号,随后返回进程号,若没有空闲的进程号,则返回**-EAGAIN**

4、copy_process
/*
 *  Ok, this is the main fork-routine. It copies the system process
 * information (task[nr]) and sets up the necessary registers. It
 * also copies the data segment in it's entirety.
 */
int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,
		long ebx,long ecx,long edx,
		long fs,long es,long ds,
		long eip,long cs,long eflags,long esp,long ss)
{
	struct task_struct *p;
	int i;
	struct file *f;

	p = (struct task_struct *) get_free_page();
	if (!p)
		return -EAGAIN;
	task[nr] = p;  //将新任务结构指针放入任务数组中,其中nr 是由前面find_empty_process()返回的任务号
	__asm__ volatile ("cld");
	*p = *current;	
	p->state = TASK_UNINTERRUPTIBLE;
	p->pid = last_pid;
	p->father = current->pid;
	p->counter = p->priority;
	p->signal = 0;
	p->alarm = 0;
	p->leader = 0;		
	p->utime = p->stime = 0;
	p->cutime = p->cstime = 0;
	p->start_time = jiffies;
	p->tss.back_link = 0;
	p->tss.esp0 = PAGE_SIZE + (long) p;
	p->tss.ss0 = 0x10;
	p->tss.eip = eip;
	p->tss.eflags = eflags;
	p->tss.eax = 0;
	p->tss.ecx = ecx;
	p->tss.edx = edx;
	p->tss.ebx = ebx;
	p->tss.esp = esp;
	p->tss.ebp = ebp;
	p->tss.esi = esi;
	p->tss.edi = edi;
	p->tss.es = es & 0xffff;
	p->tss.cs = cs & 0xffff;
	p->tss.ss = ss & 0xffff;
	p->tss.ds = ds & 0xffff;
	p->tss.fs = fs & 0xffff;
	p->tss.gs = gs & 0xffff;
	p->tss.ldt = _LDT(n r);
	p->tss.trace_bitmap = 0x80000000;
	if (last_task_used_math == current)
		__asm__("clts ; fnsave %0"::"m" (p->tss.i387));
	if (copy_mem(nr,p)) {
		task[nr] = NULL;
		free_page((long) p);
		return -EAGAIN;
	}
	for (i=0; i<NR_OPEN;i++)
		if (f=p->filp[i])
			f->f_count++;
	if (current->pwd)
		current->pwd->i_count++;
	if (current->root)
		current->root->i_count++;
	if (current->executable)
		current->executable->i_count++;
	set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
	set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));
	p->state = TASK_RUNNING;	
	return last_pid;
}

此函数中复制进程状态(不复制leader成员),复制进程页(不复制管理堆栈),以及对必要寄存器赋值

同时,进程控制块中还保存有进程的任务状态段数据结构tss,用于存储处理器管理进程的所有信息。也就是说,在任务切换过程中,首先将处理器中各寄存器的当前值被自动保存当前进程的tss中;然后,下一进程的tss被加载并从中提取出各个值送到处理器的寄存器中。由此可见,通过在tss中保存任务现场各寄存器状态的完整映象,实现任务的切换。(本段来自这篇博文)

流程分析

  • 查找空进程
  • 申请并设置新进程控制块(包括现场)
  • 内存拷贝(设置 地址空间)
  • 修改GDT
  • 返回

你可能感兴趣的:(源码,linux,内核,多进程)