本文主要是从应用角度出发,分别阐述操作系统接口,计算机语言,文件系统等背后的一些知识,规范,原理,设计思想,应用法门,让初学者编码有一个整体的,全局的认识,有一个物理的视角,找到自己的起点。

 

Table of Contents

前言

操作系统接口

ABI (application binary interface)

系统调用(system call)

sysfs 文件系统接口

 

前言

写这篇文章主要是基于自己大学的经历,当时抱着一腔热血去学计算机编程,可是当把c/c++语言,数据结构,操作系统,计算机组成原理等课程都学完后,却发现自己似乎什么也不会,只会printf打印一些字符串。那段时间真的好苦恼,特别想做软件,却不知从何开始,也不知道该如何去使力,蹉跎了好久,浪费了大量的时间。

造成这种现象的主要原因,一是自己缺少那种天赋,二是教学过于侧重基础和理论,每门课程只涉及到一个局部,没有一门课程把这些串起来。我不了解语言的基础库,除了printf后,其它API都不会用;也不了解具体的操作系统平台的API;虽然学了TCP/IP,socket的具体使用却又不清楚; 至于像fat,ext等磁盘文件系统的格式,那就更遥远了;我甚至还不清楚计算机语言和编译工具的关系;更要命的是,我还不知道自己不知道这些。说白了就是理论同应用脱节,虽然大学也安排了课程设计,实验和实习,但都只是走了过场,也没有人来指点一下,该看些什么书籍。大学读完,就知道拖拉几个控件做一个窗口,连接一下数据库。

windows + VC屏蔽了太多的技术细节,可惜大学期间接触的偏偏就是它,对用户这个是好事越傻瓜越好,可是对计算机的学生就要了命了。自从我转投到linux开源世界后,终于才发现了什么是自由,什么是编程。当阅读linux源码碰到不理解的地方,可以直接修改源码加上打印,分析kernel流程。对比着minix的源码来学习操作系统结构原理,那些概念就变成实实在在的数据结构和算法,动手写一个minix的驱动,微内核和宏内核区别就一目了然了。强烈建议,想学习编程的同学都去拥抱开源世界,然后,再回到自己感兴趣的或工作相关的领域。

计算机语言说白了就是工具,关键还是你要做什么,这样就涉及到了应用,以及专业背景知识。如想做驱动编程,离不开对操作系统驱动架构的了解;想做一个磁盘分区合并,那需要了解文件系统的格式;做个播放器吧,那对视频文件格式,编码格式,编解码API的了解必不可少。 随着你对软件系统了解的深入,会发现其实一切都是协议。 http 是一套web 通讯的协议;计算机语言是开发工具提供的协议; 操作系统是内核空间与应用空间的协议..., 这些协议被各种规范约束--并形成了各种技术。 所以,每种技术的背后都一套协议,规则和思想。了解这些才能算真正了解了相关技术。

在这篇文章里面,我以GNU/Linux作为平台,从应用的角度出发来把相关的课程来串一串提供一个“物理视图”,让初学者有个全局的认识,能够有一个方向和切入角度,至少知道该找些什么资料来看。

操作系统接口

它是内核对应用空间提供的一套协议,主要包括:

  • ABI可执行文件的结构。

  • 系统调用(system call)

  • sysfs 文件系统接口(linux kernel)

ABI (application binary interface)

同API不同, ABI是应用程序的二进制接口,也就是操作系统所支持的可执行程序结构,它面向的是编译器的作者。对操作系统而言,它关心的是应用程序如何运行--如何建立内存映像和进程映像,链接依赖的动态库,找到代码入口点。因此,它需要为程序提供一套协议,这便是 ABI。ELF是UNIX世界比较主流的一种可执行文件格式,下面以它为例来说一下ABI相关的内容。

ELF是可链接,可运行的文件的物理结构,由众多的Header, Table表等数据结构组成,包括3种类型:

  • relocatable file: .o 文件。

  • exectable file: 应用程序

  • shared file: 动态链接库。

 ELF是编译, 链接生成的,执行的时候,由ld 解析,加载在到内存,最后控制权交给程序入口代码,程序开始执行。因此,它提供了2类视图:链接视图和执行视图。  

                   

从链接视图上看,ELF由众多的 Section组成,编译器先把源码编译成.o文件,主要是提取函数,全局变量等生成符号表,把它们填充到相应的 Section里面去。 在这个阶段,所有的符号都是没法定位到地址的。

