linux0.11中的fork实现和一些注意事项

2023-11-03

最近几天刚开始在读代码,读的挺爽的,不过碰到了很多问题,慢慢来吧。。
有一个fork的系统调用一直没弄明白,查了一下再CSDN上有一篇好文,记录一
下,顺便自己添加一些自己的想法。下面是思路和提问。

内核是linux0.11版本,里面的fork()用于创建子进程。
但我现在在找这个函数的具体定义时遇到了一些困难。
先把我的查找过程说下:
1、init里的main.c中有static inline _syscall0 (int, fork);
2、在unistd.h中找到_syscall0是个宏,定义如下
Assembly code
#define _syscall0(type,name) /
type name(void) /
{ /
long __res; /
__asm__ volatile ("int $0x80" /                 // 调用系统中断0x80。
    : "=a" (__res) /                            // 返回值??eax(__res)。
    : "0" (__NR_##name)); /                     // 输入为系统中断调用号__NR_name。
if (__res >= 0) /                               //如果返回值>=0,则直接返回该值。
    return (type) __res; /                      // 否则置出错号,并返回-1。
errno = -__res; /
return -1; /
}
进行参数替换
感觉是int fork(void){....};
具体实现主要是:
__asm__ volatile ( "int $0x80" /     // 调用系统中断0x80。
:"=a" (__res) /                      // 返回值??eax(__res)。
:"" (__NR_##name)); 其中的__NR_##name是2,用来在一个指针函数的数组中作为索引号用的
这个数组定义如下:
C/C++ code
typedef int ( * fn_ptr) ();
在sys.h中 fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read, sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link, sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod, sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount, sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm, sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access, sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir, sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid, sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys, sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit, sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid, sys_getpgrp, sys_setsid, sys_sigaction, sys_sgetmask, sys_ssetmask, sys_setreuid, sys_setregid };
而__NR_##name的值为2感觉正好对应着上面那个数组里的sys_call_table[2](也就是sys_fork)
感觉有点眉目了
3、继续解释这个指令
Assembly code
__asm__ volatile ( "int $0x80" / // 调用系统中断0x80。
:"=a" (__res) / // 返回值eax (__res)。
:"" (__NR_ ##name));


感觉主要是调用系统中断0x80
4、在汇编代码system_call.s中找到该中断的中断处理函数
大概如下
#### int 0x80 --linux 系统调用入口点(调用中断int 0x80,eax 中是调用号)。
Assembly code
.align 2
_system_call:
cmpl $nr_system_calls-1,%eax # 调用号如果超出范围的话就在eax 中置-1 并退出。
ja bad_sys_call 
5.5 system_call.s 程序 
push %ds # 保存原段寄存器值。 
push %es 
push %fs
pushl %edx # ebx,ecx,edx 中放着系统调用相应的C 语言函数的调用参数。
pushl %ecx # push %ebx,%ecx,%edx as parameters
pushl %ebx # to the system call
movl $0x10,%edx # set up ds,es to kernel space 
mov %dx,%ds # ds,es 指向内核数据段(全局描述符表中数据段描述符)。 
mov %dx,%es
movl $0x17,%edx # fs points to local data space 
mov %dx,%fs # fs 指向局部数据段(局部描述符表中数据段描述符)。


# 下面这句操作数的含义是:调用地址 = _sys_call_table + %eax * 4。参见列表后的说明。
# 对应的C 程序中的sys_call_table 在include/linux/sys.h 中,其中定义了一个包括72 个
# 系统调用C 处理函数的地址数组表。
call _sys_call_table(,%eax,4)
如果上面的eax放的是__NR_##name,即2
那么call _sys_call_table(,%eax,4)
相当于call sys_fork()
在该文件的下面一点找到该函数的定义
#### sys_fork()调用,用于创建子进程,是system_call 功能2。原形在include/linux/sys.h 中。
# 首先调用C 函数find_empty_process(),取得一个进程号pid。若返回负数则说明目前任务数组
# 已满。然后调用copy_process()复制进程。
Assembly code
.align 2
_sys_fork:
call _find_empty_process # 调用find_empty_process()(kernel/fork.c,135)。
testl %eax,%eax 
js 1f push %gs
pushl %esi
pushl %edi
pushl %ebp
pushl %eax 
call _copy_process       # 调用C 函数copy_process()(kernel/fork.c,68)。
addl $20,%esp            # 丢弃这里所有压栈内容。 
1: ret
里面的_find_empty_process和copy_process()就是fork的主要子函数了.

问题1:请问我这个思路是不是对的。
问题2:汇编调用C函数为什么名字不一样啊
汇编里是_find_empty_process 而在fork.c里该函数名字是find_empty_process()
汇编这样调用能找到这个函数么????
问题3:__asm__ volatile ( "int $0x80" /         // 调用系统中断0x80。
:"=a" (__res) /                                 // 返回值??eax(__res)。
:"" (__NR_##name));这个内嵌汇编里的__NR_##name值是怎么传给eax的?以让后面的call _sys_call_table(,%eax,4)正确调用sysfork;
希望能给我详细讲解这条指令:__asm__ volatile (仅这条)调用中断int $0x80的具体操作
问题1:
差不多,但是有一個地方需要強調一下。static inline _syscall0 (int, fork),這個實際上隱含指出了系統調用的參數個數,注意這裡是以0結尾的,也就是說這個系統調用不需要傳遞參數。你應該還能從代碼中看到 _syscall1,_syscall2這樣的定義。
问题2:
gcc的C編譯器compile的時候,會在函數名字前面加上一個下劃綫,所以在彙編裏面調用C的函數的時候,要手動的加上這個下劃綫。注意:這裡說的是C編譯器,不包括C++的,C++的編譯器和C的有很多的不同,所以才會有extern "C"的存在。
问题3:
开头你自己也说了NR_name 是定义的好的值。具体在 <>unistd.h>头文件里。怎么传给eax的?:"" (__NR_
##name) 这条语句就是传给eax的意思。引号中省略表示用和输出一样的寄存器,也就是输出中的a(eax)。gcc嵌入汇编的格式你应该了解吧。int $0x80就是调用系统中断了,设置系统中断的操作在sched.c 最后一行 set_system_gate(0x80,&system_call); set_system_gate是一个嵌入式宏汇编语句,设置中断描述符表表项。具体实现在system.h.这样调用0x80系统中断后,执行的是 system_call  eax中存放的是定义好的NR_name的值,以后处理过程你都说了。


fork函数的实现(返回2次原因)
在Linux中,有一个特殊的函数fork()。这个函数会向父进程返回子进程的进程号PID,而向子进程返回0。有没有想过一个函数怎么可能有两个不同的返回值呢?
QUOTE:
#include
#include
int main(int argc, char **argv) {
if (fork() == 0) {
printf("I am the child process./n");
} else {
printf("I am the parent process./n");
}
}

在Linux0.11中,每个进程都有一个进程控制块结构task_struct。系统支持最多64个进程,定义在全局数组task中。
QUOTE:
struct task_struct * task[NR_TASKS] = {&(init_task.task), };

其中进程0为初始进程,其它所有的进程都是通过fork产生的。用户态的fork函数最终调用系统调用sys_fork()。sys_fork()系统调用分成2步完成,第一步调用函数find_empty_process(),在task[]数组中找一项空闲项;第二步调用copy_process()函数,复制进程。

对所有fork()调用产生的进程,通过递增并循环的方式为其分配进程号。有一个全局变量last_pid用来记录上次使用的进程号:

long last_pid=0;

在find_empty_process中,不断递增last_pid,寻找第一个未被其它进程使用的进程号作为新进程的进程号。如果递增后的值超出正数表示范围,则重新从1开始。

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

struct tss_struct tss;

因此,一旦在task[]数组中找到空闲项和进程号,我们就可以为该进程的进程控制块结构申请一个页面的内存。这个工作是在copy_process() 函数中完成的。当然copy_process()函数的最主要的任务是为子进程复制父进程信息,并设置子进程的任务状态段,其中最关键的两步是:

1. 把子进程tss中的eip设置为父进程系统调用返回地址,这样当子进程被调度程序选中后,将从父进程的fork()返回地址处开始执行。

p->tss.eip = eip;

2. 把子进程tss中的eax设置为0,而eax是存放函数返回值的地方,这样子进程中返回的是0。注意子进程并没有执行fork()函数,子进程的系统堆栈没有进行过操作,当然不会有像父进程那样的fork函数调用。但是当子进程开始运行时,就好像它从fork中返回。

p->tss.eax = 0;
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux0.11中的fork实现和一些注意事项 的相关文章

  • “rep stos”x86 汇编指令序列有什么作用?

    我最近偶然发现了以下汇编指令序列 rep stos dword ptr edi For ecx重复 存储内容eax到哪里edi指向 递增或递减edi 取决于方向标志 每次 4 个字节 通常 这用于memset型操作 通常 该指令简单地写成r
  • 在Python中计算结构体的CRC

    我有以下结构 来自 C 中的 NRPE 守护程序代码 typedef struct packet struct int16 t packet version int16 t packet type uint32 t crc32 value
  • 如何从“git log”中查看 Git 中的特定版本?

    My git log显示为 enter code here git trial git log commit 4c5bc66ae50780cf8dcaf032da98422aea6e2cf7 Author king lt email pro
  • 通过 SSH 将变量传递给远程脚本

    我正在通过 SSH 从本地服务器在远程服务器上运行脚本 首先使用 SCP 复制该脚本 然后在传递一些参数时调用该脚本 如下所示 scp path to script server example org another path ssh s
  • 为什么我的空循环在 Intel Skylake CPU 上作为函数调用时运行速度是原来的两倍?

    我正在运行一些测试来比较 C 和 Java 并遇到了一些有趣的事情 在 main 调用的函数中 而不是在 main 本身中 运行具有优化级别 1 O1 的完全相同的基准代码 导致性能大约翻倍 我正在打印 test t 的大小 以毫无疑问地验
  • 如何在 Linux 中使用单行命令获取 Java 版本

    我想通过单个命令获取 Linux 中的 Java 版本 我是 awk 的新手 所以我正在尝试类似的事情 java version awk print 3 但这不会返回版本 我将如何获取1 6 0 21从下面的Java版本输出 java ve
  • Haskell 类型系统的细微差别

    我一直在深入了解 haskell 类型系统的本质 并试图了解类型类的要点 我已经学到了很多东西 但我在下面的代码片段上遇到了困难 使用这些类和实例定义 class Show a gt C a where f Int gt a instanc
  • linux下如何获取昨天和前天?

    我想在变量中获取 sysdate 1 和 sysdate 2 并回显它 我正在使用下面的查询 它将今天的日期作为输出 bin bash tm date Y d m echo tm 如何获取昨天和前天的日期 这是另一种方法 对于昨天来说 da
  • 程序集比较标志理解

    我正在努力理解汇编程序中的以下代码片段 if EAX gt 5 EBX 1 else EBX 2 在汇编程序中 可以写如下 根据我的书 模拟jge操作说明 https www felixcloutier com x86 jcc您通常会使用
  • Linux 中的电源管理通知

    在基于 Linux 的系统中 我们可以使用哪些方法 最简单的方法 来获取电源状态更改的通知 例如 当计算机进入睡眠 休眠状态等时 我需要这个主要是为了在睡眠前保留某些状态 当然 在计算机唤醒后恢复该状态 您只需配置即可获得所有这些事件acp
  • GLSL 中的二阶函数?

    我正在寻找一种方法来使用一个函数作为 GLSL 中另一个函数的参数 在常规 C 中 可以通过传递函数指针作为函数参数来模拟它 似乎其他语言 如 HLSL 现在提供了处理高级构造 如高阶函数 的方法 或者可以使用以下命令来模拟它们巧妙利用 H
  • 在 shell 脚本中查找和替换

    是否可以使用 shell 在文件中搜索然后替换值 当我安装服务时 我希望能够在配置文件中搜索变量 然后在该值中替换 插入我自己的设置 当然 您可以使用 sed 或 awk 来完成此操作 sed 示例 sed i s Andrew James
  • CPU寄存器和多任务处理

    我目前正在学习汇编 我很困惑 CPU 寄存器如何与多任务一起工作 所以在多任务系统中 CPU可以随时暂停某个程序的执行并运行另一个程序 那么在这一步中寄存器值是如何保存的呢 寄存器是压入堆栈还是以其他方式 CPU 寄存器如何与多任务一起工作
  • 将参数传递给调试器中的 perl 文件并在系统执行的文件中设置断点

    因此 我使用 perl d file pl 在 perl 调试器中运行一个文件 但 file pl 也应该接受参数 如何向 file pl 提供参数 还有一个问题 file pl 中有这一行 system file2 pl 如果 file2
  • 如果仅使用第一个元素,是否必须为整个结构分配内存?

    我有一个结构 其中第一个元素被测试 并且根据其值 结构的其余部分将被读取或不会被读取 在第一个元素的值指示结构的其余部分不会被读取的情况下 我是否必须为整个结构或仅第一个元素分配足够的内存 struct element int x int
  • 如何确保 numpy BLAS 库可用作动态加载库?

    The theano安装文档 http www deeplearning net software theano install html troubleshooting make sure you have a blas library指
  • 如何从类似于 eclipse 的命令行创建可运行的 jar 文件

    我知道 eclipse 会生成一个可运行的 jar 文件 其中提取并包含在该 jar 文件中的所有库 jar 文件 从命令提示符手动创建 jar 文件时如何执行类似的操作 我需要将所有 lib jar 解压到类文件夹中吗 目前我正在使用 j
  • “grep -q”的意义是什么

    我正在阅读 grep 手册页 并遇到了 q 选项 它告诉 grep 不向标准输出写入任何内容 如果发现任何匹配 即使检测到错误 也立即以零状态退出 我不明白为什么这可能是理想或有用的行为 在一个程序中 其原因似乎是从标准输入读取 处理 写入
  • 如何从linux命令行运行.exe可执行文件? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我在 Windows 中有一个 abc exe 可执行文件 我可以使用 DOS 命令提示来执行此应用程序 并为其提供一些运行时变量 我想从
  • 如何调用位于其他目录的Makefile?

    我正在尝试这样做 我想打电话给 make Makefile存在于其他目录中 abc可以使用位于不同目录中的 shell 脚本的路径 我该怎么做呢 由于 shell 脚本不允许我cd进入Makefile目录并执行make 我怎样才能编写she

随机推荐

  • 写五个以科技为主题的html页面,要求用css实现,请写出实现代码

    在此基础上 我们可以创建五个以科技为主题的 HTML 页面 并使用 CSS 来实现设计 科技新闻页面 HTML 代码
  • Fibonacci数列(斐波那契数列)

    斐波那契数列 Fibonacci sequence 又称黄金分割数列 因数学家列昂纳多 斐波那契 Leonardoda Fibonacci 以兔子繁殖为例子而引入 故又称为 兔子数列 指的是这样一个数列 1 1 2 3 5 8 13 21
  • TCP的超时时间间隔的简单理解

    网上查了许多资料 都比较抽象 自我理解记录篇 本文适用于查了许多资料 但觉得还是比较抽象 有基础相关知识的人阅读 样本RTT SampleRTT EstimatedRTT 1 EstimatedRTT aSampleRTT 参考值 0 12
  • JS--变量

    变量在使用时分为两步 声明变量 赋值 1 声明变量 var age 声明一个名称为age的变量 var是一个JS关键词 用来声明变量 使用该关键词声明变量后 计算机会 自动为变量分配内存空间 不需要程序员管 age是程序员定义的变量名 我们
  • 《C++ primer plus》精炼(OOP部分)——对象和类(4)

    学习是人类进步的阶梯 也是个人成功的基石 罗伯特 肯尼迪 文章目录 友元函数 利用友元函数重载 lt lt 运算符 重载部分示例 矢量类 友元函数 先看看在上一章中我们作为例子的代码 class Student string name in
  • QComboBox 设置代理组件

    背景 QComboBox 是Qt中比较常用的一个输入控件 用于实现一个文本下拉列表 在简单的应用场景中 QComboBox 完全可以满足要求 但是项目实践过程中会遇到以下问题 需要实现比较复杂的下拉列表 比如同时显示图标 文字 按钮等 QC
  • CUDA学习(九)想好好解释一下利用shared memory去做matrixMul

    代码来自CUDA Samples v9 1 0 Simple matrixMul 主要注释了matrixMulCUDA C A B 它的理念是把矩阵分成小块 每个线程利用两层循环 大循环在迭代subMatrix 小循环则是迭代每个小subM
  • 【Android UI设计与开发】第08期:底部菜单栏(三)Fragment+FragmentTabHost实现仿新浪微博底部菜单栏

    PS 这是转载CSDN作者yangyu20121224的文章 真的非常不错 它主要讲述如何通过Fragment碎片实现点击TabHost切换页面 可惜作者很长时间没有更新了 他写的那系列文章都很实用 以后在做Android布局时会用到该篇文
  • Reading and Writing Images_OpenCV 3.0.0-dev documentation

    OpenCV 3 0 0 dev documentation OpenCV API Reference imgcodecs Image file reading and writing https docs opencv org 3 0 b
  • Python While 循环语句

    Python 编程中 while 语句用于循环执行程序 即在某条件下 循环执行某段程序 以处理需要重复处理的相同任务 其基本形式为 while 判断条件 condition 执行语句 statements 执行语句可以是单个语句或语句块 判
  • iOS开发抓包工具之Charles使用

    在iOS开发过程中 抓包是常见的必备技能之一 这里我们主要介绍一下Mac下的抓包利器Charles 文中版本 4 0 1 它可以非常方便的抓取Http Https请求 从而提高我们的开发效率 本文中不提供破解版安装使用说明 网上一大堆 建议
  • 单链表头插和尾插详解

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 前言 一 链表定义 二 代码解析 1 程序代码 2 程序图解 前言 链表是一种常见的数据元素 它和我们之前学的数组不同 储存元素个数不受限制 操作难度也更大 为加强
  • Linux man手册一篇通

    linux man man是Linux的一个非常强的的命令 它的全称为manual 手册 有了它不用联网 轻松获取相关信息 man手册章节组成 man手册通常由多个类型章节组成 章节类型定义如下 章节 内容 中文 1 Executable
  • 攻防世界---getit

    getit 题目描述 菜鸡发现这个程序偷偷摸摸在自己的机器上搞事情 它决定一探究竟 题目附件 https adworld xctf org cn media task attachments e3dd9674429f4ce1a25c08ea
  • Java语音技术之FreeTTS

    转自 微点阅读 https www weidianyuedu com content 0017809736406 html Java语音技术之FreeTTS Java提供了语音API技术用于支持应用程序或Web页面中的人机语音交互 其核心技
  • Flash Download failed - “Cortex-M3“解决方法

    前几天在玩STM32F1的时候 烧录代码的时候出现这样一个错误 Error Flash Download failed Cortex M3 首先肯定要看配置问题 采用不同的仿真器 需要配置的选项就不同 这个完全可以自己看产品说明 当确定配置
  • vscode资源管理器隐藏配置文件的显示

    资源管理器显示太多 开头的文件 大多是配置文件 显示过多造成查看混乱 如何隐藏这些文件呢 左下角点击设置按钮中的设置 点击如图所示的按钮 在弹出的settings json 中添加如下内容并保存 files exclude true 注意
  • Nginx报错[emerg]: unknown directive “”

    前言 今天用记事本修改Nginx中的conf配置文件 由于自己没有备份 导致出问题 解决了两个小时都没有解决掉 不过遇到问题是好事 可以解决 如果有备份遇到问题也许不会一直去解决吧 但是 如果工作中的项目 由于自己的不备份 随意改动 影响项
  • 【C++入门】C/C++的(静态)断言用法总结

    1 C语言的断言 1 1 函数原型 include
  • linux0.11中的fork实现和一些注意事项

    最近几天刚开始在读代码 读的挺爽的 不过碰到了很多问题 慢慢来吧 有一个fork的系统调用一直没弄明白 查了一下再CSDN上有一篇好文 记录一 下 顺便自己添加一些自己的想法 下面是思路和提问 内核是linux0 11版本 里面的fork