首页 > 系统相关 >Linux-----fork.c进行拆解分析

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

时间:2023-09-25 23:49:14浏览次数:42  
标签:fork pid long current base ----- Linux data tss

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行外层5个函数

#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 为止。

int copy_process(*)进行拆解

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;

标签:fork,pid,long,current,base,-----,Linux,data,tss
From: https://www.cnblogs.com/doubleconquer/p/17729139.html

相关文章

  • SonarQube系列-认证&授权的配置
    参考文档:https://docs.sonarqube.org/latest/instance-administration/security/概述SonarQube具有许多全局安全功能:认证和授权机制强制身份认证委派认证除此之外,还可在group/user级别配置:查看一个已存在的项目访问项目的源代码管理一个项目(设置排除模式,调整该项目......
  • FreeRTOS 原理 --- 任务调度机制
    任务的状态运行态就绪态阻塞态(被动让出CPU)挂起态(主动让出CPU)就绪链表就绪态,每个任务优先级对应一个链表,如下:PRIVILEGED_DATAstaticList_tpxReadyTasksLists[configMAX_PRIORITIES]={0};/*<Prioritisedreadytasks.*/ xPortPendSVHandler中断只会从高......
  • 开源防火墙实战手册(4)-linux/unix基础(3)
    目录配置文件主机名主机名和IP地址的映射域名系统(DNS)解析器配置文件主机名[waterruby@localhost~]$cat/etc/hostnamewaterruby-server主机名和IP地址的映射[waterruby@localhost~]$cat/etc/hosts127.0.0.1localhostlocalhost.localdomainlocalhost4loca......
  • abc321E - Complete Binary Tree
    E-CompleteBinaryTree首先我们只考虑x子树中的答案,非常明显,一定是一个连续的区间,那么我们只需要找到两个端点即可,左端点一直往左走即可,但是右端点要注意,如果走不了,如果左端点存在,说明n就是我们的右端点。处理完子树之后往上跳即可,因为树高只有60#include<cstdio>#include<......
  • Mybatis-Plus 系列:简介和基本使用
    目录一、简介二、特性三、基本使用1、初始化数据库2、初始化工程3、精简SpringBoot相关日志一、简介官网:https://www.baomidou.comMyBatis-Plus是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,主要作用为简化开发、提高效率。二、特性无侵入:只做增强不做改......
  • abc246F - typewriter
    F-typewriter直接容斥即可,每次选出它们的并集。#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#include<queue>#definefo(i,a,b)for(int(i)=(a);(i)<=(b);(i)++)#definefd(i,b,a)for(int(i)=(b);(i)>=(a);(i)-......
  • RAS非对称加解密-RAS加解密和签名和验签,密钥生成器(java代码)
    RAS非对称加解密-RAS加解密和签名和验签,密钥生成器(java代码)RSA算法是一种非对称加解密算法。服务方生成一对RSA密钥,即公钥+私钥,将公钥提供给调用方,调用方使用公钥对数据进行加密后,服务方根据私钥进行解密。1.RAS密钥生成器2.RAS加解密和签名和验签代码13.RAS实现签名......
  • 接口自动化测试--Postman安装和环境部署
    Postman是一款功能强大的网页调试和模拟发送HTTP请求的谷歌插件,可以使用各种方法发送网页HTTP请求,能够运行测试用例1.先安装Postman  官网:https://www.postman.com/downloads/?utm_source=postman-home这里下载的是Windows版本的进行安装的就好环境部署需要部署node.js、cnpm、......
  • ruoyi-cloud免登录访问
    1、首先在nacos中找到gateway.yml编辑,找到ignore下的whites,添加需要名登录的连接2、还要把连接上面的权限校验的代码注解给删除掉......
  • 无涯教程-JavaScript - STANDARDIZE函数
    描述STANDARDIZE函数从以均值和standard_dev为特征的分布返回归一化值。语法STANDARDIZE(x,mean,standard_dev)争论Argument描述Required/OptionalXThevalueyouwanttonormalize.RequiredMeanThearithmeticmeanofthedistribution.RequiredStandard_de......