《Linux程序设计(第四版)》---第一章:入门

贯穿本书的一个大型示例项目:一个简单的用于记录音乐CD详细资料的数据库应用程序


unix是一种多用户多任务的操作系统


unix

unix操作系统鼓励一种特定的编程风格,以下是典型的unix程序和系统所具有的特点

  • 简单
  • 集中
    当用户出现新的需求时,把小工具组合起来已完成更加复杂的任务,而不是将所有功能都放在一个大程序中
  • 可重用组件
    将应用程序的核心实现为库,具有简单而灵活的编程接口、文档完备的库可以帮助其他人开发出同类程序
  • 过滤器
    对输入进行转换并且产生输出
  • 开放的文件格式
  • 灵活性
    尽量避免随意限制字段长度或记录数目

Linux

Linux是一个可以自由发布的类unix内核实现,是一个操作系统的底层核心,目的是保证linux除包含可以自由发布的代码外,不会集成任何专有代码


GNU

GNU项目的宗旨是试图创建一个与unix系统兼容,但是并不受unix名字和源代码私有权限制的操作系统和开发环境


Linux程序表现为两种特殊类型的文件

  • 可执行文件:计算机可以直接运行的程序
  • 脚本文件:一组指令的集合,指令将由另一个程序(即解释器)来执行

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


Linux像UNIX一样,使用冒号(:)来分割path变量里的条目
Linux使用正斜线(/)分割文件名中的目录名,Windows是反斜线(\)


对于Linux开发人员来所,连接软件工具和开发资源在系统中存放的位置是很重要的

以下介绍一些重要的目录和文件

1、应用程序
系统为正常使用提供的程序,包括用于程序开发的工具,都可在目录/usr/bin中找到;系统管理员为某个特定的主机或本地网络添加的程序通常在/usr/local/bin或/opt中找到
建议对于系统级的应用程序,将其放在/usr/local目录来运行和访问所需的文件
X视窗系统通常安装在/usr/X11或/usr/bin/X11目录中
2、头文件
用c语言或者其他语言进行程序设计时,需要头文件来提供对常量的定义和对系统函数及库函数调用的声明,对c语言来说,这些头文件几乎总是位于/usr/include目录及其子目录中


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

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

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

扫描二维码关注公众号,回复: 6462921 查看本文章


用grep来搜索包含某些特定定义和函数原型的头文件是很方便的。若想知道用于从程序中返回退出状态的#define定义的名字,你只需切换到/usr/include目录,再用grep命令搜索可能的名字部分
如下:

grep EXIT_ *.h

输出

