返回顶部
首页 > 资讯 > 前端开发 > html >如何理解从创建进程到进入Main函数
  • 107
分享到

如何理解从创建进程到进入Main函数

2024-04-02 19:04:59 107人浏览 八月长安
摘要

这篇文章主要讲解了“如何理解从创建进程到进入Main函数”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何理解从创建进程到进入Main函数”吧!创建进程第一

这篇文章主要讲解了“如何理解从创建进程到进入Main函数”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何理解从创建进程到进入Main函数”吧!

创建进程

第一步,创建进程。

linux上,我们要启动一个新的进程,一般通过fork +  exec系列函数来实现,前者将当前进程“分叉”出一个孪生子进程,后者负责替换这个子进程的执行文件,来执行子进程的新程序文件。

这里的fork、exec系列函数,是操作系统提供给应用程序的api函数,在其内部最终都会通过系统调用,进入操作系统内核,通过内核中的进程管理机制,来完成一个进程的创建。

操作系统内核将负责进程的创建,主要有下面几个工作要做:

  • 创建内核中用于描述进程的数据结构,在Linux上是task_struct

  • 创建新进程的页目录、页表,用于构建新进程的内存地址空间

在Linux内核中,由于历史原因,Linux内核早期并没有线程的概念,而是用任务:task_struct来描述一个程序的执行实例:进程。

在内核中,一个任务对应就是一个task_struct,也就是一个进程,内核的调度单元也是一个个的个task_struct。

后来,多线程的概念兴起,Linux内核为了支持多线程技术,task_struct实际上表示的变成了一个线程,通过将多个task_struct合并为一组(通过该结构内部的组id字段)再来描述一个进程。因此,Linux上的线程,也称为轻量级进程。

系统调用fork的一个重要使命就是要去创建新进程的task_struct结构,创建完成后,进程就拥有了调度单元。随后将开始可以参与调度并有机会获得执行。

加载可执行文件

通过fork成功创建进程后,此时的子进程和父进程相当于一个细胞进行了有丝分裂,两个进程“几乎”是一模一样的。

而要想子进程执行新的程序,在子进程中还需要用到exec系列函数来实现对进程可执行程序的替换。

exec系列函数同样是系统调用的封装,通过调用它们,将进入内核sys_execve来执行真正的工作。

这个工作细节比较多,其中有一个重要的工作就是加载可执行文件到进程空间并对其进行分析,提取出可执行文件的入口地址。

我们使用C、c++等高级语言编写的代码,最终通过编译器会编译生成可执行文件,在Linux上,是ELF格式,在windows上,称之为PE文件。

无论是ELF文件还是PE文件,在各自的文件头中,都记录了这个可执行文件的指令入口地址,它指示了程序该从哪里开始执行。

这个入口指向哪里,是我们的main函数吗?这里卖一个关子,先来解决在这之前的一个问题:进程创建后,是如何来到这个入口地址的?

不管在Windows还是Linux上,应用线程都会经常在用户空间和内核空间来回穿梭,这可能出现在以下几种情况发生时:

  • 系统调用

  • 中断

  • 异常

从内核返回时,线程是如何知道自己从哪里进来的,该回到应用空间的哪里去继续执行呢?

答案是,在进入内核空间时,线程将自动保存上下文(其实就是一些寄存器的内容,比如指令寄存器EIP)到线程的堆栈上,记录自己从哪里来的,等到从内核返回时,再从堆栈上加载这些信息,回到原来的地方继续执行。

前面提到,子进程是通过sys_execve系统调用进入到内核中的,在后面完成可执行文件的分析后,拿到了ELF文件的入口地址,将会去修改原来保存在堆栈上的上下文信息,将EIP指向ELF文件的入口地址。这样等sys_execve系统调用结束时,返回到用户空间后,就能够直接转到新的程序入口开始执行代码。

所以,一个非常重要的特点是:exec系列函数正常情况下是不会返回的,一旦进入,完成使命后,执行流程就会转向新的可执行文件入口。

另外需要提一下的是,在Linux上,除了ELF文件,还支持一些其他格式的可执行文件,如MS-DOS、COFF

除了二进制的可执行文件,还支持shell脚本,这个情况下将会将脚本解释器程序作为入口来启动

从ELF入口到main函数

上面交代了,一个新的进程,是如何执行到可执行文件的入口地址的。

同时也留了一个问题,这个入口地址是什么?是我们的main函数吗?

这里有一个简单的C程序,运行起来后输出经典的hello world:

