操作系统(2)应用眼中的操作系统:系统调用

操作系统(2)应用眼中的操作系统:系统调用

什么是(应用)程序

  • 可执行的文件(程序的二进制代码和数据)和其他数据文件
    • Linux支持多种可执行文件·格式
    • ELF(Executable Linkable Format)最常用
  • 正在运行的称为进程
    • 操作系统中有许多进程对象
    • 在运行时,进程会
      • 在CPU上执行,进行计算
      • 使用操作系统API访问操作系统中的其他对象

image-20210313145157174

  • 系统中常见的应用程序

    image-20210313145242934

ELF二进制文件

image-20210313145327968

  • 文件处理常用命令:
    • vi a.c 直接编辑文件。按i进入编辑模式,:q不保存退出,:wq或:x保存并退出
    • file a.o 查看文件的信息。比如是什么格式的文件。
    • cat a.c 查看文件的内容
    • xxd a.o 二进制显示和处理文件工具

解析ELF文件

如何解析

readelf是专门解析EIF可执行文件的工具;我们需要关注:

  • ELF文件的header(元数据):
    • 文件内容的分布
    • 指令集体系结构
    • 入口地址
  • ELF的header:决定该文件如何被加载器加载(执行)

在c程序中引入elf.h,/usr/include/elf.h提供了必要的定义

image-20210313151009381

readelf使用说明

  • 使用man readelf可查看使用说明

image-20210313151209147

  • readelf -h a.o查看头部信息

    image-20210313151449279

  • readelf -l a.out 显示程序头(段头)信息(如果有数据的话)。

    image-20210313151720792

  • readelf -S a.out 显示节头信息(如果有数据的话)

    image-20210313151841688

  • readelf -g a.out 显示节组信息(如果有数据的话)

    image-20210313151959229

Hello World眼中的操作系统

如何实现一个最简单的c语言程序?跟着大佬做出的尝试

失败的尝试1

如下图,这是一个简单的helloworld程序:

image-20210313152957670

我们尝试进行编译:

image-20210313153138990

一个c语言经历的过程:.c -> 预处理 -> .l -> 编译 -> .s -> 汇编 -> .o -> 链接 -> .out

从上面我们可以看出,该程序在链接时出现两处错误,大佬的解释(其实还有点懵,希望后面的学习能彻底懂):

image-20210313153800705

失败的尝试2

做出修改后:

image-20210313154036678

再次进行编译链接:

image-20210313154143632

此时只出现了一处错误,为什么?还是连接器入口的问题,那我们指定一下入口,说不定可以?

image-20210313154428335

终于链接成功了,但是又…运行失败!!!为什么。。。。

此时大佬带我们学习一个编译工具gdb。这样可以观察程序的执行!

常用的命令如下(后续我会学习一下gdb手册,专门写一篇博客介绍):

image-20210313154733296

那么开始调试这个程序:

  • 首先是gdb a.ouy进入调试:

image-20210313155445036

  • 接着是starti,让程序在第一条程序上停下来

image-20210313155602736

  • 接下来是layout asm,可以让我们更加方便地查看汇编代码:

image-20210313155742162

  • 接下来是info register,可以查看所有的寄存器

image-20210313155946724

  • 接下来是单步调试 si,按一次回车键可以单步一次,如图

image-20210313160231156

从上图的调试中可以看到,return那里很奇怪,return指令是从栈上弹出返回的地址,和return配对的是call指令,当有一条call指令时,会把返回值放在堆栈上,然后跳转到main执行。而main函数又是谁调用的呢?真相只有一个,那就是操作系统帮我们加载了main

  • 使用bt去打印系统调用栈可以看到main

所以继续执行会出现非法的地址访问这个错误

操作系统做了什么?

  • 加载程序,并初始化运行环境(寄存器、代码、数据、堆栈)
  • 从_start开始执行

成功的尝试——汇编语言

image-20210313161625975

大佬的汇编语言版本(学完汇编一定亲自试一下):

image-20210313162518564

整个运行过程的梳理:前面四条设置参数,然后syscall调用系统API,然后应用程序进入操作系统,执行很多指令,直到调用I/O输出hello,os world;最后三条指令是退出程序,设置返回值什么的。

  • man 2 syscall 可以查看系统调用的手册

image-20210313163921640

C语言版本:

image-20210313162220772

运行:

image-20210313164216066

  • 使用objdump:查看目标文件的信息

image-20210313164411719

  • 使用gcc -g 命令编译成二进制代码,再使用objdump -S进行查看

image-20210313164714309

结果:

image-20210313164837131

main()之前发生了什么?

image-20210313164955822

调试程序:

image-20210313165952567

  • 查看寄存器状态:

image-20210313170040643

  • 查看进程状态:info inferiors

image-20210313170206164

  • 查看暂停进程的地址空间的内容: !pmap [pid]

image-20210313170359722

可以看到,系统帮我们加载了a.out的程序,然后再加载了ld-2.27.so初始的加载器。ld-2.27.so会帮我们加载libc,然后再调用libc初始化,最后调用main。

所以,main()开始之前:

image-20210313170915894

main执行之前,发生了哪些API的调用?使用trace工具

image-20210313171117003

一个练习的小demo:

image-20210313172431440

  • strace ./a.out 追踪api的调用

image-20210313172746698

可以看到,第一个系统调用是加载a.out,然后后面的系统调用都是加载器和libc调用的

关于libc的简单介绍:

image-20210313172945913

查看后半段的调用:

image-20210313173044425

可以看到,执行第一个write的之后就调用I/O,在终端输出helloworld,这是分时系统的一个体现

  • 将程序标准输出丢弃: strace ./a.out > /dev/null 输出到这里的内容都会被丢弃,后半段的内容变成了:

image-20210313173526672

应用眼中的操作系统

本质上,所有的程序都和hello world类似

image-20210313173722657

这些都是后面会学到的系统调用API。

demo:gcc

image-20210313173836449

  • strace -f gcc a.c 创建子进程时进行追踪

打印出了很长的系统调用序列

image-20210313174159412

  • strace -f gcc a.c 2>&1 |grep execve 查看 execve系统调用的情况

image-20210313174423564

主要的系统调用都在上面可以看到,包括cc1,as,collect2,ld

image-20210313174834409

各式各样的应用程序都是在这一套API上构建的!

image-20210313175023195

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值