- 系统编程
- 错误处理
- 检测错误
- 函数的返回值
 - errno
- 0: 没有错误
 - 非0:发生了某种类型的错误
 
 
 - 打印错误信息
- strerror(errnum)
- 作用:将整数解析成文本的错误信息
 - errnum
- 0: Success
 - 非0: 具体的错误信息
 
 
 - perror(prefix)
- 比较死板
 
 - fprintf
- 比较繁琐
 
 - error
- status
- 0: 不退出,不执行exit()
 - 非0:打印错误信息后,执行 exit(status)
 
 - errnum
- 0
 - 非0:在最后输出 strerror(errnum)
 
 - 格式串
- 自定义输出格式
 
 
 - status
 
 - strerror(errnum)
 
 - 检测错误
 - 如何查看man手册
- 手册编号
- 1:shell命令
 - 2:系统调用
 - 3:库函数
 
 - NAME
- 一句话简单地介绍函数、命令的作用
 
 - SYNOPSIS
- 头文件
 - 函数的声明
- 指针类型的参数
- 有const
- 传入参数
- 函数不会修改指针指向的内容
 
 
 - 传入参数
 - 无const
- 传入传出参数
- 函数会修改指针指向的内容
 
 
 - 传入传出参数
 
 - 有const
 - 指针类型的返回值
- 栈
- 自动存储期限
 
 - 静态区
- 静态存储期限
 
 - 堆
- 动态存储期限
- 调用者负责free
 
 
 - 动态存储期限
 
 - 栈
 
 - 指针类型的参数
 
 - RETURN VALUE
- 成功
 - 失败
 
 - DESCRIPTION
- 带着问题去看
 - 看地懂得就看,看不懂得就不看
 
 
 - 手册编号
 - 文件
- 目录
- getcwd
- 作用:获取当前工作目录的绝对路径
 - 参数
- buf
 - size
 
 - 返回值
- 成功:当前工作目录
 - 失败:NULL,并设置errno
 
 - 特例
 
 - chdir
- 作用:修改当前工作目录
- 当前工作目录是进程的属性!
 
 - 参数
- path
 
 - 返回值
- 成功:0
 - 失败:-1,并设置errno
 
 
 - 作用:修改当前工作目录
 - mkdir
- 作用:创建目录
 - 参数
- path
 - mode
- 目录的实际权限:mode & ~umask
 
 
 - 返回值
- 成功:0
 - 失败:-1,并设置errno
 
 
 - rmdir
- 作用:删除空目录
 - 参数
- path
 
 - 返回值
- 成功:0
 - 失败:-1,并设置errno
 
 
 - 目录流
- opendir
- 作用:打开目录流
 - 参数
- path
 
 - 返回值
- 成功:返回指向目录流的指针 (DIR*)
 - 失败:NULL,并设置errno
 
 
 - closedir
- 作用:关闭目录流
 - 参数
- DIR* stream
 
 - 返回值
- 成功:0
 - 失败:-1,并设置errno
 
 
 - readdir
- 作用:读取下一个目录项
 - 参数
- DIR* stream
 
 - 返回值
- 成功
- 返回指向下一个目录项的指针
- struct dirent
- d_name
 - d_ino
 - d_type
- DT_REG
 - DT_DIR
 - DT_BLK
 - DT_CHR
 - DT_LNK
 - DT_SOCK
 - DT_FIFO
 - DT_UNKNOWN
 
 
 
 - struct dirent
 - 如果读到流的末尾,返回NULL,不设置errno
 
 - 返回指向下一个目录项的指针
 - 失败:NULL,设置errno
 
 - 成功
 
 - 递归处理目录
- a. 递归打印目录 (tree)
 - b. 递归复制目录 (cp -r dir1 dir2)
 - c. 递归删除目录 (rm -r dir)
 
 
 - opendir
 
 - getcwd
 - 文件描述符
- 内核管理文件的数据结构
- fd
 - 文件描述符表
 - 打开文件
- flags
 - 文件的位置
 - vnode的指针
 - 引用计数
 
 - vnode
- inode的副本
 - 设备号
 
 - inode
 
 - 为什么这么设计
- 安全
 - 简单方便
 - 提供了统一的操作方式
- 通用性
 
 
 - 操作