argp.h:#define ARGP_HELP_EXIT_ERR       0x100 /* Call exit(1) instead of returning.  */
argp.h:#define ARGP_HELP_EXIT_OK        0x200 /* Call exit(0) instead of returning.  */
argp.h:  (ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR)
argp.h:  (ARGP_HELP_SHORT_USAGE | ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR)
argp.h:  (ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG | ARGP_HELP_EXIT_OK \
stdlib.h:#define        EXIT_FAILURE    1       /* Failing exit status.  */
stdlib.h:#define        EXIT_SUCCESS    0       /* Successful exit status.  */

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

3、库文件
库是一组预先编译好的函数的集合,通常由一组相互关联的函数组成以执行某项常见的任务,如数据库访问例程(dbm库)
标准系统库文件一般存储在/lib和/usr/lib目录中
库文件必须遵循特定的命名规范并且需要在命令行中明确指定
库文件以lib开头,随后的部分指出这是什么库(c表示c语言库,m表示数学库)文件名的最后部分以 . 开始,然后给出库文件的类型:

  • .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代表标准库目录(本例为/usr/lib)中名为libm.a的函数库,使用-lm的另一个好处是如果有共享库,编译器会自动选择共享库

可以使用-L标志位编译器增加库的搜索路径,如:

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

用/usr/openwin/lib目录中的libX11库版本来编译和链接程序下X11fred
4、静态库
程序需要使用函数库中的某个函数时,包含一个声明该函数的头文件。编译器和链接器负责将程序代码和函数库链接在一起以组成一个单独可执行的文件。需要使用-l选项指明c语言运行库外还需要使用的库

静态库也成为归档文件(archive),以.a结尾,如c语言标准函数库为/usr/lib/libc.a和X11函数库/usr/lib/libX11.a

使用ar(代表archive,即建立归档文件)程序和使用gcc -c可以对函数分别进行编译,可以创建维护自己的静态库


下面将建立一个函数库,包含fred与bill两个函数,将会在示例程序中调用其中一个函数,这两个函数只打印欢迎信息。
(1)创建源文件,分别命名为fred.c与bill.c:
fred.c

#include<stdio.h>

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

bill.c

#include<stdio.h>
void bill(char *arg){
        printf("bill: we passed %s\n",arg);
}

(2)分别编译两个函数产生要包含在库文件中的目标文件。使用-c可以阻止编译器创建一个完整的程序。

gcc -c bill.c fred.c

(3)创建一个头文件lib.h,声明库文件中的函数:
lib.h

/*
This is lib.h. It declares the functions fred and bill for users
*/

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

(4)创建示例程序program.c,记得包含头文件lib.h:

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

int main(){
        bill("Re:CREATORS");
        exit(0);
}

(5)编译文件,并且与bill.o链接:

gcc -c program.c
gcc -o program program.o bill.o
./program

结果为:

bill: we passed Re:CREATORS

(6)创建并且使用一个库文件。使用ar程序创建归档文件并且将目标文件添加到归档文件中。程序被称为ar的原因是它将若干个单独的文件归并到一个大文件中以创建归档文件或集合。也可以用ar程序创建任意类型文件的归档文件

ar crv libfoo.a bill.o fred.o

输出

a - bill.o
a - fred.o

(7)通过上步,创建好库文件,两个目标文件也添加进去了。还需要为函数库生成一个内容表,可以通过ranlib命令:

ranlib libfoo.a #威函数库生成一个内容表

现在函数库可以使用,可以在编译器使用的文件列表中添加该库文件以创建自己的程序:

gcc -o program program.o libfoo.a
./program

输出

bill: we passed Re:CREATORS

可以通过-l选项访问函数库,因为没有保存在标准位置,需要使用-L选项来告诉编译器在何处找到它:

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

-L告诉编译器在当前目录(.)查找函数库
-lfoo告诉使用libfoo.a的函数库

要查看包含在目标文件、函数库或者可执行文件里的函数,使用nm命令

程序被创建时,只包含函数库实际需要的函数
在这里插入图片描述
5、共享库
静态库的缺点是,当运行多个应用程序并且都使用同一函数库的函数时,内存就会有同一函数的多份副本,会浪费内存和磁盘空间
共享库的保存位置与静态库是一样的,在Linux中,标准数学库的共享版本是/usr/lib/libm.so

程序使用共享库时,程序本身不再含函数代码,而是引用运行时可访问的共享代码。当编译好的程序被装载到内存执行时,函数引用被解析并且产生对共享库的调用,有必要才会将共享库加载到内存中。
这样系统就可以只保留共享库的一份副本供许多应用程序调用,而且共享库的更新可以独立于依赖它的应用程序
当Linux启动应用程序时,会考虑应用程序需要的函数库版本,防止函数库的最新版本使得旧的应用程序不能使用

对Linux来说,装载共享库并且解析客户应用程序函数引用的程序(动态装载器)是ld.so,有可能是其他。用于搜索共享库的额外位置可以在文件/etc/ld.so.conf中配置。若修改了这个文件,需要执行ldconfig处理(如安装x视窗系统以后需要添加x11共享库)

可以通过运行工具ldd查看程序需要的共享库。如在示例程序上运行ldd,会看到下面结果

ldd program

输出

linux-vdso.so.1 (0x00007fffe1489000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f423f490000)
/lib64/ld-linux-x86-64.so.2 (0x00007f423fc00000)

可以由上看到,标准c语言函数库(libc)是共享的(.so),程序需要的版本号为6

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


获得帮助

man命令可以访问在线手册页

GNU软件和其他自由软件还会使用名为info的在线文档系统。可以通过程序info或通过emacs编辑器中的info命令来在线浏览全部的文档

如:
查看GNU C语言编译器(gcc)的文档:

(1)查看手册页:

man gcc

结果:

GCC(1)                                                   GNU                                                   GCC(1)

NAME
       gcc - GNU project C and C++ compiler

SYNOPSIS
       gcc [-c|-S|-E] [-std=standard]
           [-g] [-pg] [-Olevel]
           [-Wwarn...] [-Wpedantic]
           ...
           ...
           ...
           ...

(2)使用info查看gcc文档

info gcc

输出:

GCC(1)                                                   GNU                                                  GCC(1)

NAME
       gcc - GNU project C and C++ compiler

SYNOPSIS
       gcc [-c|-S|-E] [-std=standard]
           [-g] [-pg] [-Olevel]
           [-Wwarn...] [-Wpedantic]
           ...
           ...
           ...

猜你喜欢

转载自blog.csdn.net/qq_40061206/article/details/91365024
今日推荐