#include <stdio.h> int main() {     printf("hello, world!\n");     return 0; }

通过GCc编译后,生成了一个ELF可执行文件,通过readelf指令,可以实现对ELF文件的分析,这里可以看到ELF文件的入口地址是0x400430:

如何理解从创建进程到进入Main函数

随后,我们通过反汇编神器,IDA打开分析这个文件,看一下位于0x400430入口的地方是什么函数?

如何理解从创建进程到进入Main函数

可以看到,入口地方是一个叫做 _start 的函数,并不是我们的main函数。

在_start的结尾,调用了 __libc_start_main 函数,而这个函数,位于libc.so中。

你可能疑惑,这个函数是哪里冒出来的,我们的代码中并没有用到它呢?

其实,在进入main函数之前,还有一个重要的工作要做,这就是:C/C++运行时库的初始化。上面的 __libc_start_main  就是在完成这一工作。

在通过GCC进行编译时,编译器将自动完成运行时库的链接,将我们的main函数封装起来,由它来调用。

如何理解从创建进程到进入Main函数

glibc是开源的,我们可以在GitHub上找到这个项目的libc-start.c文件,一窥 __libc_start_main  的真面目,我们的main函数正是被它在调用。

完整流程

到这里,我们梳理了,从进程创建fork,到通过exec系列函数完成可执行文件的替换,再到执行流程进入到ELF文件的入口,再到我们的main函数的完整流程。

如何理解从创建进程到进入Main函数

Windows上的一些区别

下面简单介绍下Windows上这一流程的一些差异。

首先是创建进程的环节,Windows系统将fork+exec两步合并了一步,通过CreateProcess系列函数一步到位,在其参数中指定子进程的可执行文件路径。

不同于Linux上进程和线程的边界模糊,在Windows操作系统上,内核是有明确的进程和线程概念定义,进程用EPROCESS结构表示,线程用ETHREAD结构表示。

所以在Windows上,进程相关的工作准备就绪后,还需要单独创建一个参与内核调度的执行单元,也就是进程中的第一个线程:主线程。当然,这个工作也封装在了CreateProcess系列函数中了。

新进程的主线程创建完成后,便开始参与系统调度了。主线程从哪里开始执行呢?内核在创建时就明确进行了指定:nt!KiThreadStartup,这是一个内核函数,线程启动后就从这里开始执行。

线程从这里启动后,再通过Windows的异步过程调用APC机制执行提前插入的APC,进而将执行流程引入应用层,去执行Windows进程应用程序的初始化工作,比如一些核心DLL文件的加载(Kernel32.dll、ntdll.dll)等等。

随后,再次通过APC机制,再转向去执行可执行文件的入口点。

这后面和Linux上的机制类似,同样没有直接到main函数,而是需要先进行C/C++运行时库的初始化,这之后经过运行时函数的包装,才最终来到我们的main函数。

下面是Windows上,从创建进程到我们的main函数的完整流程:

如何理解从创建进程到进入Main函数

感谢各位的阅读,以上就是“如何理解从创建进程到进入Main函数”的内容了,经过本文的学习后,相信大家对如何理解从创建进程到进入Main函数这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

--结束END--

本文标题: 如何理解从创建进程到进入Main函数

