0
点赞
收藏
分享

微信扫一扫

《Linux程序设计(中文第四版)》阅读笔记 + 源码实践

追梦人的自留地 2022-03-14 阅读 119

第1章 入门

1.1 UNIX、Linux、GNU简介

Linux 已经真正成为一个切实可行的操作系统,特别是在服务器市场中。
Linux 的成功归功于:UNIX + GNU。

1.1.1 什么是UNIX

UNIX哲学

UNIX:多用户,多任务操作系统。硬件兼容非常广泛(从PC工作站到超级计算机都可以运行)。
UNUX哲学:

  1. 简单性:“小而简单” 是一种值得学习的技术。
    越大、越复杂的系统注定包含越大、越复杂的错误。
    一个程序执行一个任务,易扩展、易维护、易理解使用。

  2. 集中性:让一个程序很好的执行一个任务。
    便于使用、维护、改进。

  3. 可重用组件: 将应用程序的核心实现为库。
    面向组件编程思想,效率高且稳定,维护成本低,以扩展使用。

  4. 过滤器:对输入进行转换并产生输出。

  5. 开放的文件格式:使用纯ASCII码的文本文件或者XML文件作为配置文件或数据文件。
    用户可以使用标准工具来修改和搜索配置项,并且可以开发出新工具在数据文件上执行新功能。

  6. 灵活性: 不能期待用户都能非常正确地使用你的程序。
    永远不要认为你知道用户想做的一切事。
    在实际开发中不断总结。

1.1.2 什么是Linux

Linux:可以自由发布的类UNIX内核实现,是一个操作系统的底层核心。
开源的类UNIX内核。
所有UNIX编写的程序都可以在Linux上编译运行。
只要设备有处理器,就会有人试图让Linux运行其上。
Linux硬件兼容性强。

1.1.3 GNU项目和自由软件基金会

1.1.4 Linux发行版

Linux 系统 = Linux 内核 + GNU自由发布的软件。

1.2 Linux程序设计

1.2.1 Linux程序

Linux应用程序表现为两种特殊类型的文件:
可执行文件:在计算机中直接运行的程序,相当于Windows中的.exe文件。
脚本文件:一组指令的集合,这些指令由另一个程序(解释器)来执行,相当于windows中的.bat文件、.cmd文件或者解释执行的BASIC程序。

不要求可执行文件和脚本文件具有特殊的文件名或扩展名。
可以用编译过的程序代替脚本(反之亦然)而不会影响其他程序或调用者。
在用户级别:本质上可执行程序和脚本没有任何不同。

登录进入Linux时,与shell程序进行交互。
在一组制定目录路径下按照用户给出的程序名搜索与之同名的文件,搜索路径存储在shell变量PATH里,搜索路径由管理员配置,通常包含如下一些存储系统程序的标准路径。

  • /bin:二进制文件目录,用于存放启动系统时用到的程序。
  • /usr/bin:用户二进制文件目录,用于存放用户使用的标准程序。
  • /usr/local/bin:本地二进制文件目录,用于存放软件安装的程序。
  • /sbin和/usr/sbin:存放系统管理程序的目录。(root用户可见)
  • /opt:可选的操作系统组件和第三方应用程序可能被安装的目录。

除非确信后果,否则不要从PATH中删除目录。
Linux使用 : 分隔PATH变量里的条目。
Linux使用 / 分隔文件名里的目录名。

1.2.2 文本编辑器

1.2.3 C语言编辑器

实验:打印hello world
代码演示:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	printf("hello world\n");
	exit(0);
}

运行结果:
运行结果

如果忘记使用 -o name 选项告诉编译器可执行程序的名字,编译器就会把程序放在一个名为 a.out的文件里。

1.2.4 开发系统引导

应用程序

系统为正常使用提供的应用程序,包括用于程序开发的工具存放在:/usr/bin 目录。
系统管理员为某个特征的主机或本地网络添加的程序通常存放在:/usr/local/bin/opt目录。

系统管理员一般喜欢使用/opt/usr/local目录。
原因:分离了厂商提供及后续添加的文件与系统本身提供的应用程序。
好处:需要升级操作系统时,只有目录/opt/usr/local目录的内容需要保留。

建议:
系统级应用程序放在/usr/local目录中来运行和访问所需的文件。
开发用和个人应用程序放在用户家目录中来保存。

头文件

头文件提供常量的定义和对系统函数及库函数调用的声明。
调用C语言编译器时,可以使用 -I 标志来包含保存在子目录或非标准位置中的头文件:

gcc -I/usr/openwin/include fred.c

指示编译器不仅在标准位置,也在/usr/openwin/include 目录中查找程序fred.c中包含的头文件。

使用grep命令搜索包含某些特定定义的函数原型的头文件:

grep EXIT_ *.h

在当前目录下的所有以.h结尾的文件中搜索字符串EXIT_

