读Kernel感悟-Linux内核启动-从hello world说起

2024-04-18 13:38

本文主要是介绍读Kernel感悟-Linux内核启动-从hello world说起,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

内核是从哪里开始执行的呢?几乎任何一本Linux内核源代码分析的书都会给出详细的答案。不过,我试图从一个不同的角度(一个初学者的角度)来叙述,而不是一上来就给出答案。从熟悉的事物入手,慢慢接近陌生的事物,这是比较常见的思路。既然都是二进制代码,那么不妨从最简单的用户态C程序,hello world开始。说不定能找到共同点。恰好我是一个喜欢寻根究底的人。也许,理解了hello world程序的启动过程,有助于更好地理解内核的启动。

好,开始寻根究底吧。从普通的C语言用户态程序开始写。先写一个简单的hello world程序。

/*helloworld.c*/

#include <stdio.h>

int main()

{

    printf("hello world/n");

    return 0;

}

然后gcc helloworld.c -o helloworld,一个最简单的hello world程序出现了。

它是从哪里开始执行的呢?这还不简单?main函数么。地球人都知道。

为什么一定要从main函数开始呢?于是,我开始琢磨这个hello world程序。

file helloworld可知,它是一个elf可执行文件。

反汇编试试。

objdump -d helloworld

反汇编的结果令人吃惊,因为出现了_start()等一堆函数。一定是gcc编译时默认链接了一些库函数。

其实,只要运行gcc -v helloworld.c -o helloworld就会显示gcc详细的编译链接过程。其中包括链接/usr/lib/下的crti.o crt1.o crtn.o等等文件。用objdump查看,_start()函数就定义在crt1.o文件中。

那么helloworld的真正执行的入口在哪里呢?我们可以使用readelf来查看,看有没有有用信息。

readelf -a helloworld

helloworld作为一个elf文件,有elf文件头,section table和各个section等等。有兴趣可以去看看elf文件格式的文档。

用readelf可知,在helloworld的elf文件头的信息中,有这么一项信息:

入口点地址:               0x80482c0

可见,helloworld程序的入口地址在0x80482c0处,而由objdump得:

080482c0 <_start>:

可见,_start()是helloworld程序首先执行的函数。_start()执行完一些初始化工作后,经过层层调用,最终调用main().可以设想,如果_start()里最终调用的是foo(),那么C程序的主函数就不再是main(),而是foo()了。

再进一步:helloworld程序具体是如何执行的呢。我们只能猜测是由bash负责执行的。然而具体看bash代码就太复杂了。我们可以用strace跟踪helloworld的执行。

strace ./helloworld

出来一大堆函数调用。其中第一个是execve().这是一个关键的系统调用,它负责载入helloworld可执行文件并运行。其中有很关键的一步,就是把用户态的eip寄存器(实际上是它在内存中对应的值)设置为elf文件中的入口点地址,也就是_start()。具体可见内核中的sys_execve()函数。

由此可见,程序从哪里开始执行,取决于在刚开始执行的那一刻的eip寄存器的值。而这个eip是由其它程序设置的,在这里,eip是由Linux内核设置的。具体过程如下:

1.用户在shell里运行./helloworld。

2.shell(这里是bash)调用系统调用execve()。

3.execve陷入到内核里执行sys_execve(),把用户态的eip设置为_start()。

4.当系统调用执行完毕,helloworld进程开始运行时,就从_start()开始执行

5.helloworld进程最后才执行到main()。

 

参考:elf文件格式

http://www.skyfree.org/linux/references/ELF_Format.pdf

这篇关于读Kernel感悟-Linux内核启动-从hello world说起的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/914936

相关文章

Linux中SSH服务配置的全面指南

《Linux中SSH服务配置的全面指南》作为网络安全工程师,SSH(SecureShell)服务的安全配置是我们日常工作中不可忽视的重要环节,本文将从基础配置到高级安全加固,全面解析SSH服务的各项参... 目录概述基础配置详解端口与监听设置主机密钥配置认证机制强化禁用密码认证禁止root直接登录实现双因素

在Linux终端中统计非二进制文件行数的实现方法

《在Linux终端中统计非二进制文件行数的实现方法》在Linux系统中,有时需要统计非二进制文件(如CSV、TXT文件)的行数,而不希望手动打开文件进行查看,例如,在处理大型日志文件、数据文件时,了解... 目录在linux终端中统计非二进制文件的行数技术背景实现步骤1. 使用wc命令2. 使用grep命令

Linux如何快速检查服务器的硬件配置和性能指标

《Linux如何快速检查服务器的硬件配置和性能指标》在运维和开发工作中,我们经常需要快速检查Linux服务器的硬件配置和性能指标,本文将以CentOS为例,介绍如何通过命令行快速获取这些关键信息,... 目录引言一、查询CPU核心数编程(几C?)1. 使用 nproc(最简单)2. 使用 lscpu(详细信

linux重启命令有哪些? 7个实用的Linux系统重启命令汇总

《linux重启命令有哪些?7个实用的Linux系统重启命令汇总》Linux系统提供了多种重启命令,常用的包括shutdown-r、reboot、init6等,不同命令适用于不同场景,本文将详细... 在管理和维护 linux 服务器时,完成系统更新、故障排查或日常维护后,重启系统往往是必不可少的步骤。本文

基于Linux的ffmpeg python的关键帧抽取

《基于Linux的ffmpegpython的关键帧抽取》本文主要介绍了基于Linux的ffmpegpython的关键帧抽取,实现以按帧或时间间隔抽取关键帧,文中通过示例代码介绍的非常详细,对大家的学... 目录1.FFmpeg的环境配置1) 创建一个虚拟环境envjavascript2) ffmpeg-py

nginx启动命令和默认配置文件的使用

《nginx启动命令和默认配置文件的使用》:本文主要介绍nginx启动命令和默认配置文件的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录常见命令nginx.conf配置文件location匹配规则图片服务器总结常见命令# 默认配置文件启动./nginx

Linux脚本(shell)的使用方式

《Linux脚本(shell)的使用方式》:本文主要介绍Linux脚本(shell)的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述语法详解数学运算表达式Shell变量变量分类环境变量Shell内部变量自定义变量:定义、赋值自定义变量:引用、修改、删

Nexus安装和启动的实现教程

《Nexus安装和启动的实现教程》:本文主要介绍Nexus安装和启动的实现教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、Nexus下载二、Nexus安装和启动三、关闭Nexus总结一、Nexus下载官方下载链接:DownloadWindows系统根

Java中实现线程的创建和启动的方法

《Java中实现线程的创建和启动的方法》在Java中,实现线程的创建和启动是两个不同但紧密相关的概念,理解为什么要启动线程(调用start()方法)而非直接调用run()方法,是掌握多线程编程的关键,... 目录1. 线程的生命周期2. start() vs run() 的本质区别3. 为什么必须通过 st

Linux链表操作方式

《Linux链表操作方式》:本文主要介绍Linux链表操作方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、链表基础概念与内核链表优势二、内核链表结构与宏解析三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势六、典型应用场景七、调试技巧与