Link的时候,对.o文件进行合并,对各个文件内的符号进行重定位,安排它们的地址,如下图所示, link完成后,g_u8 和 g_flag2都有地址了。

对于动态链接的函数,在link阶段没法安排地址,需要放到 dynsym Section里面去,在 ld的时候,来进行定位 -- 这就是所谓的 ”函数重定位“ 。

linux系统提供了可执行程序readelf来解析 ELF文件格式,我们可以使用它来了解一下ELF文件的一些通用的Section

  • .bss  是没有指定初始化值的数据, 有些编译器会默认全部初始化为0。
  • .data 全局变量初始化。
  • .text 代码。
  • .init 程序初始化运行的代码。
  • .fini 程序结束运行的代码。
  • .symtab 符号表, 它是源码编译生成的产物,可以为代码运行,调试提供信息。 属于辅助性质,不参与 load 和运行, 可以用 strip来删除掉。

'offset Align' 是各个Section在ELF文件内的偏移地址,我们以二进制的方式打开ELF文件,根据偏移地址,就可以查看相应Section的二进制内容。

从下图中可以看到 .interp的内容是 "/lib64/ld-linux-x86-64.so.2",

上面这些就是编译,链接生成ELF文件的过程:编译器以源文件作为输入,先提取各个文件的全局变量和函数,生成符号表,再把它们链接到一起,链接的时候对各个符号进行定位,分配地址。对于动态链接库的函数,则推迟到'加载'程序到内存的时候进行定位。编译链接后,代码和数据分散到了相应的section里面,程序加载的时候,需要把Section 合并成Secgment,然后,以Secgement为单位加载到内存页面里面去,我们来看一下Segment的结构。

ELF9Segment,其中比较核心的是 --

  • INTERP, 用来指定谁执行这个ELF文件,

  • LOAD, 分别是文本(代码)段和数据端。

SegmentSection 是有对应关系的,如:

  • INTERP -- .interp

  • LOAD(文本) --  .interp .init .text .fini .rodata 等。

  • LOAD(数据)  --   .init_array .fini_array .data .bss 等。

程序执行的时候,先从INTERP 段找到对应的执行程序--可执行程序一般是ld.so, 首先加载ELF文件,根据Prgrame Header 数据结构,把section加载到各个程序段里面, 然后,递归重定位动态链接符号,加载这些符合依赖的动态链接库,处理ELF文件中的重定位, 然后,把控制权交给代码入口,程序开始运行。在这个过程中,ld 最重要的一个事情就是'重定位', 修改ELF里面的动态链接函数的符号表。

系统调用(system call)

系统调用是kernel提供给应用层的API,通过软中断来调用。调用的形式是这样

对ENTER_KERNEL宏的定义,传统(i386)的调用方式是 'int $80', ia64是 'syscall'指令来进入kernel。 软中断的流程基本上都是这么几个步骤

  • 保存现场。
  • 把参数压如相应的寄存器。-- kernel有定义它们的对应关系。
  • 写系统调用号到 %eax 寄存器。
  • 调用 int $80 或者 syscall进入内核。 --不同的CPU会有不同的指令要求。
  • 得到结果,恢复现场等。

我们可以在'arch/x86/syscalls/'目录下找到 syscall的列表,

           

最前面的数字就是系统调用号, 如: 'syscall 5'调用的是'open'。 总共大约是400个左右,涵盖了最基本的应用:如上图中文件相关的操作, 进程类的(fork, execve)等。 这些系统调用都被libc库做了封装, 一些简单的底层函数(如:mount, mkdir, stat)则只是简单地被包裹了一下,直接软中断到kernel了。所以,大家如果想了解文件系统相关API的实现,一定要看kernel的源码,看 libc库的源码是没用的,它都是简单地做了一个系统调用的转换。

前面提到过一切都是协议,POSIX是一个广泛被支持的协议(规范),Linux和各类unix都对它提供了支持,只要操作系统申明支持POSIX接口,它就得实现POSIX定义的系统调用。对linux/unix而言, POSIX只是它们的一个子集,它会还会支持UNIX世界的一些系统调用规范。总之,有了这些规范,libc就可以在各个系统间无缝移植。

sysfs 文件系统接口

sysfs是linux kernel以文件系统的方式提供给应用层的接口,在linux的世界里,驱动模块都被抽象为文件系统节点,因此,我们对 /sys/文件系统进行读写操作,可以与内核里面的驱动层进行交流。 具体的接口内容,请查询 '/Documentation/ABI/'目录下sysfs-module文件。

用户评论:
发表评论: (限500字)

注册 忘记密码 登录