- open
- 作用:打开文件描述符
 - 参数
- path
 - flags
- O_RDONLY
 - O_WRONLY
 - O_RDWR
 - O_CREAT
 - O_EXCL
 - O_TRUNC
 - O_APPEND
 
 - mode
- 如果flags里设置了 O_CREAT, 就需要传入文件的创建权限
 
 
 - 返回值
- 成功:文件描述符 (最小可用的文件描述符)
 - 失败:-1, 并设置errno
 
 
 - close
- 作用:关闭文件描述符
 - 参数
- fd
 
 - 返回值
- 成功:0
 - 失败:-1,并设置errno
 
 
 - read
- 作用:读文件描述符
 - 参数
- fd
 - buf
 - size
 
 - 返回值
- 成功:返回实际读取的字节数目。如果返回值为 0,表示读到了文件的末尾。
 - 失败:-1,并设置errno
 
 
 - write
- 作用:写文件描述符
 - 参数
- fd
 - buf
 - size
 
 - 返回值
- 成功:返回实际写入的字节数目
 - 失败:-1,并设置errno
 
 
 - lseek
- 作用:移动文件位置
 - 参数
- fd
 - offset
 - whence
- SEEK_SET
 - SEEK_CUR
 - SEEK_END
 
 
 - 返回值
- 成功:移动后文件的位置
 - 失败:-1,并设置errno
 
 
 - fsync
- 作用:将数据写回到磁盘
 - 参数
- fd
 
 - 返回值
- 成功:0
 - 失败:-1,并设置errno
 
 
 - ftruncate
- 作用:截断文件
 - 参数
- fd
 - length
- length < 原文件大小,截断的数据会丢失
 - length > 原文件大小,扩充的部分会填入空字符 (‘\0’)
 
 
 - 返回值
- 成功:0
 - 失败:-1,并设置errno
 
 
 - fstat
- 作用:查看文件的元数据
 - 参数
- fd
 - struct stat*  statbuf
- 传出参数,用来存储文件的元数据
- struct stat
- st_ino
 - st_size
 - st_mode
 - st_blocks
 - st_nlinks
 - ……
 
 
 - struct stat
 
 - 传出参数,用来存储文件的元数据
 
 - 返回值
- 成功:0
 - 失败:-1,并设置errno
 
 
 - dup
- 作用:复制文件描述符
 - 参数
- oldfd
 
 - 返回值
- 成功:新的文件描述符 (最小可用的文件描述符)
 - 失败:-1,并设置 errno
 
 - 缺陷
- 不能指定 newfd
 - close(); dup() 之间有一个时间窗口,可能会引发竞态条件,导致并发问题。
 
 
 - dup2
- 作用:复制文件描述符
 - 参数
- oldfd
 - newfd
 
 - 返回值
- 成功:新的文件描述符
 - 失败:-1,并设置 errno
 
 
 - 内存映射 I/O
- mmap
- 作用:将文件的内容直接映射到进程的虚拟内存空间
 - 参数
- addr
- 一般填 NULL,由内核指定起始位置
 
 - length
 - prot
- PROT_NONE
 - PROT_READ
 - PROT_WRITE
 - PROT_EXEC
 
 - flags
- MAP_PRIVATE
- 特点
- 写时复制
 - 对映射区的修改,不会写回到底层文件
 
 - 作用:用文件的内容初始化内存空间
 
 - 特点
 - MAP_SHARED
- 特点
- 对映射区的修改,会写回到磁盘
 - 进程共享
 
 - 作用
- 内存映射 I/O (零拷贝), 一般适用于大文件
 - IPC
 
 
 - 特点
 
 - MAP_PRIVATE
 - fd
 - offset
- 一定是页的整数倍
 
 
 - addr
 - 返回值
- 成功:映射区的起始地址
 - 失败:MAP_FAILED,并设置 errno
 
 
 - munmap
- 作用:解除映射
 - 参数
- addr
 - length
 
 - 返回值
- 成功:0
 - 失败:-1,并设置errno
 
 
 
 - mmap
 
 - open
 
 - 内核管理文件的数据结构
 
 - 目录
 - 进程
- 理论模型
- 什么是进程
- 目的:压榨计算机的资源
 - 概念
- 用户:程序的动态执行过程
 - 内核:正在执行的任务,计算机的负载
