Unix 环境高级编程(一):开发环境

本文详细介绍Unix环境下编程所需的各种工具和技术,包括GCC编译器的基本用法及高级特性、静态库和共享库的创建与使用方法,以及如何动态加载共享库等内容。

一、Unix操作系统

二、Linux操作系统

三、GNU编译工具(GCC)

1、简介

  GCC是以GPL许可证所发行的自由软件,也是GNU计划的关键部分。GCC的初衷是为GNU操作系统专门编写一款编译器,现已被大多数类Unix操作系统(如Linux、BSD、MacOS X等)采纳为标准的编译器,甚至在微软的Windows上也可以使用GCC。GCC支持多种计算机体系结构芯片,如x86、ARM、MIPS等,并已被移植到其他多种硬件平台。

  GCC原名为GNU C语言编译器(GNU C Compiler),只能处理C语言。但其很快扩展,变得可处理C++,后来又扩展为能够支持更多编程语言,如Fortran、Pascal、Objective -C、Java、Ada、Go以及各类处理器架构上的汇编语言等,所以改名GNU编译器套件(GNU Compiler Collection)。

2、基本用法

gcc [options] [filenames] 
/* GCC最基本的用法是∶gcc [options] [filenames] 
 * 其中 options 就是编译器所需要的参数
 * filenames 给出相关的文件名称
 * */

-c /* 只编译,不链接成为可执行文件,
	* 编译器只是由输入的.c等源代码文件生成.o为后缀的目标文件,
	* 通常用于编译不包含主程序的子程序文件 
	* */
	
-o /* output_filename,确定输出文件的名称为output_filename,
	* 同时这个名称不能和源文件同名。
	* 如果不给出这个选项,gcc就给出预设的可执行文件a.out 
	* */

-x /* 设定文件所使用的语言, 使后缀名无效, 对以后的多个有效
	* 根据约定 C 语言的后缀名称是 .c 的,
	* 而 C++ 的后缀名是 .C 或者 .cpp, 
	* 如果你很个性,决定你的 C 代码文件的后缀名是 .pig ,
	* 那你就要用这个参数, 这个参数对他后面的文件名都起作用,
	* 除非到了下一个参数的使用
	*  */

-I /* Idirname,
	* 将dirname所指出的目录加入到程序头文件目录列表中,
	* 是在预编译过程中使用的参数 
	* */

-E /* 只激活预处理,这个不生成文件, 
	* 你需要把它重定向到一个输出文件里面 
	* */

-S /* 只激活预处理和编译,就是指把文件编译成为汇编代码 */
	
-g /* 产生符号调试工具(GNU的gdb)所必要的符号资讯,
	* 要想对源代码进行调试,我们就必须加入这个选项
	*  */

-O /* 对程序进行优化编译、链接,
	* 采用这个选项,整个源代码会在编译、链接过程中进行优化处理,
	* 这样产生的可执行文件的执行效率可以提高,
	* 但是,编译、链接的速度就相应地要慢一些 
	* */

-v 			/*  gcc执行时执行的详细过程,gcc及其相关程序的版本号 */

-pedantic 	/* 对不符合ANSI/ISO C语言标准的扩展语法产生警告 */

-Wall 		/* 产生尽可能多的警告 */

-Werror 	/* 将警告作为错误处理 */

3、文件后缀

.c		/* C语言源代码文件 */

.h		/* 程序所包含的头文件 */

.i		/* 预处理后的C语言源代码文件 */

.s		/* 汇编语言文件 */

.S		/* 经过预编译的汇编语言源代码文件 */

.o		/* 编译后的目标文件 */

.a		/* 静态库文件 */

.so		/* 共享库(动态库)文件 */

4、构建过程

编辑 -> 预编译(预处理)-> 编译 -> 汇编 -> 链接

  1. 编辑(hello.c)
/* 使用 vim 编辑器编写代码 */
vim hello.c
  1. 预编译(hello.i)
/* 使用 -E 选项,生成 .i 预编译文件
 * 这个过程处理宏定义和include,去除注释,不会对语法进行检查
 */
gcc -E hello.c -o hello.i
  1. 编译(hello.s)
/* 使用 -S 选项,生成 .s 汇编文件
 * 这个阶段,检查语法
 */
gcc -S hello.i
  1. 汇编(hello.o)
/* 使用 -c 选项,生成 .o 目标文件 */
gcc -c hello.s
  1. 链接(hello)
/* 使用 -o 选项,生成可执行文件 */
gcc hello.o -o hello

5、预处理指令