本文链接: https://lsjlt.com/news/84402.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 如何理解从创建进程到进入Main函数
    这篇文章主要讲解了“如何理解从创建进程到进入Main函数”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何理解从创建进程到进入Main函数”吧!创建进程第一...
    99+
    2024-04-02
  • 《Linux从练气到飞升》No.17 进程创建
       🕺作者: 主页 我的专栏C语言从0到1探秘C++数据结构从0到1探秘Linux菜鸟刷题集 😘欢迎关注:👍点赞🙌收藏✍️留言 🏇码字不易,你的&#...
    99+
    2023-08-31
    linux 服务器 后端 进程 centos
  • 如何创建Linux进程
    如何创建Linux进程,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。下面在Linux系统中创建进程使用fork、vfork、clone这三个命令进行操作。fork  ...
    99+
    2023-06-28
  • java中main函数如何理解
    本篇文章给大家分享的是有关java中main函数如何理解,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。前言前段时间看到一道面试题:“main函数可以被重载么?”,当时就蒙圈了,...
    99+
    2023-06-26
  • Linux系统如何创建进程
    这篇文章主要为大家展示了“Linux系统如何创建进程”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Linux系统如何创建进程”这篇文章吧。fork    fork创建一个进程...
    99+
    2023-06-28
  • Docker如何创建并进入mysql容器
    这篇文章主要介绍“Docker如何创建并进入mysql容器”,在日常操作中,相信很多人在Docker如何创建并进入mysql容器问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Docker如何创建并进入mysq...
    99+
    2023-07-05
  • Hadoop进入sbin后如何创建文件
    要在Hadoop的sbin目录中创建文件,您可以使用以下命令: 首先,在终端中进入Hadoop的sbin目录,可以使用以下命令:...
    99+
    2024-03-05
    hadoop
  • Linux的进程是如何创建的
    在Linux中,进程是通过调用系统调用fork()或clone()来创建的。当一个进程调用fork()时,操作系统会创建一个新的子进...
    99+
    2024-04-02
  • 从阿里云如何进入数据库
    在今天的数字化时代,数据已经成为企业的重要资产。然而,如何有效地管理和分析这些数据却是一个挑战。本文将详细讲解如何从阿里云进入数据库,以充分利用阿里云的数据资源。 一、阿里云数据库的概述阿里云数据库是阿里云推出的一种云数据库服务,它提供了高...
    99+
    2023-11-17
    阿里 数据库
  • Node.js中创建和管理外部进程详解
    Node被设计用来高效的处理I/O操作,但是你应该知道,有些类型的程序并不适合这种模式。比如,如果你打算用Node处理一个CPU密集的任务,你可能会堵塞事件循环,并因此降低了程序的响应。替代办法是,把CPU...
    99+
    2022-06-04
    建和 详解 进程
  • JavaScript 构造函数模式:从入门到精通的进阶指南
    ...
    99+
    2024-04-02
  • 如何理解Python进程
    这篇文章主要讲解了“如何理解Python进程”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何理解Python进程”吧!前言进程,一个新鲜的字眼,可能有些人并不了解,它是系统某个运行程序的载...
    99+
    2023-06-15
  • 【Linux】Linux进程控制 --- 进程创建、终止、等待、替换、shell派生子进程的理解…
    柴犬: 你好啊,屏幕前的大帅哥or大美女,和我一起享受美好的今天叭😃😃😃 文章目录 一、进程创建1.调用fork之后,内核都做了什么?2.如何...
    99+
    2023-09-08
    linux 运维 服务器
  • Vue 状态管理进阶:从创建状态到实现响应式更新
    创建状态 在 Vue 中,可以通过以下方式来创建状态: // 在 Vue 实例中创建状态 this.count = 0; // 在组件中创建状态 export default { data() { return { ...
    99+
    2024-02-24
    Vue 状态管理 响应式更新 状态管理工具
  • chatgpt赋能python:Python如何创建窗口——从入门到精通
    Python如何创建窗口——从入门到精通 Python是一种高级编程语言,它的易读性和清晰简洁的语法使它成为许多人喜欢学习的编程语言之一。Python的一个主要特色是其丰富的库和模块。在本文中,我们将...
    99+
    2023-09-30
    python chatgpt 开发语言 计算机
  • 深入解析 C++ 函数指针:从原理到应用
    c++++ 函数指针是指向函数的内存区域,存储函数入口地址,允许动态调用。它们用于回调函数、事件处理和动态函数调用。例如,可以使用函数指针将字符串转换大写的函数 touppercase ...
    99+
    2024-04-30
    c++ 函数指针
  • 如何创建 PHP 函数库并从 Composer 加载它?
    在 php 中通过 composer 加载函数库的步骤:创建函数库文件和 composer.json 文件,定义命名空间并加载函数。安装 composer 并使用它安装函数库。使用 re...
    99+
    2024-04-28
    composer php函数库
  • 如何从CSV文件导入数据到Analytics Cloud里创建模型和Story
    如何从CSV文件导入数据到Analytics Cloud里创建模型和Story,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。从SAP官网下载一个供学习使用的csv文件:ht...
    99+
    2023-06-03
  • 【C语言进阶】最常用的库函数大全——从入门到精通
    目录 前言: 一.字符串函数 1.strlen——求字符串长度 strlen 2.长度不受限制的字符串函数 a.strcpy——字符串拷贝 strcpy  b.strcat——追加字符串 strcat  c.strcmp——字符串比较 st...
    99+
    2023-10-20
    c语言 java 前端
  • 如何进行C语言函数栈帧的创建和销毁分析
    如何进行C语言函数栈帧的创建和销毁分析,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。以下是我们平时接触过,但不了解的问题:1.为什么局部变量在未赋值前是随机的。2.局部变量...
    99+
    2023-06-22
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作