Linux 程序设计--读书笔记

1 Linux 程序设计

1.1 Linux shell

登录进 Linux 与 shell 进行交互时, 它像 Windows 中的命令提示窗口一样运行程序. 它在一组指定的目录路径下按照你给出的程序名搜索与之同名的文件. 搜索的目录路径存储在 shell 变量 PATH 中, 这一点与Windows 也十分类似. 搜索路径 (你也可以添加这个路径) 由系统管理员配置, 它通常包含如下一些系统存储程序的标准路径.

  • /bin: 二进制文件目录, 用于存放启动系统时用到的程序
  • /usr/bin: 用户二进制文件目录, 用于存放用户使用的标准程序
  • /usr/local/bin: 本地二进制文件目录, 用于存放软件安装的程序

系统管理员登录后使用的 PATH 变量可能还包含存放系统管理程序的目录, 如/sbin/usr/sbin.

可选的操作系统组件和第三方应用程序可能被安装在/opt目录下, 安装程序可以通过用户安装脚本将路径添加到PAHT环境变量中.

从PATH中删除目录并不是一个好注意, 除非确信你了解这么做的后果.

Linux 像 Unix 一样, 使用: 分隔 PATH中的条目, 而 Windows 使用;
例:

/usr/local/bin:/bin:/usr/bin:.:/home/hill/bin

Linux 使用 /分隔文件名里的目录名, 而 Windows 使用\.

1.2 开发系统导引

软件工具和开发资源在系统中存放的位置

1.2.1 应用程序

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

系统管理员一般喜欢使用/usr/local/bin/opt, 以分离厂商提供及后续添加的文件与系统本身提供的应用程序. 这种组织的好处在于, 升级系统时只需保留/opt/usr/local里的内容即可. 文件组织建议: 系统级的应用程序放在 /usr/local中运行和访问所需文件; 开发用和个人的应用程序, 放在/home中的一个文件夹下.

GNU 编译系统的驱动程序 gcc 一般位于/usr/bin/usr/local/bin中, 但它会从其它位置运行各种编译器支持的应用程序. 这个位置是在编译编译器本身时指定的, 并且它随主机类型的不同而不同. 对 Linux 系统来说, 这个位置可能是 /usr/lib/gcc目录下的一个版本特定的子目录.

1.2.2 头文件

C 语言的头文件几乎总位于/usr/include目录及其子目录中. 依赖于特定 Linux 版本的头文件通常位于 /usr/include/sys/usr/include/linux中.

其它编程系统各自的头文件被存储在可被相应编译器自动搜索到的目录里. 例如, X视窗系统的/usr/include/X11目录和GNU C++的/usr/include/c++目录.

在调用C语言编译器时, 可以使用-I标志来包含保存在子目录或非标准位置中的头文件. 例如:

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

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

1.2.3 库文件

库是一组按照可重用原则编写并预先编译好的函数的集合.

标准系统库文件一般存储在/lib/usr/lib目录中. C语言编译器(更确切地说是链接程序)需要知道要搜索哪些库文件, 因为在默认情况下, 它只搜索标准C语言库.

库文件的名字总是以lib开头, 随后的部分指明是什么库. 文件名后缀给出库类型:

  • .a代表静态函数库
  • .so代表共享函数库

可以通过给出完整的库文件路径或用-l标志来告诉编译器要搜索的库文件. 例如:

$ gcc -o fred fred.c /usr/lib/libm.a

编译器编译fred.c, 将编译产生的程序文件命名为fred, 并且除了搜索标准的C语言函数库外, 还搜索数学库以解决函数引用问题. 下面的命令也能产生类似的结果:

$ gcc -o fred fred.c -lm

-lm 是简写方式, 代表标准库目录中名为libm.a的函数. -lm的好处是如果有共享库, 编译器会自动选择共享库.

虽然库文件和头文件一样,通常都保存在标准位置, 但你也可以通过使用 -L标志为编译器增加库的搜索路径. 例如:

$ gcc -o x11fred -L/usr/openwin/lib x11fred.c -lX11

这条命令用/usr/openwin/lib/目录中的libX11库版本编译和链接程序x11fred

1.2.4 静态库

函数库最简单的形式是一组处于”准备好使用”状态的目标文件. 当程序需要使用函数库中的某个函数时, 它包含一个声明该函数的头文件. 编译器和链接器负责将程序代码和函数库结合在一起以组成一个单独的可执行文件. 必须使用-l选项指明除标准C语言运行库外还需要使用的库.