#include      // 将指定文件的内容插至此指令处 
#include_next // 与#include一样,但从当前目录之后的目录查找,极少用
#define       // 定义宏
#undef        // 删除宏
#if           // 判定
#ifdef        // 判定宏是否已定义
#ifndef       // 判定宏是否未定义
#else         // 与#if、#ifdef、#ifndef结合使用
#elif         // else if多选分支
#endif        // 结束判定
##            // 连接宏内两个连续的字符串
#             // 将宏参数扩展成字符串字面值
#error        // 产生错误,结束预处理
#warning      // 产生警告
#pragma       // 提供额外信息的标准方法,可用于指定平台
#pragma GCC dependency <文件>     // 若<文件>比此文件新则产生警告
#pragma GCC poison <标识>         // 若出现<标识>则产生错 误
#pragma pack(1/2/4/8)             // 按1/2/4/8字节对齐补齐
#line                             // 指定行号

For example:

  1. error.c
#include <stdio.h>

#if (VERSION < 1)
	#error "Version is too low!"
#elif (VERSION > 4)
	#warning "version is too high!"
#endif

int main(void)
{
	printf("Version is :%d\n", VERSION);

	return 0;
}

在这里插入图片描述

  1. line.c
#include <stdio.h>

int main(void)
{
	printf("line is %d\n", __LINE__);
#line 100
	printf("line is %d\n", __LINE__);	

	return 0;
}

在这里插入图片描述

  1. pragma.c
#include <stdio.h>

#pragma GCC dependency "tmp.c" // 若tmp.c比此文件新则产生警告

int main(void)
{
		

	return 0;
}

在这里插入图片描述

#include <stdio.h>

#pragma GCC poison goto float  // 若出现 goto 或 float 则产生错误

int main(void)
{

	float a;
		
loop :
	goto loop;

	return 0;
}

在这里插入图片描述

/* 64位操作系统下,默认8字节对齐 */
#include <stdio.h>

#pragma pack(1)
    struct S1 
    {
        double d;
        char   c;
        int    i;
        short  h;
    }; // DDDDDDDDCIIIIHH, 15
#pragma pack(4)
    struct S2 
    {
        double d;
        char   c;
        int    i;
        short  h;
    }; // DDDDDDDDCXXXIIIIHHXX, 20
#pragma pack(8)
    struct S3 
    {
        double d;
        char   c;
        int    i;
        short  h;
    }; // DDDDDDDDCXXXXXXXIIIIHHXX, 24


int main(void)
{
#pragma pack()
    printf ("S1: %lu字节\n", sizeof (struct S1));
    printf ("S2: %lu字节\n", sizeof (struct S2));
    printf ("S3: %lu字节\n", sizeof (struct S3));

	return 0;
}

在这里插入图片描述

6、预定义宏

__BASE_FILE__     // 正在编译的源文件名
__FILE__          // 所在文件名
__LINE__          // 行号
__FUNCTION__      // 函数名
__func__          // 同__FUNCTION__
__DATE__          // 日期
__TIME__          // 时间
__INCLUDE_LEVEL__ // 包含层数,从0开始
__cplusplus       // C++编译器将其定义为1,
                  // C编译器不定义该宏

For example:

  1. print.h
#ifndef _PRINT_H
#define _PRINT_H

#include <stdio.h>

void print (void) 
{
    printf ("__BASE_FILE__     : %s\n", __BASE_FILE__);
    printf ("__FILE__          : %s\n", __FILE__);
    printf ("__LINE__          : %d\n", __LINE__);
    printf ("__FUNCTION__      : %s\n", __FUNCTION__);
    printf ("__func__          : %s\n", __func__);
    printf ("__DATE__          : %s\n", __DATE__);
    printf ("__TIME__          : %s\n", __TIME__);
    printf ("__INCLUDE_LEVEL__ : %d\n", __INCLUDE_LEVEL__);
#ifdef __cplusplus
    printf ("__cplusplus       : %d\n", __cplusplus);
#endif // __cplusplus
}

#endif//_PRINT_H
  1. predef.h
#ifndef _PREDEF_H
#define _PREDEF_H

#include "print.h"

#endif//_PREDEF_H

  1. predef.c
#include "predef.h"


int main (void) 
{
    print ();
    return 0;
}

在这里插入图片描述
在这里插入图片描述

7、环境变量

C_INCLUDE_PATH     // C头文件的附加搜索路径,相当于gcc的-I选项
CPATH              // 同C_INCLUDE_PATH
CPLUS_INCLUDE_PATH // C++头文件的附加搜索路径
LIBRARY_PATH       // 链接时查找静态库/共享库的路径
LD_LIBRARY_PATH    // 运行时查找共享库的路径
  1. 通过gcc的-I选项指定C/C++头文件的附加搜索路径
gcc calc.c cpath.c -I.
  1. 将当前目录作为C头文件附加搜索路径,添加到CPATH环境变量中
