ch2 系统调用(编程工具)

本文详细介绍了系统调用的概念及其在程序执行中的作用,通过具体示例讲解了如何利用系统调用与操作系统交互。同时,文章还探讨了ELF文件格式及其在程序编译、链接和加载过程中的应用。

OS ——对象 API

什么是应用程序

什么是软件?

可执行的文件和其他数据文件: ELF(Executable Linkable Format)

vi elf.c //写入空的main函数

gcc -c elf.c // gcc -c 编译,生成目标文件elf.o

man gcc //查看帮助文档

file elf.o  //查看目标文件, ELF 64位可重定位的文件

gcc elf.o // 链接程序,生成a.out

./a.out  //运行的程序 —— 进程

file a.out // 查看文件信息 ELF 64 LSB shared 

进程:运行的程序

ps ax | less // 查看进程
os中有很多进程对象
运行时,进程会在CPU上执行,进行计算;使用OS的API访问OS中其他对象

应用程序实例:
coreutils —— UNIX核心工具包
系统/工具程序: bash apt ssh vim tmux jdk python
apt —— 把文件复制到指定的地方,有时执行脚本/trigger

软件包:Ubuntu Packages

ELF二进制文件

可执行文件也是普通的文件,都是字节的序列
OS提供API打开、读取、改写文件

vim /bin/ls // 查看文件,二进制部分显示异常,字符串常量原样保存

cat  //

xxd // 二进制文件分析工具,二进制文件的字节

解析ELF文件:头(元数据)和字节流

man readelf //查看信息

readelf -h /bin/ls  // 头部信息,存储方式

readelf -l /bin/ls   // 文件加载方式  LOAD 地址0,地址空间随机化,加载到内存任意位置

<elf.h> //头文件,可在c中直接引用  
# include <elf.h>

在这里插入图片描述

Hello World程序的编译/链接/加载

失败的尝试1

// hello.c
int main()
{
	printf("Hello World!");
}
gcc hello.c 
./a.out

// output: Hello World!

运行流程:

gcc -c hello.c // 编译为hello.o
ls -l //查看
file hollo.o // 查看ELF
ld hello.o // 链接

链接错误
在这里插入图片描述错误原因:有的是"puts",gcc - o0 在不优化的选项下也会进行一定的优化 —— 导致一些编译器bugs的源头
puts是库函数(libc)
main: _start是链接器默认的入口。将main改为_start 或者 ld -e main hello.o 指定入口

// null.c
int main()
{
}
gcc -c null.c 
objdump -d hello.o // 寄存器操作,显示如下
ld -e main null.o // 不报错
file a.out // ELF
./a.out    // segmentation fault

在这里插入图片描述
一个简单的程序的运行也是非常复杂。 —— 观察程序执行过程(调试gdb)
STFW/RTFM

man gdb  // 手册,都要去读读
gdb a.out //调试
starti // 在第一条指令开始执行程序
layout asm // 查看汇编代码
info register // 查看寄存器
si // 单步执行
bt // 打印栈上内容

OS加载main函数。

成功的尝试:汇编

main函数确实运行了,只是在返回时crash了

syscall : 调用系统的API,进入操作系统。。。

man 2 syscall // 系统调用的ABI(Application Binary Interface),详细介绍系统调用的过程
# include <unistd.h>
# include <sys/syscall.h>

# define LENGTH(arr) (sizeof(arr) / sizeof(arr[0]))

const char hello[] = "\033[01;31mHello,OS World\033[0m\n";
//try : const char *hello =

int main()
{
        syscall(SYS_write, 1, hello, LENGTH(hello)); 
        // 调用SYS_write系统调用,往编号为1的问价描述符中写入地址为hello缓冲区上的大小为LENGTH字节的数据
        syscall(SYS_exit, 1);
}                                                                                  ~                   

syscall代码在哪里??

man objdump //  显示目标文件的信息
objdump -d a.out // 打印汇编
objdump -S  a.out // 打印汇编语言
gcc -g syscall-demo.c // 源代码信息包装在文件中

—— main()函数之前发生了什么?
普通C程序第一条指令在哪里?
libc的_start?
调试找答案:

gdb a.out
starti
info inferior // 显示当前进程信息
!ls // !运行系统命令
!pmap 770(process id) // 打印该进程此时的地址空间中的内容

main开始前,OS已经加载好了一些文件 —— a.out, 加载了ld,再调用libc,再调用main函数
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

trace工具—— 追踪程序执行信息

system call trace
strace // 使用ptrace系统调用实现的
man strace // 追踪系统调用和信号
strace ./a.out //打印系统调用序列
strace ./a.out > /dev/null  // 丢弃标准输出

在这里插入图片描述

其他系统调用例子

gcc 调用哪些系统调用

.c -> (preprocess) -> .i -> (compile) -> .s -> (assembly) -> .o -> (link) -> a.out

man strace
strace -f gcc a.c // gcc 会启动其他进程,追踪子进程
strace -f gcc a.c 2&1 | grep execve | less // 重定向标准输出,并查看execve的系统调用

调用:
cc1:调用编译器,从c到汇编的过程
as:若干路径上的as,直到成功为止,从汇编到ELF
collect2: 链接。链接之前先收集构造函数和析构函数,生成一个它们的调用函数 —— 非main函数中的部分需要被调用
ld: 链接
在这里插入图片描述

图形界面程序(xedit)

实际上是调用系统中其它程序,进行程序之间的通信。
在这里插入图片描述

总结

所有的应用程序都是在一套操作系统的API上构建的!!
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值