静态库, 也称归档文件(archive), 按惯例文件名都以.a结尾. 只要使用ar (代表archive, 即建立归档文件) 程序和使用 gcc -c命令对函数分别进行编译. 应该尽可能把函数分别保存到不同的源文件中. 如果函数需要访问公共数据, 就可以把他们放在同一个源文件中, 并使用在该文件中声明的静态变量.


创建静态库:
(1) 创建函数源文件fred.c和bill.c
源文件fred.c

// fred.c
#include <stdio.h>

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

源文件bill.c

#include <stdio.h>

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

(2) 分别编译这两个函数以产生要包含在库文件中的目标文件.

$ gcc -c bill.c fred.c
$ ls *.o
bill.o fred.o

(3) 创建库文件. 使用ar程序创建一个归档文件并将上一步产生的目标文件添加进去. ar可以用来创建任何类型文件的归档文件. 由此可见静态库只是目标文件的简单归档!

$ ar crv libfoo.a bill.o fred.o
a - bill.o
a - fred.o

必要参数:

  • -r:将文件插入备存文件中

选项参数

  • -c:建立备存文件
  • -v:程序执行时显示详细的信息

(4) 使用库函数. 先编写一个调用bill函数的程序. 首先, 为库文件创建一个头文件. 这个头文件声明库文件中的函数, 并被所有使用库文件的应用程序包含.
头文件lib.h

//lib.h
void bill(char *);
void fred(int);                

调用程序program.c

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

int main()
{
    bill("Hello World");
    exit(0);
}

编译测试:

$ gcc -c program.c
$ gcc -o program program.o bill.o
$ ./program
bill: we pass Hello World

使用库文件:
方式一:

$ gcc -o program program.o libfoo.a 
$ ./program
bill: we pass Hello World

方式二:

$ gcc -o program program.o -L. -lfoo 
$ ./program
bill: we pass Hello World
# -L选项告诉编译器搜索路径, -L.为搜索当前目录.
# -lfoo告诉编译器搜索名为libfoo.a或libfoo.so的库文件.
# 可以使用nm命令查看目标文件, 函数库, 可执行文件中包含的函数
$ nm libfoo.a 

bill.o:
0000000000000000 T bill
                 U printf

fred.o:
0000000000000000 T fred
                 U printf

$ nm program
000000000040057e T bill
0000000000601040 B __bss_start
0000000000601040 b completed.7594
0000000000601030 D __data_start
0000000000601030 W data_start
00000000004004a0 t deregister_tm_clones
0000000000400520 t __do_global_dtors_aux
0000000000600e18 t __do_global_dtors_aux_fini_array_entry
0000000000601038 D __dso_handle
0000000000600e28 d _DYNAMIC
0000000000601040 D _edata
0000000000601048 B _end
                 U exit@@GLIBC_2.2.5
0000000000400624 T _fini
0000000000400540 t frame_dummy
0000000000600e10 t __frame_dummy_init_array_entry
00000000004007a0 r __FRAME_END__
0000000000601000 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
0000000000400654 r __GNU_EH_FRAME_HDR
0000000000400400 T _init
0000000000600e18 t __init_array_end
0000000000600e10 t __init_array_start
0000000000400630 R _IO_stdin_used
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
0000000000600e20 d __JCR_END__
0000000000600e20 d __JCR_LIST__
                 w _Jv_RegisterClasses
0000000000400620 T __libc_csu_fini
00000000004005b0 T __libc_csu_init
                 U __libc_start_main@@GLIBC_2.2.5
0000000000400566 T main
                 U printf@@GLIBC_2.2.5
00000000004004e0 t register_tm_clones
0000000000400470 T _start
0000000000601040 D __TMC_END__

库函数libfoo.a中包含fred和bill两个函数, 而program中只包含bill.

1.2.5 共享库

一个使用共享库的程序的链接方式: 程序本身不再包含函数代码, 而是引用运行时可访问的共享代码. 当编译好的程序被装载到内存中执行时, 函数引用被解析并产生对共享库的调用, 如果有必要, 共享库才被加载到内存中.

可以通过运行工具ldd来查看一个程序需要的共享库.

$ ldd program
    linux-vdso.so.1 =>  (0x00007fff0d565000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007faa237dd000)
    /lib64/ld-linux-x86-64.so.2 (0x00007faa23ba7000)

可以看出, 标准C语言库函数(libc)是共享的(.so).程序需要的主版本号是6.

猜你喜欢

转载自blog.csdn.net/Yuberhu/article/details/80215171