- 进程是计算机分配资源的最小单位
 
 
 - struct proc
- 内存资源
 - 上下文
- CPU的状态
 
 - 外部设备
- ofile
 
 - 当前工作目录
 - 父进程
 - 状态
 - ….
 
 - 实现
- 底层机制
- 如何切换进程
- 方案一
- 简单,快速
 - 不安全
 - 没有控制权
 
 - 方案二
- 引入了CPU的模态
- 用户态
- 不能访问非法内存空间
 - 不能执行特权指令
 
 - 内核态
 - 问题:应用程序如何执行特权指令
- 系统调用
 - 陷阱表
 
 
 - 用户态
 - 缺点:操作系统不能主动获取控制权
 
 - 引入了CPU的模态
 - 方案三
- 引入时钟设备
- 每隔xms会触发一次时钟中断
 
 
 - 引入时钟设备
 
 - 方案一
 
 - 如何切换进程
 - 上层策略
- 调度算法
 
 
 - 底层机制
 - 上下文切换的开销
- 保存寄存器
 - 恢复寄存器
 - 高速缓存失效
 - TLB的失效
- 页表的缓存
 
 
 
 
 - 什么是进程
 - API
- 基本操作 (原语)
- 获取进程的标识
- getpid
 - getppid
 
 - 创建进程
- fork
- 原理
- 复制父进程的 PCB,并修改其中的某些信息
 - 复制父进程的页表
- 写时复制 (copy on write)
 
 - ……
 
 - 关键点
- 父子进程都是从 fork() 返回点,开始执行的
- 一次调用,两次返回
 
 - 当fork()返回后,到底是父进程先执行,还是子进程先执行,这是不确定的。
 
 - 父子进程都是从 fork() 返回点,开始执行的
 - 返回值
- 成功
- 父进程:子进程的pid
 - 子进程:0
 
 - 失败:-1,并设置 errno
 
 - 成功
 - 父子进程资源共享
- 代码段
- 共享
 
 - 栈,堆,数据段
- 私有
 
 - 用户态缓冲区
- 私有
- 面试题
 
 
 - 私有
 - 文件描述符列表
- 私有
 
 - 打开文件
- 共享
 
 
 - 代码段
 
 - 原理
 
 - fork
 - 终止进程
- 正常终止
- 从 main 函数返回
- exit()
 
 - exit()
- 按和注册相反的顺序执行退出处理函数
 
- atexit
- 参数
- void (*function) (void)
 
 - 返回值
- 成功:0
 - 失败:非0
 
 - 局限性
- 不能获取退出状态
 - 没有参数
 
 
 - 参数
 - on_exit
- 参数
- void (function) (int status, void arg)
 - void* args
 
 - 返回值
- 成功:0
 - 失败:非0
 
 
 - 参数
 
- 刷新用户态缓冲区
 
- 调用 _exit()
 
 - _exit()
- 参数
- status
- 只用了后8位,表示退出状态。0 表示执行成功,非0表示执行失败。
 
 
 - status
 
 - 参数
 
 - 从 main 函数返回
 - 异常终止
- abort()
- 给自己发送 SIGABRT 信号
 
 - 发送信号
 
 - abort()
 
 - 正常终止
 - 监控子进程的状态
- wait
- 作用:监控子进程是否终止;如果没有子进程终止,会一直阻塞。
 - 参数
- int* status
- 传出参数,用来接收子进程的终止状态信息
- WIFEXITED
- 判断子进程是否正常终止
 
 - WEXITSTATUS
- 获取子进程的退出状态
 
 - WIFSIGNALED
- 判断子进程是否异常终止
 
 - WTERMSIGNAL
- 获取导致子进程异常终止的信号
 
 - WCOREDUMP
- 非标准
 
 
 - WIFEXITED
 
 - 传出参数,用来接收子进程的终止状态信息
 
 - int* status
 - 返回值
- 成功:终止子进程的pid
 - 失败:-1,并设置erno
 
 - 局限性
- 如果没有子进程终止,会一直阻塞。
 
- 不能指定监控哪一个子进程
 
- 只能监控子进程是否终止
 
 
 - waitpid
- 作用:监控子进程的状态 (终止、停止、继续)
 - 参数
