当前课程知识点:基于Linux的C++ > 第十二讲 Linux系统编程基础 > 12.4 程序执行环境(三) > LinuxCPP1204
在进行系统编程的时候
你可能需要频繁地进行系统调用
系统调用就有可能导致问题
因为涉及到操作系统的核心概念
所以它产生的问题是非常非常复杂的
所有的这些系统调用
它产生的错误都应该被处理
因为它们大部分时候
都涉及到了资源的访问
系统调用本身
就是访问系统资源的主要手段嘛
就这个地方
所以我们必须处理系统调用时发生的错误
所以我们必须处理系统调用时发生的错误
在 C 的代码规范下
我们往往使用断言来处理系统调用错误
到了 C++ 程序里边
那么你使用断言还行不行呢
还是可以的
但是 C++ 又提供了更好的机制
你比如讲异常处理规范
那么在这种情况下
你使用异常处理机制
显然要比使用断言更高级一些
尤其涉及到类库的架构的时候
使用异常处理机制其实是更自然的选择
使用异常处理机制其实是更自然的选择
在系统调用的时候
有两个主要的地方
同学们是需要特别注意的
一个 系统调用失败
因为这个失败的原因是非常复杂的
刚才不是说了吗
那么在整个系统调用过程中
因为你在访问资源
这个资源能不能够被你访问
它可能产生的结果是非常非常复杂的
有很多种情况
都有可能导致这个资源访问失败的
比如讲资源不足
比如讲因权限不足而导致
你这个请求被堵塞
还有呢 调用参数是无效的
你比如讲内存基地址
无效的文件描述符
这些都可能导致我们系统调用失败
更不要说你的系统调用
还可能被外部事件所中断
还有不可预计的外部原因
说不好什么原因 反正不可抗力
出现了 这个系统调用就失败了
所以为了保持你的程序健壮性
显然你必须处理这些错误
在处理这个错误的过程中
你要时刻记得
因为在产生这个系统调用的时候
你可能分配了资源
那么这个错误发生的时候
你要确保这个资源能够被正确地释放
在 Linux 系统里边它使用整数来表示系统调用的错误
在 Linux 系统里边它使用整数来表示系统调用的错误
这些标准错误一般都是以大写的字母 “E” 开头的宏
这些标准错误一般都是以大写的字母 “E” 开头的宏
它都已经预定义好了
你就直接可以用
有一个宏叫 errno 它是错误号
专门用来表达最后一次发生的那个错误的错误码
专门用来表达最后一次发生的那个错误的错误码
你可以查询这个错误码
来知道它发生了几号错误
每次都看错误码
你显然不知道那个错误码是什么意思
所以要想让它可读性更好
可以调用一个函数叫 strerror()
显示那个错误码所对应的字符串的解释信息
显示那个错误码所对应的字符串的解释信息
strerror() 这个函数干的就是这个事情
它定义在 “string.h” 这个头文件里
注意这是 C 的 “string.h” 头文件
所以你要包含时候应该 cstring
#include
不要包含 “string.h”
因为在 C++ 库里边
还有一个 “string.h” 头文件
这个我们前面谈到过
在进行系统错误处理的时候
实际上是相当复杂的
我们看这个例子 我调用一个函数
当然这是个系统调用
chown() 将指定文件的拥有者
改为指定的用户和指定的组 对吧
change own 就这个意思
它的第一个参数就是那个文件的名字
包含路径的文件名字
第二个和第三个参数就是用户的 ID 和组 ID
如果你传 -1 就表示不变
这个系统调用可能产生的结果是非常复杂的
这个系统调用可能产生的结果是非常复杂的
错误有很多很多种
操作成功了那就算了
如果 -1 那显然操作就不成功了
所以我们就要检查它的 errno
errno 的错误 你比如说 EPERM
表示操作被否决、权限不足
EROFS 表示 PATH 这个路径位于一个只读文件系统里
EROFS 表示 PATH 这个路径位于一个只读文件系统里
你想改 没门
像 ENAMETOOLONG 文件名太长了
ENOENT 没有这个项 文件不存在
ENOTDIR path 的某个成分不是目录
ENOTDIR path 的某个成分不是目录
都有可能导致错误
还有 EACCES 某个成分不可访问
EFAULT 无效内存地址
ENOMEM 核心内存不足 等等
你看 就这一个 chown() 简单的系统调用
都可能产生这么多的错误
一个健壮的程序
这些错误理论上都应该处理
资源管理的时候 同学们要特别记住
在系统调用的过程中必须明确管理的资源类型
在系统调用的过程中必须明确管理的资源类型
第一 内存 第二 文件描述符
第三 文件指针 第四 临时文件
第五 同步对象 等等
我这里边才列了五个 实际上很多
你在进行 Linux 系统调用的时候
所有涉及到的资源访问
这些都必须明确
申请资源、分配资源、处理过程中
如果出现失败
出现失败的时候
那么就一定要能够把这个资源正确地释放
那么就一定要能够把这个资源正确地释放
资源管理本身
是一个非常非常重要的任务
一个健壮的程序必须能够保证这一点
当分配资源正常处理之后
发现流程失败 不管是什么原因
那么此前分配的资源都应该保证正确地释放
那么此前分配的资源都应该保证正确地释放
所以它有一个明确的资源管理流程
凡是是涉及到内存、文件描述符、
文件指针、临时文件、同步对象等等
这样的资源都必须遵照
这样的流程进行分配和管理
首先 你要分配 第二 你正常处理
第三 如果流程失败
那么程序就要释放资源并且退出
一切正常 释放资源、函数返回
一切正常 释放资源、函数返回
必须按照这个模式来工作
我们看这样一个函数 ReadFromFile()
我从文件里读数据
我会构造一片缓冲区
构造这个缓冲区失败 要返回
然后我打开这个文件
当然是以只读模式打开
O_RDONLY 去读它
如果打开失败 要销毁刚刚分配的 buffer
返回 然后读
如果读失败 要销毁 buffer 返回
最后关闭文件描述符 fd
这个是 ReadFromFile()
这个函数写的时候要做的事情
如果这些错误你都不处理
这程序代码那就很短了
但是它就不够健壮
因为它涉及到了复杂资源管理
第一 内存分配 那是资源
第二 打开文件 那是资源
第三 读取文件 还有可能失败
也要处理
实际上系统编程的时候
这些细节全都处理到
代码量实际上是非常非常大的
工作量也很大
-1.1 提纲
-1.2 程序设计的基本概念
-1.3 简单C/C++程序介绍
-1.4 程序设计的基本流程
-1.5 基本语法元素
-1.6 程序设计风格
-1.7 编程实践
-第一讲 C/C++基本语法元素--编程实践提交入口
-2.1 提纲
-2.2 结构化程序设计基础
-2.3 布尔数据
-2.4 分支结构
-2.5 break语句
-2.6 循环结构
-2.7 编程实践
-第二讲 程序控制结构--编程实践提交入口
-3.1 提纲
-3.2 函数声明、调用与定义
-3.3 函数调用栈框架
-3.4 编程实践
-第三讲 函数--编程实践提交入口
-4.1 提纲
-4.2 算法概念与特征
-4.3 算法描述
-4.4 算法设计与实现
-4.5 递归算法(一)
-4.6 递归算法(二)
-4.7 容错与计算复杂度
-4.8 编程实践
-第四讲 算法--编程实践提交入口
-5.1 提纲
-5.2 库与接口
-5.3 随机数库(一)
-5.4 随机数库(二)
-5.5 作用域与生存期
-5.6 典型软件开发流程(一)
-5.7 典型软件开发流程(二)
-5.8 编程实践
-第五讲 程序组织与开发方法--编程实践提交入口
-6.1 提纲
-6.2 字符
-6.3 数组(一)
-6.4 数组(二)
-6.5 结构体
-6.6 编程实践
-第六讲 复合数据类型--编程实践提交入口
-7.1 提纲
-7.2 指针基本概念
-7.3 指针与函数
-7.4 指针与复合数据类型(一)
-7.5 指针与复合数据类型(二)
-7.6 字符串
-7.7 动态存储管理(一)
-7.8 动态存储管理(二)
-7.9 引用
-7.10 编程实践
-第七讲 指针与引用--编程实践提交入口
-8.1 提纲
-8.2 数据抽象(一)
-8.3 数据抽象(二)
-8.4 链表(一)
-8.5 链表(二)
-8.6 链表(三)
-8.7 链表(四)
-8.8 函数指针(一)
-8.9 函数指针(二)
-8.10 抽象链表(一)
-8.11 抽象链表(二)
-8.12 编程实践
-第八讲 链表与程序抽象--编程实践提交入口
-9.1 提纲
-9.2 程序抽象与面向对象
-9.3 类类型
-9.4 对象(一)
-9.5 对象(二)
-9.6 类与对象的成员(一)
-9.7 类与对象的成员(二)
-9.8 类与对象的成员(三)
-9.9 继承(一)
-9.10 继承(二)
-9.11 继承(三)
-9.12 多态(一)
-9.13 多态(二)
-9.14 编程实践
-第九讲 类与对象--编程实践提交入口
-10.1 提纲
-10.2 四则运算符重载(一)
-10.3 四则运算符重载(二)
-10.4 关系与下标操作符重载
-10.5 赋值操作符重载(一)
-10.6 赋值操作符重载(二)
-10.7 赋值操作符重载(三)
-10.8 赋值操作符重载(四)
-10.9 赋值操作符重载(五)
-10.10 流操作符重载(一)
-10.11 流操作符重载(二)
-10.12 流操作符重载(三)
-10.13 操作符重载总结
-10.14 编程实践
-第十讲 操作符重载--编程实践提交入口
-11.1 提纲
-11.2 泛型编程概览
-11.3 异常处理机制(一)
-11.4 异常处理机制(二)
-11.5 运行期型式信息(一)
-11.6 运行期型式信息(二)
-11.7 模板与型式参数化
-11.8 题外话:术语翻译
-11.9 泛型编程实践(一)
-11.10 泛型编程实践(二)
-11.11 泛型编程实践(三)
-11.12 泛型编程实践(四)
-11.13 泛型编程实践(五)
-11.14 泛型编程实践(六)
-11.15 泛型编程实践(七)
-11.16 泛型编程实践(八)
-11.17 泛型编程实践(九)
-11.18 泛型编程实践(十)
-11.19 编程实践
-第十一讲 泛型编程--编程实践提交入口
-12.1 提纲
-12.2 程序执行环境(一)
-12.3 程序执行环境(二)
-12.4 程序执行环境(三)
-12.5 程序执行环境(四)
-12.6 输入输出(一)
-12.7 输入输出(二)
-12.8 文件系统
-12.9 设备
-12.10 库(一)
-12.11 库(二)
-12.12 makefile文件(一)
-12.13 makefile文件(二)
-12.14 makefile文件(三)
-12.15 编程实践
-第十二讲 Linux系统编程基础--编程实践提交入口
-13.01 提纲
-13.02 进程基本概念
-13.03 信号
-13.04 进程管理(一)
-13.05 进程管理(二)
-13.06 进程管理(三)
-13.07 进程间通信(一)
-13.08 进程间通信(二)
-13.09 进程间通信(三)
-13.10 进程间通信(四)
-13.11 进程池
-13.12 编程实践
-第十三讲 进程编程--编程实践提交入口
-14.1 提纲
-14.2 线程基本概念
-14.3 线程管理(一)
-14.4 线程管理(二)
-14.5 线程管理(三)
-14.6 线程管理(四)
-14.7 线程同步机制(一)
-14.8 线程同步机制(二)
-14.9 C++11线程库(一)
-14.10 C++11线程库(二)
-14.11 C++11线程库(三)
-14.12 C++11线程库(四)
-14.13 C++11线程库(五)
-14.14 编程实践
-第十四讲 线程编程--编程实践提交入口
-15.1 提纲
-15.2 Internet网络协议
-15.3 套接字(一)
-15.4 套接字(二)
-15.5 编程实践
-第十五讲 网络编程--编程实践提交入口