export CPATH=$CPATH:. // export保证当前shell的,子进程继承此环境变量
echo $CPATH
env | grep CPATH
  1. 也可以在/.bashrc或/.bash_profile,配置文件中写环境变量,永久有效
export CPATH=$CPATH:.
执行
# source ~/.bashrc 或 # source ~/.bash_profile
//生效,以后每次登录自动生效。
  1. 头文件的三种定位方式
    a)#include “目录/xxx.h”
      头文件路径发生变化,需要修改源程序
    b)C_INCLUDE_PATH/CPATH=目录
      同时构建多个工程,可能引发冲突
    c)gcc -I目录
      既不用改程序,也不会有冲突
  2. 头文件的作用
    a)声明外部变量、函数和类
    b)定义宏、类型别名和自定义类型
    c)包含其它头文件
    d)借助头文件卫士,防止因同一个头文件被多次包含,而引发重定义错
  3. 包含头文件时需要注意的问题
    a)gcc的-I选项
      指定头文件附加搜索路径
    b)#include <…>
      先找-I指定的目录,再找系统目录
    c)#include “…”
      先找-I指定的目录,再找当前目录,最后找系统目录
    d)头文件的系统目录
/usr/include 
/usr/local/include 
/usr/lib/gcc/x86_64-linux-gnu/5.4.0/include

四、静态库

1、简介

  1. 链接静态库是将库中的被调用代码复制到调用模块中
  2. 静态库占用空间非常大,不易修改但执行效率高
  3. 静态库的缺省扩展名是.a

2、创建静态库

  1. 编辑源代码
vim xxx.c/xxx.h
  1. 编译成目标文件
gcc -c xxx.c -o xxx.o
  1. 打包成静态库文件
ar -r libxxx.a xxx.o ...

3、ar 指令

ar指令:ar [选项] 静态库文件名 目标文件列表 
-r // 将目标文件插入到静态库中,已存在则更新 
-q // 将目标文件追加到静态库尾 
-d // 从静态库中删除目标文件 
-t // 列表显示静态库中的目标文件 
-x // 将静态库展开为目标文件 
/* 注意:提供静态库的同时也需要提供头文件 */

4、调用静态库

  1. 直接调用
gcc main.c libxxx.a
  1. 通过LIBRARY_PATH环境变量指定库路径
export LIBRARY_PATH=$LIBRARY_PATH:.
gcc main.c -lmath (环境法)
  1. 通过gcc的-L选项指定库路径
unset LIBRARY_PATH
gcc main.c -lmath -L. (参数法)
  1. 一般化的方法:
gcc .c/.o -l<库名> -L<库路径>
  1. 运行
      在可执行程序的链接阶段,已将所调用的函数的二进制代码,复制到可执行程序中,因此运行时不需要依赖静态库。

五、共享库

1、简介

  1. 链接共享库则只是在调用模块中,嵌入被调用代码在库中的(相对)地址
  2. 共享库占用空间小,易于修改但执行效率略低
  3. 共享库的缺省扩展名是.so

2、创建共享库

  1. 编辑源程序
vim xxx.x/xxx.h
  1. 编译成目标文件
gcc -c -fpic xxx.c
  1. 链接成共享库文件
gcc -shared xxx.o -o libxxx.so
  1. PIC (Position Independent Code)
      位置无关代码。可执行程序加载它们时,可将其映射到其地址空间的任何位置。
-fPIC // 大模式,生成代码比较大,运行速度比较慢,所有平台都支持。 
-fpic // 小模式,生成代码比较小,运行速度比较快,仅部分平台支持。 
/* 注意:提供共享库的同时也需要提供头文件 */

3、调用共享库

  1. 直接调用
gcc main.c libxxx.so
  1. 通过LIBRARY_PATH环境变量指定库路径
export LIBRARY_PATH=$LIBRARY_PATH:.
gcc main.c -lmath (环境法)
  1. 通过gcc的-L选项指定库路径
unset LIBRARY_PATH
gcc main.c -lmath -L. (参数法)
  1. 一般化的方法
gcc .c/.o -l<库名> -L<库路径>

3、运行

  运行时需要保证LD_LIBRARY_PATH,环境变量中包含共享库所在的路径。
  在可执行程序的链接阶段,并不将所调用函数的二进制代码复制到可执行程序中。
  而只是将该函数在共享库中的地址嵌入到可执行程序中,因此运行时需要依赖共享库。
  gcc缺省链接共享库,可通过-static选项强制链接静态库。

六、动态加载共享库

1、头文件

#include <dlfcn.h>

2、加载共享库