- pid
0: 监控指定的子进程
- =0:监控同进程组的子进程
 - =-1:监控任意的子进程
 - <-1: 监控进程组 ID 为 |pid| 的子进程
 
 - int* status
- 传出参数,用来接收子进程的终止状态信息
- WIFEXITED
- 判断子进程是否正常终止
 
 - WEXITSTATUS
- 获取子进程的退出状态
 
 - WIFSIGNALED
- 判断子进程是否异常终止
 
 - WTERMSIGNAL
- 获取导致子进程异常终止的信号
 
 - WCOREDUMP
- 非标准
 
 - WIFSTOPPED
- 判断子进程是否停止
 
 - WSTOPSIG
- 获取导致子进程停止的信号
 
 - WIFCONTINUED
- 判断子进程是否继续执行
 
 
 - WIFEXITED
 
 - 传出参数,用来接收子进程的终止状态信息
 - options
- WONHANG
- 不阻塞
 
 - WUNTRACE
- 监控停止状态
 
 - WCONTINUED
- 监控继续状态
 
 
 - WONHANG
 
 - pid
 
 
 - wait
 - 执行程序
- execve
- 原理
- 清除进程的代码段、数据段、堆、栈…
 - 加载新的可执行程序,设置代码段和数据段
 - 从新的可执行程序main函数的第一行开始执行
 
 - 参数
- pathname
- 可执行程序的路径
 
 - char* argv[]
- 命令行参数
- argv[0]: 可执行程序的名字
 - 以NULL结尾
 
 
 - 命令行参数
 - char* envp[]
- 环境变量
- 以 NULL 结尾
 
 
 - 环境变量
 
 - pathname
 - 返回值
- 成功:不返回
 - 失败:-1,并设置 errno
 
 
 - 原理
 - exec函数簇
- l: list
- 命令行参数以可变长参数的形式指定
 
 - v: vector
- 命令行参数以数组的形式指定
 
 - e: environment
- 重新设置环境变量
 
 - p: PATH
- 根据PATH环境变量查找可执行程序
 
 
 - l: list
 - system
- 作用:执行任意的 shell 命令
 - 原理:fork()一个子进程,让子进程执行 “sh -c command”
 - 参数
- command
- 要执行的命令
 
 
 - command
 - 惯用法
- fork()
 - 子进程执行新的可执行程序
 - 父进程等待子进程结束
 
 
 
 - execve
 
 - 获取进程的标识
 - IPC
- 管道
- 概念:内核管理的数据结构,用于进程之间通信
 - 特性
- 字节流
- 数据是没有边界的
 - 接收的顺序就是发送的顺序
 - 不能够用 lseek() 移动位置
 
 - 半双工
 - 管道的容量是有限的
- 如果管道已经满了,写管道会引发阻塞
 
 - 读管道
- 如果管道里面没有数据,读管道会引发阻塞
 
 
 - 字节流
 - pipe
- 作用:创建管道
 - 参数
- int fields[2]
- 传出参数:fields[0]会关联到管道的读端;fields[1]会关联到管道的写端
 
 
 - int fields[2]
 - 返回值
- 成功:0
 - 失败:-1,并设置errno
 
 - 惯用法
- 先 pipe
 - 再 fork
 - 父进程关闭管道的一端;子进程关闭管道的另一端
 
 - 限制
- 只能用于相关进程之间通信,往往是父子进程
 
 
 - fifo
- 作用:创建有名管道 (fifo)
 - 命令:mkfifo [-m mode] 名字
 - mkfifo
- 参数
- pathname
 - mode
 
 - 返回值
- 成功:0
 - 失败:-1,并设置errno
 
 
 - 参数
 - 用于任意两个进程之间通信
 
 
 - 信号 (signal)
- 异步的事件通知机制。是进程感知外部世界的一种很重要的方式。
 - 特征
- 异步
- 进程不知道什么时候会收到信号,但一旦收到信号,会立刻响应信号。
 
 - 不稳定
- 处于 pending 状态的信号,可能发生丢失
 
 - 语义不明确
- 不同操作系统信号的语义不尽相同
 
 
 - 异步
 - 事件源
- 用户
- ctrl + c
- SIGINT
 
 - ctrl + z
- SIGTSTP
 
 - ctrl + \
