《Unix/Linux系统编程》第一、二章学习笔记

第一章 引言

让我最有收获的内容:

1.8 关于Unix

Unix是一种通用操作系统。
开发者:肯·汤普森(Ken Thompson)和丹尼斯·里奇(Dennis Richie)
诞生日期:20世纪70年代早期
经典书目:K&R 1988 《The C Programming Language》


1.9 关于Linux

Linux(Linux 2017)是一个类Unix系统。它最初是Linus Torvalds在1991年为基于Intel x86的个人计算机开发的一个实验性内核。
Linux的一个重要里程碑发生在20世纪90年代末,当时,它与GNU(Stallman 2017)相结合,纳入了许多GNU软件,如GCC编译器、GNU emacs编辑器和bash等,极大地促进了Linux的进一步发展。不 久之后,Linux实现了访问互联网的TCP/IP协议族,并移植了支持GUI的X11(X-window),成为一个完整的操作系统。
Linux包含其他Unix系统的许多特性。在某种意义上,它是由各种最为流行的Unix系统组合而成。在很大程度上,Linux是兼容POSIX标准的。Linux已被移植到许多硬件体系结构中,如摩托罗拉、SPARC和ARM等。主要的Linux平台仍然是基于Intel x86的个人计算机,包括广泛可用的台式机和笔记本电脑。此外,Linux可免费使用,且易于安装,因此,颇受计算机科学专业的学生欢迎。


1.13 使用Linux

1.13.1 Linux内核镜像

在典型的Linux系统中,Linux内核映像位于/boot目录中。可启动的Linux内核映像名为vmlinuz-generic-VERSION_NUMBER
initrd是Linux内核的初始的ramdisk映像。
一个可启动的Linux内核映像由三部分组成:
| BOOT | SETUP | linux kernel
BOOT是一个512字节的启动程序,用于从软盘映像启动早期的Linux版本。但现在,它不再用于Linux启动,而是包含一些参数,以供“SETUP”使用。SETUP是一段16位和32位的汇编代码,用于在启动期间将16位模式转换为32位保护模式。linux kernel是Linux的实际内核映像。它采用压缩格式,但在开始时有一段解压代码,用于解压Linux内核映像并将其放入高端内存中。

1.13.2 Linux启动程序

Linux内核可以由几个不同的启动加载程序启动。最受欢迎的Linux启动加载程序是GRUB和LILO。另外,HD启动程序(Wang 2015)也可用来启动Linux。

1.13.3 Linux启动

在启动期间,Linux启动加载程序首先会定位Linux内核映像(文件)。然后:

  • 加载BOOT+SETUP至实模式内存的0x90000处。
  • 加载Linux内核至高端内存的1MB处。

对于常见的Linux内核映像,该加载程序还会将初始ramdisk映像initrd加载到高端内存中。然后转移控制权,运行0x902000处的SETUP代码,启动Linux内核。首次启动时,Linux内核会将initrd作为临时根文件系统,在initrd上运行。Linux内核将执行一个sh脚本,该脚本指示内核加载实际根设备所需的模块。当实际根设备被激活并准备就绪时,内核将放弃初始ramdisk,将实际根设备挂载为根文件系统,从而完成Linux内核的两阶段启动。

1.13.4 Linux运行级别

Linux内核以单用户模式启动。它可以模仿System V Unix的运行级别,以多用户模式运行。然后,创建并运行INIT进程P1,后者将创建各种守护进程和供用户登录的终端进程。之后,INIT进程将等待任何子进程终止。

1.13.5 登录进程

各登录进程将在其终端上打开三个文件流:stdin(用于输入),stdout(用于输出),stderr(用于错误输出)。然后等待用户登录。在使用X-window作为用户界面的Linux系统上,X-window服务器通常会充当用户登录界面。用户登录后,(伪)终端默认属于用户。

1.13.6 命令执行

登录后,用户进程通常会执行命令解释程序sh,后者将提示用户执行命令。sh将直接执行一些特殊命令,如cd(更改目录)、exit(退出)、logout(注销)、&。


