在Linux系统中程序是如何执行的?

在Linux系统中,程序的执行是一个复杂而精细的过程,涉及多个步骤。

1.进程创建

在Linux中,进程的创建,除了第一个进程(0号进程)是通过硬编码创建,其他所有进程通常都是通过fork()系统调用来实现,fork()会创建当前进程的一个副本,即子进程。父进程和子进程几乎拥有相同的代码和数据,除了一些必要的信息是不同的,比如进程id(pid)和内核堆栈等。

Linux中有几个与创建进程相关的系统调用函数fork、vfork和clone,这三个系统调用底层都是通过do_fork函数创建进程,只是这三个调用函数的传递参数各有不同。

  1. do_fork首先检查是否允许创建新进程,包括检查系统的最大进程数限制等。
  2. 然后进入复制阶段,调用copy_process(),此时会复制task_struct结构和根据情况处理mm_struct结构的复制或共享。
  3. 最后,调用wake_up_new_task(),新进程被加入到调度器的就绪队列中,并设置相应的状态,使其可以被CPU调度执行。

总之进程调用do_fork()后,系统会把当前进程的描述符(PCB)等相关进程资源复制一份,从而产生一个子进程,并根据子进程的需要对复制的进程描述符做一些修改,然后把创建好的子进程放入就绪队列中等待CPU调度。

2.可执行程序的加载

不同于Windows系统的PE格式文件,Linux系统中大多数可执行程序和共享库都采用ELF(Executable and Linkable Format)文件格式。ELF文件不仅支持二进制代码的执行,还支持代码与数据的动态链接。一个ELF文件通常包含程序头表(program header table)和节区(section),这些信息指导了程序如何被加载到内存并执行。

ELF格式的目标文件可分为三种不同类型:

  • 可重定位文件,.c代码文件经过编译得到的.o文件就是可重定位文件,由编译器和汇编器创建,一个源代码文件会生成一个可重定位文件,用来和其他目标文件一起创建一个可执行文件、静态库文件或动态库文件。
  • 可执行文件:一般由多个可重定位文件生成,是完成了所有重定位工作和符号解析的文件,文件中保存着一个用来执行的程序。
  • 共享目标文件:是指可以被其他进程动态地加载和链接的目标文件,通常在Linux环境下以.so为扩展名,跟可执行文件一样包含可执行代码和数据,但是不可被运行。

程序从源代码文件到可执行文件一般要经历预处理、编译、汇编和链接四个主要步骤。

1.预处理阶段

预处理器处理源代码文件中的所有以#开头的预处理指令,如#include、#define等。这一阶段主要负责删除注释、包含头文件、展开宏定义以及处理条件编译指令。

预处理的输出是一个经过修改的C或C++源代码,其文件扩展名通常为.i。这个文件不再包含任何预处理指令,是被送往编译器进一步处理的纯净代码。

2.编译阶段

编译器分析预处理后的代码,进行词法分析、语法分析和语义分析。这一阶段检查代码中的各种语法错误和警告,并产生一个中间表示,通常是汇编语言代码。

编译阶段的输出是一个.s文件,即汇编代码文件。这个文件包含了机器码的文本表示形式,是下一步汇编器的输入。

3.汇编阶段

汇编器将编译生成的汇编代码转换成机器代码,生成的目标文件是可重定位的,意味着它们可以被链接到其他目标文件或库文件,形成最终的可执行文件。

汇编器的输出是一个.o文件,即目标代码文件,它包含机器码,但没有执行头部信息和可能缺失的一些外部引用。

4.链接阶段

链接器负责将一个或多个目标文件和所需的库文件合并成一个单一的可执行文件。此过程中,链接器解决符号引用问题,确定数据和函数的最终内存地址。

链接成功后,生成的是可执行文件,扩展名通常为.exe(在Windows系统)或无扩展名(在Unix/Linux系统)。此时的文件已准备好被操作系统加载并执行。

3.程序的执行