- SIGQUIT
 
 
 - ctrl + c
 - 硬件
- 执行了非法的指令
- SIGILL
 
 - 访问非法的内容
- SIGSEGV
 
 - 除0异常
- SIGFPE
 
 
 - 执行了非法的指令
 - 软件
- 子进程的状态发生修改
- SIGCHLD
 
 - 写破损的管道
- SIGPIPE
 
 - 调用 abort()
- SIGABRT
 
 
 - 子进程的状态发生修改
 
 - 用户
 - 信号的执行流程
 - 响应信号
- 默认处置
- Term
 - Core
 - Stop
 - Cont
 - Ign
 
 - 自行处置
- 注册信号处理函数
 
 
 - 默认处置
 - API
- 注册信号处理函数
- signal
- 参数
- signo
 - handler
- typedef void (*sighandler_t) (int)
- SIG_DFL
 - SIG_IGN
 - 自定义处理函数
 
 
 - typedef void (*sighandler_t) (int)
 
 - 返回值
- 成功:旧的信号处理函数
 - 失败:SIG_ERR
 
 
 - 参数
 
 - signal
 - 发送信号
- kill
- 参数
- pid
0: 给进程ID为pid的进程发送信号
- =0: 给同进程组的进程发送信号
 - =-1:给权限允许的所有进程发送信号
 - <-1: 给进程组 id 为 |pid| 的进程发送信号
 
 - signo
 
 - pid
 - 返回值
- 成功 (至少发送了一次信号):0
 - 失败:-1,并设置 errno
 
 
 - 参数
 - raise
- kill(getpid(), signo)
 
 
 - kill
 
 - 注册信号处理函数
 
 - 套接字 (socket)
 - 共享内存 + 信号量 (semaphor)
 - 消息队列
 
 - 管道
 
 - 基本操作 (原语)
 
 - 理论模型
 - I/O多路复用
- 最佳实践:一个执行流程最多只能有一个阻塞点
 - 作用:同步的I/O事件监听机制
 - API
- select
- 参数
- nfds
- 监听的最大文件描述符 + 1
 
 - readfds
- 传入:要监听读事件的文件描述符集合
 - 传出:读事件已就绪的文件描述符集合
 
 - writefds
- 传入:要监听写事件的文件描述符集合
 - 传出:写事件已就绪的文件描述符集合
 
 - exceptfds
- 传入:要监听看异常事件的文件描述符集合
 - 传出:异常事件已就绪的文件描述符集合
 
 - timeout
- 传入:超时时间
 - 传出:剩余的时间
 - NULL: 一直阻塞
 - {0, 0}:不阻塞
 
 
 - nfds
 - 返回值
- 成功
- 返回就绪的 I/O 事件个数;如果超时,返回0
 
 - 失败
- -1,并设置errno
 
 
 - 成功
 - 缺陷
- 监听的文件描述符个数是受限的
 
- 轮询fd_set,已判断哪些文件描述符就绪
 
 
 - 参数
 - poll
 - epoll
 
 - select
 
 - 线程
- 什么是线程
- 进程的一条执行流程,它是CPU调度的最小单位
 
 - 为什么引入线程
- 相对进程而言,线程是轻量级的
 - 进程之间通信需要打破隔离的壁障,线程之间通信的代价几乎没有
 - 线程之间切换,不会导致 CPU 高速缓存和 TLB 的失效
 
 - API
- 基本操作 (控制原语)
- 获取线程的标识
- pthread_self
 
 - 创建线程
- pthread_create
- 参数
- tid: 传出参数,用来接收新线程的 ID
 - attr: 线程的属性,一般填NULL,表示采用默认属性
 - void* (start_routine) (void): 线程的入口函数
 - arg: 线程入口函数的实参
 
 - 返回值
- 成功:0
 - 失败:返回错误码,不设置errno
 
 
 - 参数
 
 - pthread_create
 - 终止线程
- 进程终止,会导致该进程的所有线程终止
 - 从线程入口函数返回
 - 调用 pthread_exit()
 - 响应 pthread_cancel()
 
 - 退出线程
- pthread_exit
- 参数
- void* retval
 
 
 - 参数
 
 - pthread_exit
 - 连接线程
- pthread_join
- 参数
- tid
 - void** retval
