Linux-----fork.c进行拆解分析

发布时间 2023-09-25 23:34:28作者: 不会笑的孩子

fork()函数说明

fork() 是一个用于创建新进程的系统调用,fork可以在父进程中创建一个子进程。子进程是父进程的副本,frok从父进程继承了大部分资源和状态。

先简单理解一下fork()函数

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
    pid_t pid; // 声明一个进程标识符变量

    // 使用 fork() 创建子进程
    pid = fork();

    if (pid < 0) {
        // fork() 失败
        perror("Fork failed");
        exit(1);
    } else if (pid == 0) {
        // 子进程
        printf("This is the child process (PID=%d).\n", getpid());
    } else {
        // 父进程
        printf("This is the parent process (PID=%d) of the child process (PID=%d).\n", getpid(), pid);
    }

    // 父子进程都会执行以下代码
    printf("This is common code executed by both parent and child processes.\n");

    return 0;
}

linux0.11源码fork只有136行

#include <errno.h>//一组错误码
#include <linux/sched.h>//进程调度和进程管理struct task_struct。
#include <linux/kernel.h>//内核编程所需的通用功能
#include <asm/segment.h>//特定于架构的头文件
#include <asm/system.h> //特定于架构的头文件

//验证给定的物理内存地址是否可以进行写入操作
extern void write_verify(unsigned long address);//extern 关键字用于在一个源文件中引用其他源文件中定义的全局变量或函数。这允许不同的源文件共享全局变量和函数,以实现模块化和可维护的代码结构。

long last_pid=0;
//验证给定内存区域是否可以进行写入操作
void verify_area(void * addr,int size)
{
	unsigned long start;

	start = (unsigned long) addr;
	size += start & 0xfff;
	start &= 0xfffff000;
	start += get_base(current->ldt[2]);
	while (size>0) {
		size -= 4096;
		write_verify(start);
		start += 4096;
	}
}

int copy_mem(int nr,struct task_struct * p)
{
	unsigned long old_data_base,new_data_base,data_limit;
	unsigned long old_code_base,new_code_base,code_limit;

	code_limit=get_limit(0x0f);
	data_limit=get_limit(0x17);
	old_code_base = get_base(current->ldt[1]);
	old_data_base = get_base(current->ldt[2]);
	if (old_data_base != old_code_base)
		panic("We don't support separate I&D");
	if (data_limit < code_limit)
		panic("Bad data_limit");
	new_data_base = new_code_base = nr * 0x4000000;
	p->start_code = new_code_base;
	set_base(p->ldt[1],new_code_base);
	set_base(p->ldt[2],new_data_base);
	if (copy_page_tables(old_data_base,new_data_base,data_limit)) {
		free_page_tables(new_data_base,data_limit);
		return -ENOMEM;
	}
	return 0;
}

/*
 *  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;
	*p = *current;	/* NOTE! this doesn't copy the supervisor stack */
	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;		/* process leadership doesn't inherit */
	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(nr);
	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;	/* do this last, just in case */
	return last_pid;
}

int find_empty_process(void)
{
	int i;//用于遍历进程数组
    // long last_pid = 0
	repeat://标签
		if ((++last_pid)<0) last_pid=1;//确保进程标识符为1
		for(i=0 ; i<NR_TASKS ; i++)
			if (task[i] && task[i]->pid == last_pid) goto repeat;//用于跳转到repeat标签
    //用于查找一个空闲的槽位,找到后返回i
	for(i=1 ; i<NR_TASKS ; i++)
		if (!task[i])
			return i;
	return -EAGAIN;//没有槽位则返回,说明不能找到task[]和task[]->pid 
}

先对int find_empty_process()来进行拆解

功能:找到一个可用的进程槽位,以便在创建新进程时将其分配给新进程。它会遍历已有的进程表中的所有进程,检查它们的 PID 是否与当前分配的 PID 相同,如果相同就说明该 PID 已被占用,需要继续尝试分配下一个 PID。这个过程会一直重复,直到找到一个未被占用的 PID 为止。