当需要启动一个新程序时,通常使用fork()和execve()组合。fork()创建一个新进程后,execve()负责替换当前进程的映像为新的程序文件,从而开始执行新的程序。

其中do_execve_common()函数是execve()系统调用的核心实现,位于Linux内核中。它负责解析ELF文件的头部,初始化新进程的mm_struct(内存管理结构),以及处理程序的加载和执行。

通过mmap系统调用,将可执行文件的代码段和数据段映射到新进程的虚拟地址空间。这种映射是按需加载的,即只有在真正访问到某部分时才会将其内容加载到物理内存中。

内核会设置好进程的页表,确保程序的所有引用都正确地映射到物理内存或在访问时由内核处理页面错误进行加载。

如果一个可执行程序依赖于动态链接库,那么这些库可以在程序启动时由动态链接器加载,或者在程序运行时通过dlopen等API按需加载。

动态链接器会查找并加载所需的共享库。这个过程包括解析程序的依赖项,将这些库映射到进程的地址空间,并处理任何未解决的符号引用。

一旦所有的加载步骤完成,内核就会开始执行新程序的main函数。这个调用是通过直接跳转到ELF文件中指定的入口点来实现的。

在execve()调用中,可以向新程序传递环境变量和命令行参数。这些信息被复制到新进程的地址空间中,使得新程序能够访问这些数据。

对进程的管理是操作系统的核心功能,Linux通过分页机制和虚拟内存管理进程的内存,每个进程有独立的虚拟地址空间,操作系统将虚拟地址映射到物理内存。进程通过文件描述符管理打开的文件、管道和设备。信号是进程间通信的一种机制,操作系统可以向进程发送信号,通知其发生了某些事件。

4.进程的调度

Linux系统中进程的调度涉及多种状态、优先级以及调度策略。

进程状态

在Linux系统中,每个进程都有其状态,这些状态定义了进程当前的行为模式和系统对其的处理方式。常见的状态包括运行态(Running)、就绪态(Ready)、阻塞态(Blocked)和挂起态(Suspended)。这些状态通过一个整型变量表示,并存储在task_struct结构中。

队列

Linux内核利用不同的队列来管理处于不同状态的进程。例如,就绪队列用于存放准备执行但尚未分配CPU的进程。这些队列通常通过链表实现,确保了高效的队列操作和管理。

优先级

Linux中的进程优先级决定了进程获取CPU资源的先后顺序。优先级较高的进程更容易获得CPU时间片,从而更快地执行。Linux使用一个整数来表示进程的优先级,数值越小,优先级越高。

Linux允许根据系统负载和运行情况动态调整进程的优先级。这种调整可以通过nice值来完成,用户可以通过修改nice值来提升或降低进程的优先级,从而影响其执行顺序。

调度算法与策略

inux系统支持多种调度算法,如CFS(完全公平调度器)用于普通进程,实时调度用于需要快速响应的实时进程等。每种算法针对不同的进程类型和场景,确保系统的高效运行。

Linux内核根据进程的类型和属性选择合适的调度策略。例如,对于实时进程,系统会使用实时调度策略,以保证其严格的时间需求得到满足。

进程切换与并发管理

进程切换是操作系统中频繁发生的操作之一,指的是CPU从一个进程转向另一个进程的过程。Linux通过保存和恢复进程的上下文(如寄存器状态、内存映射等)来实现高效的进程切换。

Linux系统支持多任务并发执行,通过时间片轮转等方式,使得多个进程可以“同时”执行,提高系统的吞吐量和资源利用率。

特殊进程状态的处理

当子进程结束但其父进程尚未读取其退出状态时,子进程将成为僵尸进程。系统需要特别处理这类进程,以避免资源泄漏。

当父进程结束后,其子进程成为孤儿进程,系统会自动将这些进程的父进程设置为init进程,由init进程负责其后续的资源回收工作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/22571.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

力扣2134.最少交换次数得到连续的1(断环成链)