1.14 使用Ubuntu Linux

1.14.2 Ubuntu Linux的特性

(1)在台式机或笔记本电脑上安装Ubuntu时,需要输入用户名和密码来创建一个默认主目录为“/home/username”的用户账户。当Ubuntu启动时,它会立即在用户环境中运行,因为其已自动登录默认用户。

(2)出于安全原因,用户应为普通用户,而不是根用户或超级用户。
要运行任何特权命令,用户必须输入
sudo command

(3)用户的“PATH”(路径)环境变量设置通常不包括用户的当前目录。为在当前目录下运行程序,用户每次必须输入./a.out。为方便起见,用户应更改路径设置,以包含当前目录。在用户的主目录中,创建一个包含以下代码的.bashrc文件:
PATH=$PATH:./

用户每次打开伪终端时,sh都会先执行.bashrc文件来设置路径,以包含当前工作目录。
(4)很多用户可能都安装了64位的Ubuntu Linux。本书中的一些编程练习和作业是针对32位机器的。在64位Linux下,使用
gcc -m32 t.c # compile t.c into 32-bit code

生成32位代码。若64位Linux不采用-m32选项,则用户必须安装适用于gcc的附加支持插件,才能生成32位代码。
(5)Ubuntu具有友好的GUI用户界面。许多用户都习惯于使用GUI,以至于产生了过度依赖,这往往需要反复拖动和点击指向设备,从而浪费了大量时间。在系统编程中,用户还需要学习如何使用命令行和sh脚本,它们比GUI要通用和强大得多。


1.15 Unix/Linux文件系统组织

1.15.3 Unix/Linux命令

Unix/Linux中最常用的命令:

  • ls:ls dirname:列出CWD或目录的内容。
  • cd dirname:更改目录。
  • pwd:打印CWD的绝对路径名。
  • touch filename:更改文件名时间戳(如果文件不存在,则创建文件)。
  • cat filename:显示文件内容。
  • cp src dest:复制文件
  • mv src dest:移动或重命名文件。
  • mkdir dirname:创建目录。
  • rmdir dirname:移除(空)目录。
  • rm filename:移除或删除文件。
  • ln oldfile newfile:在文件之间创建链接。
  • find:搜索文件。
  • grep:搜索文件中包含模式的行。
  • ssh:登录到远程主机。
  • gzip filename:将文件压缩为.gz文件。
  • tar -zcvf file.tgz.:从当前目录创建压缩tar文件。
  • tar -zxvf file.tgz.:从.tgz文件中解压文件。
  • man:显示在线手册页。
  • zip file.zip filenames:将文件压缩为.zip文件。
  • unzip file.zip:解压.zip文件。

第二章 编程背景

让我最有收获的内容:

2.1 Linux中的文本编辑器

2.1.1 Vim

vim(Linux Vi和Vim Editor 2017)是Linux的标准内置编辑器。它是Unix原始默认vi编辑器的改进版本。与其他大多数编辑器不同,vim有3种不同的操作模式,分别是

  • 命令模式:用于输入命令
  • 插入模式:用于输入和编辑文本。
  • 末行模式:用于保存文件并退出。

vim启动时,处于默认的命令模式,在该模式下,大多数键表示特殊命令。移动光标的命令键示例如下:

  • h:将光标向左移动一个字符
  • l:将光标向右移动一个字符
  • j:将光标向下移动一个字符
  • k:将光标向上移动一个字符

在X-window中使用vim时,也可以通过箭头键来完成光标的移动。要输入文本进行编辑,用户必须输入i(插入)或a(追加)命令将vim切换到插入模式

  • i:切换到插入模式,插入文本。
  • a:切换到插入模式,追加文本。

要退出插入模式,请按ESC键一次或多次。在命令模式下,输入“:”进入末行模式,将文本保存为文件或退出vim:

  • :w:写入(保存)文件。
  • :q:退出vim。
  • :wq:保存并退出。
  • :q!:不保存更改,强制退出。