/*
* 返回值:
* 		成功返回共享库句柄,失败返回NULL。
* 
* 参数:
*   filename:共享库路径
*      若只给文件名,则根据LD_LIBRARY_PATH环境变量搜索。
*   flag取值:加载方式
*      RTLD_LAZY - 延迟加载,使用共享库中的符号 (如调用函数)时才加载。 
*      RTLD_NOW  - 立即加载。
*/
void* dlopen (const char* filename,    int flag);

3、获取函数地址

/*
 * 返回值:
 * 		成功返回函数地址,失败返回NULL。
 * 
 * 参数:
 * 		hanedle:共享库句柄
 * 		symbol:函数名
 */
void* dlsym (void* handle,const char* symbol );

4、卸载共享库

/*
 * 成功返回0,失败返回非零。
 */
int dlclose (void* handle);

5、 获取错误信息

/*
 * 有错误发生则返回错误信息字符串指针,否则返回NULL。
 */
char* dlerror (void);

七、辅助工具

本书全面介绍了UNIX系统的程序设计界面—系统调用界面和标准C库提供的许多函数。 本书的前15章着重于理论知识的阐述,主要内容包括UNIX文件和目录、进程环境、进程控制、 进程间通信以及各种I/O。在此基础上,分别按章介绍了多个应用实例,包括如何创建数据库函数库, PostScript 打印机驱动程序,调制解调器拨号器及在伪终端上运行其他程序的程序等。 本书内容丰富权威, 概念清晰精辟,直以来被誉为UNIX编程的“圣经”,对于所有UNIX程序员—无论是初学者还是专家级人士 —都是本无价的参考书籍。 目 录 译者序 译者简介 前言 第1章 UNIX基础知识 1 1.1 引言 1 1.2 登录 1 1.2.1 登录名 1 1.2.2 shell 1 1.3 文件和目录 2 1.3.1 文件系统 2 1.3.2 文件名 2 1.3.3 路径名 2 1.3.4 工作目录 4 1.3.5 起始目录 4 1.4 输入和输出 5 1.4.1 文件描述符 5 1.4.2 标准输入、标准输出和标准 出错 5 1.4.3 不用缓存的I/O 5 1.4.4 标准I/O 6 1.5 程序和进程 7 1.5.1 程序 7 1.5.2 进程和进程ID 7 1.5.3 进程控制 7 1.6 ANSI C 9 1.6.1 函数原型 9 1.6.2 类属指针 9 1.6.3 原始系统数据类型 10 1.7 出错处理 10 1.8 用户标识 11 1.8.1 用户ID 11 1.8.2 组ID 12 1.8.3 添加组ID 12 1.9 信号 12 1.10 UNIX时间值 14 1.11 系统调用和库函数 14 1.12 小结 16 习题 16 第2章 UNIX标准化及实现 17 2.1 引言 17 2.2 UNIX标准化 17 2.2.1 ANSI C 17 2.2.2 IEEE POSIX 18 2.2.3 X/Open XPG3 19 2.2.4 FIPS 19 2.3 UNIX实现 19 2.3.1 SVR4 20 2.3.2 4.3+BSD 20 2.4 标准和实现的关系 21 2.5 限制 21 2.5.1 ANSI C限制 22 2.5.2 POSIX限制 22 2.5.3 XPG3限制 24 2.5.4 sysconf、pathconf 和fpathconf 函数 24 2.5.5 FIPS 151-1要求 28 2.5.6 限制总结 28 2.5.7 未确定的运行时间限制 29 2.6 功能测试宏 32 2.7 基本系统数据类型 32 2.8 标准之间的冲突 33 2.9 小结 34 习题 34 第3章 文件I/O 35 3.1 引言 35 3.2 文件描述符 35 3.3 open函数 35 3.4 creat函数 37 3.5 close函数 37 3.6 lseek函数 38 3.7 read函数 40 3.8 write函数 41 3.9 I/O的效率 41 3.10 文件共享 42 3.11 原子操作 45 3.11.1 添加至个文件 45 3.11.2 创建个文件 45 3.12 dup和dup2函数 46 3.13 fcntl函数 47 3.14 ioctl函数 50 3.15 /dev/fd 51 3.16 小结 52 习题 52 第4章 文件和目录 54 4.1 引言 54 4.2 stat, fstat和lstat函数 54 4.3 文件类型 55 4.4 设置-用户-ID和设置-组-ID 57 4.5 文件存取许可权 58 4.6 新文件和目录的所有权 60 4.7 access函数 60 4.8 umask函数 62 4.9 chmod和fchmod函数 63 4.10 粘住位 65 4.11 chown, fchown和 lchown函数 66 4.12 文件长度 67 4.13 文件截短 68 4.14 文件系统 69 4.15 link, unlink, remove和rename 函数 71 4.16 符号连接 73 4.17 symlink 和readlink函数 76 4.18 文件的时间 76 4.19 utime函数 78 4.20 mkdir和rmdir函数
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值