力扣2134.最少交换次数得到连续的1(断环成链) 最终一定是所有1的个数(长度) 的区间 所以求所有1的和 用和作为k作滑动窗口将环断成长度为nsum-1的链 class Solution {public:int minSwaps(vector<int>& nums) {int sum accumulate(nums.begin(),nums.end(),0);in…

如何保持气膜场馆内部空气新鲜—轻空间

气膜建筑作为现代建筑的一种新兴形式&#xff0c;以其独特的优势和设计受到了广泛欢迎。然而&#xff0c;保持气膜内部空气新鲜是一个必须解决的问题。我们通过配备先进的新风系统&#xff0c;提供了高效的解决方案。 新风系统的工作原理 气膜建筑内部空气的新鲜度主要依靠其配…

在C++中,NULL和nullptr有什么区别?

在C11之前&#xff0c;一般使用NULL代表空指针。 NULL的定义在C和C中不同&#xff0c;而且C和C针对0和指针之间的运算规则也存在差异&#xff1a; C03标准&#xff1a;空指针常量是整数类型的整型常量表达式右值&#xff0c;其值为零。空指针常量可以转换为指针类型&#xff…

【vscode-快捷键 一键JSON格式化】

网上有很多JSON格式化工具&#xff0c;也有很多好用的在线json格式化工具。但是其实Vscode里面的可以直接格式化JSON&#xff0c;这里分享一个我常用的小插件 Prettify JSON 未格式化的JSON数据 召唤出命令行&#xff0c;输入prettify JSON 即可! ✿✿ヽ(▽)ノ✿

算法题:Java求数组中最大的值

采用分而治之&#xff08;二分法&#xff09;的思想去求解 分而治之&#xff1a;分而治之的思想可以用于解决很多问题&#xff0c;大概的思路就是把一个比较大的复杂的问题切分成小的块&#xff0c;然后分头去解决他们&#xff0c;最后再把结果合并起来&#xff0c;就是“分而治…

C++中的string类详解

在C中&#xff0c;字符串是一个非常重要的数据类型&#xff0c;用于存储和处理文本数据。C标准库提供了std::string类&#xff0c;它是一个模板类&#xff0c;专门用于处理字符串。std::string类提供了丰富的成员函数和操作符重载&#xff0c;使得字符串操作变得简单而高效。本…

【JAVA】把结果保留两位小数的方法

在Java中&#xff0c;保留两位小数可以使用几种不同的方法&#xff0c;下面将详细解释并给出每种方法的例子&#xff1a; 1.使用DecimalFormat类 DecimalFormat是java.text包中的一个类&#xff0c;专门用于格式化数字&#xff0c;包括保留小数点后指定的位数。下面是使用Dec…

git介绍、安装、配置

文章目录 1. GIT介绍2. 使用GIT的好处3. GIT 安装4. GIT 配置4.1 GIT 初始化设置、命令别名设置4.2 如果终端安装了oh-my-zsh&#xff0c;会带一堆git命令别名4.3 GIT配置文件介绍4.3.1 Linux、Mac OS系统4.3.2 windows系统 5. git设置远程仓库账号密码(拉取、上传代码不用输入…

快速理解 Node.js 版本差异:3 分钟指南

Node.js 是一个广泛使用的 JavaScript 运行时环境&#xff0c;允许开发者在服务器端运行 JavaScript 代码。随着技术的发展&#xff0c;Node.js 不断推出新版本&#xff0c;引入新特性和改进。了解不同版本之间的差异对于开发者来说至关重要。以下是一个快速指南&#xff0c;帮…

Unity3D DOTS 10W GPU Intancing 动画与合批优化详解

前言 Unity3D DOTS&#xff08;Data-Oriented Technology Stack&#xff09;是Unity引擎的一个新的技本堆栈&#xff0c;旨在提高游戏的性能和效率。其中的GPU Instancing和合批技术是其重要的优化手段之一。本文将详细介绍Unity3D DOTS中的10W GPU Instancing技术以及动画与合…

高清多媒体接口(High Definition Multimedia Interface, HDMI)

目录 1. Overview1.1. TMDS(Time Minimized Differential Signal)1.2. Display Data Channel(DDC)1.3. CEC1.4. HEAC1.5. HPD2. Signaling and Encoding3. Video4. Control and Configuration5. Compatibility with DVI6. EDID and E-EDID7. HDCP<

C++高级 - 接口模板

目录 一. 接口 二. 模板 一. 接口 接口通常是通过抽象类或纯虚函数来实现的。 以下是一个使用抽象类来定义接口的示例代码&#xff1a; #include <iostream>class Interface { public:virtual void operation() 0; // 纯虚函数定义接口 };class ConcreteClass : pu…

linux flask | 接口保持在后台一直运行、python后端接口长期调用、python后台持续运行方法、python提供后端接口

文章目录 一、flask接口二、长期运行接口2.1、nohup与&后台运行 实际项目中我们需要用python提供一个后端接口&#xff0c;并在linux上持续运行这个程序&#xff0c;以供其他项目调用。下面就用个简单示例讲解下怎么写python后端接口&#xff0c;以及如何将程序长期运行在l…

URL统一资源定位符 、协议类型、url的组成

1、URL统一资源定位器 URL&#xff08;Uniform Resource Locator&#xff09;即统一资源定位器&#xff08;或统一资源定位符&#xff09;&#xff0c;可以理解网页地址。如同在网络上的门牌&#xff0c;是因特网上标准的资源的地址&#xff08;Address&#xff09;。由Tim Be…

某航天技术公司职级体系搭建项目成功案例纪实

某航天技术公司职级体系搭建项目成功案例纪实 ——的搭建科学合理的职级晋升体系&#xff0c;解决员工流失问题 【客户行业】航空航天 【问题类型】职级体系搭建 【客户背景】 某航天技术公司致力于自主创新&#xff0c;研发和生产航空航天设备。目前公司研发的多套系统和…

Linux云计算实践:OpenStack与云服务

Linux云计算实践&#xff1a;OpenStack与云服务 云计算作为一种革命性的技术&#xff0c;正在改变我们对计算资源的使用和管理方式。Linux操作系统因其开源、稳定和灵活的特性&#xff0c;成为构建云平台的理想选择。OpenStack&#xff0c;作为开源云平台的代表&#xff0c;与…

Java 性能调优与监控工具详解

在Java开发中&#xff0c;性能调优和监控是确保应用程序高效、稳定运行的关键环节。本文将详细介绍Java性能调优的基本原则和常用的监控工具&#xff0c;并通过代码示例帮助读者理解如何进行实际操作。 一、性能调优的基本原则 在开始调优之前&#xff0c;了解以下基本原则是…

【图书推荐】《Ubuntu Linux系统管理与运维实战》

本书重点 全面学习Ubuntu系统操作&#xff0c;快速掌握Linux日常管理和运维 安装和配置、桌面环境、文件系统、文件和目录管理、用户和权限管理系统的启动和关闭、服务和进程管理、软件包管理、磁盘和文件系统管理网络管理、网络服务管理、系统和网络安全 内容简介 Linux是…

【TypeScript】ts中的keyof语法和作用

文章目录 简言keyof和泛型搭配使用约束范型参数的范围其他 结语 简言 keyof 运算符接收一个对象类型&#xff0c;并产生其键的字符串或数字字面联合。 有的时候使用keyof 可以简化类型定义代码或者约束类型范围。 keyof keyof 运算符接收一个对象类型&#xff0c;并产生其键…

计算机基础(5)——进制与进制转换

&#x1f497;计算机基础系列文章&#x1f497; &#x1f449;&#x1f340;计算机基础&#xff08;1&#xff09;——计算机的发展史&#x1f340;&#x1f449;&#x1f340;计算机基础&#xff08;2&#xff09;——冯诺依曼体系结构&#x1f340;&#x1f449;&#x1f34…