库文件

库文件是一组预先编译好的函数的集合。
库的存在:避免重复造轮子,提高开发效率。

标准库文件一般存储在/lib/usr/lib目录中。
C语言编译器链接程序需要知道要搜索那些库文件,默认情况下只搜索标准C语言库。

库文件命名规范:
总是以lib开头。
.a文件表示传统的静态函数库。
.so代表共享函数库。

可以使用 ls/usr/lib 命令查看静态库和共享库。

通过给出完成的库文件路径名或使用 -l 标志来告诉编译器要搜索的库文件。
例如:gcc -o fred fred.c /usr.lib.libm.a
要求编译器编译文件 fred.c 将编译产生的程序文件命名为fred,除了搜索标准的C语言函数库外,还搜索数据库以解决函数引用问题。

例如:gcc -o fred fred.c -lm
-lm 代表标准库目录中名为libm.a的函数库。
如果有共享库,编译器会自动选择共享库。

可以使用 -L 标志为编译器增加库的搜索路径。
例如:gcc -o x11fred -L/usr/openwin/lib x11fred.c -lx11
在搜索标准的C语言函数库之后,用/usr/openwin/lib目录中的libX11库版本来编译和链接程序x11fred。

静态库

函数库:一组处于“准备好使用”状态的目标文件。

程序使用函数库中的某个函数时:
①:包含一个声明该函数的头文件。
②:编译器和链接器将程序代码和函数库结合在一起组成单独的可执行文件。

必须使用 -l 选项指明除标准C语言运行库外还需使用的库。
静态库:也称作归档文件,文件名以.a结尾。

创建和维护自己的静态库:
使用 ar 程序和使用 gcc -c 命令对函数分别进行编译。

尽可能的把函数分别保存到不同的源文件中。
如果函数需要访问公共数据,可以把它们放在同一个源文件中,并使用在该文件中声明的静态变量。

实验:创建静态库
①:创建源代码
fred.c

#include<stdio.h>
#include"lib.h"

void fred(int arg)
{
	printf("fred :we passed %d\n",arg);
}

bill.c

#include<stdio.h>
#include"lib.h"

void bill(char *arg)
{
	printf("bill:we passed %s\n",arg);
}

②编译源文件产生目标文件。

gcc -c bill.c fred.c

编译结果

③为库文件创建头文件。
头文件将声明库文件中的函数。
头文件被所有使用库文件的应用程序所包含。
头文件包含在fred.c 和 bill.c 源文件中。

lib.h

void bill(char *arg);
void fred(int arg);

④调用库函数。

#include<stdlib.h>
#include"lib.h"

int main()
{
        bill("from fun bill\n");
        fred(666);
        exit(0);
}

⑤:为编译器显式指定目标文件,编译目标文件,链接编译好的库文件。
编译程序

⑥:将目标文件添加到归档文件。

将目标文件添加到归档文件
库文件创建完成

⑦:为函数库生成一个内容表。(并非必须步骤)

ranlib libfoo.a

使用-l选项访问函数库。(没有保存到标准位置,必须使用 -L 选项来告诉编译器在何处找到它)

gcc -o program program.o -L. -lfoo

-L. 告诉编译器在当前目录中查找函数库。
-lfoo 告诉编译器使用名为libfoo.a的函数库。
(或者名为libfoo.a的共享库,如果存在)

命令:nm 查看函数库中包含的函数。
命令: nm libfoo.a
查看函数库中包含的函数
命令:nm program
只能查看到 program 程序中使用到 libfoo.a 里面的函数。
最终程序中,只包含库函数中实际需要的函数。

共享库

静态库缺点:同时运行多个应用程序并使用来自于同一个函数库的函数时,内存中就会有同一个函数的多个副本,程序文件自身也有多份同样的副本,消耗大量内存和磁盘空间。

共享库的保存位置与静态库相同。
共享库后缀 .so。

程序使用共享库:
程序本身不包含函数代码,引用运行时可访问的共享代码。
编译好的程序被装载到内存中执行时,函数引用被解析并产生对共享库的调用。
如果有必要,共享库才会被加载到内存中。

好处:
系统可以只保留共享库的一份副本供许多应用程序同时使用,磁盘上也仅保存一份。
共享库更新可以独立于依赖它的应用程序。
例如:文件/lib/libm.so就是对实际库文件修订版本(/lib/libm.so.N,N代表主版本号)的符号链接。

Linux启动应用程序,会考虑应用程序需要的函数库版本,防止函数库的新版本导致旧的应用程序不能使用。

共享库类似于windows中使用的动态链接库。
.so 库对应于DLL文件,都是在程序运行时加载。
.a 库对应于.LAB文件,它们都包含在可执行程序中。

1.3获得帮助

man命令访问在线手册。

第2章 shell程序设计

举报

相关推荐

0 条评论