- 传出参数,用来接收线程的返回值
- 如果线程是因为响应取消请求而终止,线程会返回 PTHREAD_CANCELLED
 
 
 - 传出参数,用来接收线程的返回值
 
 - 返回值
- 成功:0
 - 失败:返回错误码,不设置errno
 
 
 - 参数
 
 - pthread_join
 - 分离线程
- pthread_detach
- 参数
- tid
 
 - 处于游离态的进程不可以被 join, 也不能重新恢复到 joinable 状态
 
 - 参数
 
 - pthread_detach
 - 发送取消请求
- pthread_cancel
- 参数
- tid: 目标线程
 
 - 目标线程是否响应取消请求,以及何时响应
- 取消状态
- ENABLE (默认)
 - DISABLE
 
 - 取消类型
- DEFFERED (默认)
- 取消点
 
 - ASYNCHROUNS
- 任意时候
 
 
 - DEFFERED (默认)
 
 - 取消状态
 
 - 参数
 
 - pthread_cancel
 - 清理线程
- 线程的清理函数栈
- pthead_cleanup_push
- 参数
- void (routine) (void)
 - void* arg
 
 
 - 参数
 - pthread_cleanup_pop
- 参数
- execute
- 0: 不执行
 - 非0:执行
 
 
 - execute
 
 - 参数
 
 - pthead_cleanup_push
 - 执行时机
- 响应取消请求
 - 调用 pthread_exit()
 - 调用 pthread_cleanup_pop(非0)
 - 注意:从线程入口函数返回,不执行!
 
 
 - 线程的清理函数栈
 
 - 获取线程的标识
 - 同步
- 竞态条件
- 程序的运行结果 (状态), 取决于程序的调度情况
 - 原因
- 多线程
 - 共享资源 (数据、文件…)
 - 至少有一个线程会修改共享资源
 
 
 - 并发问题
- 竞态条件
 
 - 同步:避免坏的调度的发生
- 互斥地访问资源
- 锁机制
- 互斥锁
- 用法
- 上锁
 - 执行临界区的代码 (对共享资源的访问)
 - 释放锁
 
 - API
- 初始化
- 静态初始化
- PTHREAD_MUTEX_INITIALIZER
 
 - 动态初始化
- pthread_mutex_init
 
 
 - 静态初始化
 - 上锁
- pthread_mutex_lock
 - pthread_mutex_trylock
 - pthread_mutex_timedlock
 
 - 释放锁
- pthread_mutex_unlock
 
 - 销毁
- pthread_mutex_destroy
 
 
 - 初始化
 
 - 用法
 - 自旋锁
 - 读写锁
 - …
 
 - 互斥锁
 - CAS (无锁编程)
 
 - 锁机制
 - 等待某个条件成立
- 条件变量 (提供了等待唤醒机制)
- 条件不成立: 等待
 - 条件成立:被唤醒 (执行)
 - API
- 初始化
- 静态初始化
- PTHREAD_COND_INITIALIZER
 
 - 动态初始化
- pthread_cond_init
 
 
 - 静态初始化
 - 等待
- pthread_cond_wait(&cond, &mutex)
- 释放锁
 
- 陷入阻塞状态
 
- 当pthread_cond_wait返回,当前线程一定再一次获取了锁
 
- 注意事项:返回时,条件不一定成立!存在虚假唤醒现象。
 
 - pthread_cond_timedwait(&cond, &mutex, abstime)
 
 - pthread_cond_wait(&cond, &mutex)
 - 唤醒
- pthread_cond_signal(&cond)
 - pthread_cond_broadcast(&cond)
 
 - 销毁
- pthread_cond_destroy
 
 
 - 初始化
 
 
 - 条件变量 (提供了等待唤醒机制)
 
 - 互斥地访问资源
 
 - 竞态条件
 - 生产者消费者模型
- 生产者
- 队列不满
- 生产商品,入队列;队列不空,唤醒消费者
 
 - 队列满了
- 等待
 
 
 - 队列不满
 - 消费者
- 队列不空
- 消费商品,出队列。队列不满,唤醒生产者
 
 - 队列空了
- 等待
 
 
 - 队列不空
 - 阻塞队列
 
 - 生产者
 
 - 基本操作 (控制原语)
 
 - 什么是线程
 
 - 错误处理