操作系统
UNIX系统介绍
- 最早版本于1970年左右诞生于美国贝尔实验室,作者是丹尼斯·里奇、肯·汤姆逊
- 最早的多用户、多任务、支持多种CPU架构
- 具备高安全性、高可靠性、高稳定性
- 在构建大型关键性业务的商用服务器中进行使用,也能在嵌入式设备中架设
Minix是一种基于微内核架构的类UNIX计算机操作系统,并开源给大学教学研究使用,Linux是受其启发
Linux系统介绍
- Linux,全称GNU/Linux,是一种免费使用和自由传播的类UNIX操作系统,其内核由林纳斯·托瓦兹于1991年10月5日首次发布
- 一个基于POSIX的多用户、多任务、支持多线程和多CPU的操作系统
- Linux继承了UNIX以网络为核心的设计思想,是一个性能稳定的多用户网络操作系
Linux内核版本和发行版本
- Linux只是表示内核是Linux,只要操作系统的内核是Linux,则可以称该系统属于Linux操作系统
- 完整的操作系统:
- 内核+Shell+界面软件
GNU编译工具
多样化
- 支持多种编程语言、支持各种操作系统
- gcc -v 可以查看是否安装了gcc以及版本信息
编译参数
-
-E:只显示预处理的结果
-
-S:生成汇编文件
-
-c:只编译不链接
-
-o:指定编译结果的名字
-
-Wall:尽可能多的产生警告信息
-
-Werror:把警告当错误处理
-
-std:设置编译语法标准 -std=gnu89/99 或者 -std=c99(不建议)
-
-I:指定头文件的加载路径 -I(i) path
-
-l:根据库名指定要使用的库文件 -lm导入数学库
-
-L:指定库文件的查找路径
-
-pedantic:配合使用-ansi 告诉编译器要严格遵守ANSI标准,如果不符合会产生警告
预处理指令
- 是否支持预处理指令由编译器决定
#define // 定义宏常量、宏函数
#include <>/""
#undef // 删除宏
#ifdef/#ifndef/#if
#elif
#endif
#pragma pack(n) // 设置对齐补齐的最大字节数
#pragma once // 相当于头文件卫士
#pragma GCC dependency "文件名" // 监控文件在上一次编译到这一次编译期间是否有修改,如果有会产生警告
#pragma GCC poison key // 设置key标识符为有毒的,禁止出现该标识符,例如禁止出现“goto”
#error "提示信息" // 提示错误 不会生成可执行文件
#warning "提示信息"// 提示警告 会生成可执行文件
#line n // 设置行号,并且会影响后面的行号
// 一般都与条件编译配合使用
库
- 库文件就是目标文件的集合,可以被其他代码调用,把代码封装成库文件后方便使用、方便管理、安全性高、保密性强
静态库 xxx.a
- 就是把目标文件集合成一个以.a结尾的库文件,当调用静态库时,会把静态库中的使用到的二进制指令拷贝到可执行文件中
优点
- 运行速度比共享库快
- 可执行文件运行时不需要依赖静态库文件
缺点
- 可执行文件相对比较大
- 如果静态库内容发生修改,所有之前使用过该静态库的可执行文件都需要重新编译,耗时
制作、使用静态库
制作
- 1、编译出目标文件
- gcc -c xxx.c
- 2、打包目标文件生成静态库文件
- ar -r libxxx.a xxx.o …
- 注意:库文件名字格式必须是libxxx.a / libxxx.so
使用
- 1、直接使用
- gcc code.c libxxx.a
- 2、指定库文件的位置
- -L 指定库的查找路径
- -l 指定要加载的库名 -lxxx [库名是去掉lib和后缀]
- gcc code.c -L path -lxxx
- 3、通过修改环境变量来指定系统查找库文件的默认路径
- 打开系统配置文件 vim ~/.bashrc
- 在文件的末尾添加:
- export LIBRARY_PATH=$LIBRARY_PATH:绝对路径
- 保存退出并重新加载
- source ~/.bashrc
- 使用静态库:
- gcc code.c -lxxx
- 注意:如果是删除配置文件中的环境变量,需要重启终端后才能生效
共享库 xxx.so
- 当调用共享库时,在使用共享库代码的地方形成一个入口,该入口记录了调用的代码在共享库中二进制指令的位置,并且当执行可执行文件时,共享库会与可执行文件一起加载到内存中,当执行共享库的代码时,程序就会跳转到对应的共享库中执行,执行完后可能会返回
- 本质上就是一个带入口的可执行文件,有执行权限
优点
- 可执行文件相对较小
- 如果当共享库发生修改,可执行文件不需要重新编译,只需要重新运行即可
缺点
- 运行速度比静态库慢些
- 可执行文件运行时必须依赖于共享库
制作、使用共享库
制作
- 1、编译生成目标文件
- gcc -fpic -c xxx.c
- -fpic:生成与位置无关的文件
- gcc -fpic -c xxx.c
- 2、打包目标文件生成共享库文件
- gcc -fpic -shared xxx.o … -o libxxx.so
使用
-
注意:共享库要一起与可执行文件加载到内存中才能运行,但是加载执行共享库的路径与编译使用共享库的路径是两种查找路径,因此还需要额外设置共享库的加载路径,一般通过设置环境变量LD_LIBRARY_PATH来实现
-
1、当前路径下直接使用
- gcc code.c libxxx.so
-
2、指定库文件的位置
- gcc code.c -L path -lxxx
-
3、通过修改环境变量来指定系统查找库文件的默认路径
- 打开系统配置文件 vim ~/.bashrc
- 在文件的末尾添加:
- export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:绝对路径
- 保存退出并重新加载
- source ~/.bashrc
- 使用共享库:
- gcc code.c -lxxx
- 注意:如果是删除配置文件中的环境变量,需要重启终端后才能生效
注意:
-
当共享库和静态库文件同名且在同一个查找路径下,编译器编译时会优先使用共享库进行编译
-
可以增加 -static 指定优先查找使用同名的静态库文件
了解
- 共享库的动态加载,作用是在编译时不需要依赖共享库
环境变量表
- 每个程序执行时操作系统都会提供一张环境变量表,该表中记录了操作系统所有的环境变量,这些环境变量的作用反映了当前操作系统的配置情况、以及当前程序所处于的系统环境
- 注意:每个程序的环境变量只是一张复制表,随便修改,不会对真正的环境变量有所影响
查看环境变量表
- env
在程序中获取环境变量表
- 声明:
extern char** environ;
- 操作环境变量表的相关函数:
char *getenv(const char *name);
// 功能:获取某个环境变量的值
// name:环境变量名
// 返回值:返回环境变量的值
int setenv(const char *name, const char *value, int overwrite);
// 功能:对环境变量表中的环境变量进行增加或修改
// name:环境变量名
// value:环境变量的值
// overwrite:当环境变量存在时
// 为真则会修改环境变量
// 为假则不会修改
// 返回值:0表示成功,-1表示失败
int putenv(char *string);
// 功能:以 "name=value" 格式修改或者增加环境变量
// 注意:如果存在则修改,不存在则新增
// 返回值:0表示成功、-1表示失败
int unsetenv(const char *name);
// 功能:删除某个环境变量
// 返回值:0表示成功、-1表示失败
int clearenv(void);
// 功能:清空环境变量表
错误处理
-
1、通过函数的返回值表示错误
- a、合法值表示成功,非法值表示失败
- 例如:计算大小、长度、查找下标
- b、返回值是指针类型
- 返回 NULL / 0xFFFFFFFF表示失败
- 其余都是成功
- c、返回0表示成功,返回-1表示失败,一般都是系统函数
- d、永远成功 如printf
- a、合法值表示成功,非法值表示失败
-
2、通过影响全局的错误编号变量 errno,声明在errno.h中
-
#include <string.h> char *strerror(int errnum); // 功能:返回错误编号对应的错误信息
-
注意:errno是一个全局变量,很容易被其他人修改,不能根据它的值判断是否产生错误
-
#include <stdio.h> void perror(const char *s); // 功能:打印系统执行错误信息 // s:自行添加的提示信息,一般放可能导致错误的函数名