系统编程:C、进程与并发
集中回顾核心概念、公式和适用条件。
先看学习路线
按箭头顺序学习。点击一个阶段进入第一节;完整知识框架列出了每一个主题。
01 / 表示方式是一份约定
无符号算术按其取值范围进行模运算;有符号溢出是未定义行为。sizeof 以 C 字节为单位返回大小,结果类型是 size_t。自动对象要先初始化再读取。格式化输入输出的转换说明必须与实参的实际类型匹配。
02 / 指针需要有效的生命周期与边界
数组指针运算必须留在同一数组内,或恰好指向末尾之后一个位置;不能解引用这个末尾之后的指针。多维数组通常转换为指向一行的指针,而不是指向指针的指针。
03 / 文本需要足够容量和结束标记
至少预留 strlen+1 个字节。有长度限制的读取也可能只读到一行的一部分。检查转换结果、剩余后缀和取值范围。不能把任意字节序列直接当作有终止符的 C 字符串。
04 / 每次分配都要明确归属
乘法之前先检查 n≤SIZE_MAX/sizeof(*p)。检查分配结果,先初始化再读取。realloc 失败时要保留原指针。所有借用都结束之后,由明确的负责人最终调用一次 free。
05 / 构建程序的各阶段职责不同
预处理展开记号;编译检查一个翻译单元;链接解析外部定义。头文件描述接口。宏可能重复执行带副作用的表达式。头文件保护宏不能阻止不同文件之间出现多个外部定义。
06 / 信号处理函数应尽量简单
优先使用 sigaction 和异步信号安全的操作。一个简单的停止标志可以把清理工作推迟到正常控制流程。检查条件并等待信号时,要用原子地切换屏蔽字并等待的协议。
07 / 进程的生命周期
fork 复制执行状态,普通内存随后各自独立;exec 替换程序映像;waitpid 收集状态。检查每次调用的结果,关闭不用的继承文件描述符,并用配套的宏解释等待状态。
08 / 字节流不等于消息
管道和流式套接字的读取可能拆开一次写入,也可能合并多次写入。定义消息分帧规则,并循环处理部分传输。只有缓冲数据已读完且所有写端都已关闭,管道读取才会遇到 EOF。
09 / 共享存储仍然需要协议
共享内存减少了一部分复制,并没有取消同步要求。不同映射之间用偏移量表示引用。信号量的许可数量跟踪资源;互斥锁保护不变式。明确谁负责初始化、关闭和故障恢复。
10 / 线程需要明确协调
参数必须在线程使用期间一直有效,并正确等待已创建的线程结束。所有冲突访问都要受到保护。条件变量等待要放在检查条件的循环中,并配合对应的互斥锁。volatile 不是通用同步机制。
11 / 防止循环等待
按照稳定的全局顺序获取锁。分析依赖关系时,也要考虑线程等待、条件等待和输入输出。反复调用 trylock 虽然可能不阻塞,却仍可能活锁。仅凭超时不能判断已经死锁。
12 / 比较完成同等工作的耗时
加速比=T₁/Tp。比较的是得到同样且经过验证的结果所需的实际经过时间。划分输出区域的写入归属,依赖完成后再合并结果。考虑串行工作、带宽、负载不均和同步成本。