虽然许多Unix用户已经习惯了vim不同的操作模式,但是其他用户可能认为与其他基于图形用户界面(GUI)的编辑器相比,vim使用起来既不自然也不方便。以下类型的编辑器属于通常所说的所见即所得(WYSIWYG)编辑器。在WYSIWYG编辑器中,用户可以输入文本,用箭头键移动光标,和普通的文本输入一样。通常,通过输入一个特殊的meta键,接着输入一个字母键即可创建命令。例如:

  • Ctrl+C:中止或退出。
  • Ctrl+K:删除行到缓冲区。
  • Ctrl+Y:从缓冲区内容中复制或粘贴。
  • Ctrl+S:保存已编辑文本等。

2.3 程序开发

2.3.1 程序开发步骤

(1)创建源文件;
全局变量、局部变量、静态变量、自动变量和寄存器变量。
(2)用gcc把源文件转换成二进制可执行文件。
gcc t1.c t2.c
(3)gcc的步骤:

1.将C源文件转换为汇编代码文件,即将.c文件转为.s文件。
2.将汇编代码转换为目标代码,即将.s文件转为.o文件。
每个.o文件包含:

  • 一个文件头
  • 一个代码段
  • 一个数据段
  • 一个BSS段
  • 指针、数据和重定位信息
  • 符号表

3.链接。

  • 将.o文件的所有代码段组合成单一代码段。
  • 将所有数据段组合成单一数据段。
  • 将所有BSS段组合成单一bss段。
  • 使用.o文件的重定位信息调整组合指针、数据和bss段的偏移量。
  • 用符号表来解析各个.o文件之间的交叉引用。
2.3.2 静态与动态链接

这是创建二进制可执行文件的两种方式。

静态库的特点:
链接器将所有必要的库函数代码和数据纳入a.out文件中。这使得a.out文件完整、独立,但通常非常大。
动态链接的优点:

  • 减小每个a.out文件大小;
  • 许多执行程序共享相同库函数;
  • 修改库函数无需重新编译源文件。
    动态链接所用的库称为动态链接库(DLL)。它们在Linux中称为共享库(.so文件)。动态加载(DL)库是指按需加载的共享库,可用作插件和动态加载模块。
2.3.3 可执行文件格式

1.二进制可执行平面文件
2.a.out可执行文件
3.ELF可执行文件

2.3.4 a.out文件的内容

1.文件头
2.代码段
3.数据段
4.符号表

2.3.5 程序执行过程

1.读取a.out文件头,以确定所需总内存大小;
2.sh从总大小中分配一个内存区给执行映像;
3.接着,sh放弃旧映像,开始执行新映像
4.执行从crt0.o开始,调用main()

2.3.6 程序终止

1.正常终止
2.异常终止


2.4 C语言中的函数调用

2.4.1 32位GCC中运行时堆栈使用情况

1.进程执行映像
2.每个CPU都有以下寄存器或同等寄存器:

  • PC(IP):指向CPU要执行的下一条指令。
  • SP(SP):指向栈顶。
  • FP(BP):指向当前激活函数的栈帧。
  • 返回值寄存器(AX):函数返回值的寄存器。

3.main函数由crt0.o调用。
4.每个函数入口,编译后的代码完成如下功能:

  • 将FP压栈,在堆栈上保存CPU的FP寄存器。
  • 让FP指向保存的FP#建立栈帧。
  • 向下移动SP为堆栈上的自动局部变量分配空间。
  • 编译后的代码可能继续向下移动SP,在堆栈上分配一些临时工作空间,用temps表示。

2.6 链接库

2.6.1 创建静态链接库
gcc -c mysum.c
ar rcs libmylib.a mysum.o
gcc -static t.c -L. -lmylib
a.out
2.6.2 创建动态链接库
gcc -c -fPIC mysum.c
gcc -shared -o libmylib.so mysum.o
gcc t.c -L. -lmylib
export LD_LIBRARY_PATH=./
a.out
posted @ 2022-09-04 22:29  20201229赵斌  阅读(104)  评论(0编辑  收藏  举报