System programming

  • 系统编程
    • 错误处理
      • 检测错误
        • 函数的返回值
        • errno
          • 0: 没有错误
          • 非0:发生了某种类型的错误
      • 打印错误信息
        • strerror(errnum)
          • 作用:将整数解析成文本的错误信息
          • errnum
            • 0: Success
            • 非0: 具体的错误信息
        • perror(prefix)
          • 比较死板
        • fprintf
          • 比较繁琐
        • error
          • status
            • 0: 不退出,不执行exit()
            • 非0:打印错误信息后,执行 exit(status)
          • errnum
            • 0
            • 非0:在最后输出 strerror(errnum)
          • 格式串
            • 自定义输出格式
    • 如何查看man手册
      • 手册编号
        • 1:shell命令
        • 2:系统调用
        • 3:库函数
      • NAME
        • 一句话简单地介绍函数、命令的作用
      • SYNOPSIS
        • 头文件
        • 函数的声明
          • 指针类型的参数
            • 有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
                • 如果读到流的末尾,返回NULL,不设置errno
              • 失败:NULL,设置errno
          • 递归处理目录
            • a. 递归打印目录 (tree)
            • b. 递归复制目录 (cp -r dir1 dir2)
            • c. 递归删除目录 (rm -r dir)
      • 文件描述符
        • 内核管理文件的数据结构
          • 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
                    • ……
            • 返回值
              • 成功: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
                • fd
                • offset
                  • 一定是页的整数倍
              • 返回值
                • 成功:映射区的起始地址
                • 失败:MAP_FAILED,并设置 errno
            • munmap
              • 作用:解除映射
              • 参数
                • addr
                • length
              • 返回值
                • 成功:0
                • 失败:-1,并设置errno
    • 进程
      • 理论模型
        • 什么是进程
          • 目的:压榨计算机的资源
          • 概念
            • 用户:程序的动态执行过程
            • 内核:正在执行的任务,计算机的负载
              • 进程是计算机分配资源的最小单位
          • struct proc
            • 内存资源
            • 上下文
              • CPU的状态
            • 外部设备
              • ofile
            • 当前工作目录
            • 父进程
            • 状态
            • ….
          • 实现
            • 底层机制
              • 如何切换进程
                • 方案一
                  • 简单,快速
                  • 不安全
                  • 没有控制权
                • 方案二
                  • 引入了CPU的模态
                    • 用户态
                      • 不能访问非法内存空间
                      • 不能执行特权指令
                    • 内核态
                    • 问题:应用程序如何执行特权指令
                      • 系统调用
                      • 陷阱表
                  • 缺点:操作系统不能主动获取控制权
                • 方案三
                  • 引入时钟设备
                    • 每隔xms会触发一次时钟中断
            • 上层策略
              • 调度算法
          • 上下文切换的开销
            • 保存寄存器
            • 恢复寄存器
            • 高速缓存失效
            • TLB的失效
              • 页表的缓存
      • API
        • 基本操作 (原语)
          • 获取进程的标识
            • getpid
            • getppid
          • 创建进程
            • fork
              • 原理
                • 复制父进程的 PCB,并修改其中的某些信息
                • 复制父进程的页表
                  • 写时复制 (copy on write)
                • ……
              • 关键点
                • 父子进程都是从 fork() 返回点,开始执行的
                  • 一次调用,两次返回
                • 当fork()返回后,到底是父进程先执行,还是子进程先执行,这是不确定的。
              • 返回值
                • 成功
                  • 父进程:子进程的pid
                  • 子进程:0
                • 失败:-1,并设置 errno
              • 父子进程资源共享
                • 代码段
                  • 共享
                • 栈,堆,数据段
                  • 私有
                • 用户态缓冲区
                  • 私有
                    • 面试题
                • 文件描述符列表
                  • 私有
                • 打开文件
                  • 共享
          • 终止进程
            • 正常终止
              • 从 main 函数返回
                • exit()
              • exit()
                  1. 按和注册相反的顺序执行退出处理函数
                  • atexit
                    • 参数
                      • void (*function) (void)
                    • 返回值
                      • 成功:0
                      • 失败:非0
                    • 局限性
                      • 不能获取退出状态
                      • 没有参数
                  • on_exit
                    • 参数
                      • void (function) (int status, void arg)
                      • void* args
                    • 返回值
                      • 成功:0
                      • 失败:非0
                  1. 刷新用户态缓冲区
                  1. 调用 _exit()
              • _exit()
                • 参数
                  • status
                    • 只用了后8位,表示退出状态。0 表示执行成功,非0表示执行失败。
            • 异常终止
              • abort()
                • 给自己发送 SIGABRT 信号
              • 发送信号
          • 监控子进程的状态
            • wait
              • 作用:监控子进程是否终止;如果没有子进程终止,会一直阻塞。
              • 参数
                • int* status
                  • 传出参数,用来接收子进程的终止状态信息
                    • WIFEXITED
                      • 判断子进程是否正常终止
                    • WEXITSTATUS
                      • 获取子进程的退出状态
                    • WIFSIGNALED
                      • 判断子进程是否异常终止
                    • WTERMSIGNAL
                      • 获取导致子进程异常终止的信号
                    • WCOREDUMP
                      • 非标准
              • 返回值
                • 成功:终止子进程的pid
                • 失败:-1,并设置erno
              • 局限性
                  1. 如果没有子进程终止,会一直阻塞。
                  1. 不能指定监控哪一个子进程
                  1. 只能监控子进程是否终止
            • waitpid
              • 作用:监控子进程的状态 (终止、停止、继续)
              • 参数
                • pid
                  • 0: 监控指定的子进程

                  • =0:监控同进程组的子进程
                  • =-1:监控任意的子进程
                  • <-1: 监控进程组 ID 为 |pid| 的子进程
                • int* status
                  • 传出参数,用来接收子进程的终止状态信息
                    • WIFEXITED
                      • 判断子进程是否正常终止
                    • WEXITSTATUS
                      • 获取子进程的退出状态
                    • WIFSIGNALED
                      • 判断子进程是否异常终止
                    • WTERMSIGNAL
                      • 获取导致子进程异常终止的信号
                    • WCOREDUMP
                      • 非标准
                    • WIFSTOPPED
                      • 判断子进程是否停止
                    • WSTOPSIG
                      • 获取导致子进程停止的信号
                    • WIFCONTINUED
                      • 判断子进程是否继续执行
                • options
                  • WONHANG
                    • 不阻塞
                  • WUNTRACE
                    • 监控停止状态
                  • WCONTINUED
                    • 监控继续状态
          • 执行程序
            • execve
              • 原理
                • 清除进程的代码段、数据段、堆、栈…
                • 加载新的可执行程序,设置代码段和数据段
                • 从新的可执行程序main函数的第一行开始执行
              • 参数
                • pathname
                  • 可执行程序的路径
                • char* argv[]
                  • 命令行参数
                    • argv[0]: 可执行程序的名字
                    • 以NULL结尾
                • char* envp[]
                  • 环境变量
                    • 以 NULL 结尾
              • 返回值
                • 成功:不返回
                • 失败:-1,并设置 errno
            • exec函数簇
              • l: list
                • 命令行参数以可变长参数的形式指定
              • v: vector
                • 命令行参数以数组的形式指定
              • e: environment
                • 重新设置环境变量
              • p: PATH
                • 根据PATH环境变量查找可执行程序
            • system
              • 作用:执行任意的 shell 命令
              • 原理:fork()一个子进程,让子进程执行 “sh -c command”
              • 参数
                • command
                  • 要执行的命令
              • 惯用法
                • fork()
                • 子进程执行新的可执行程序
                • 父进程等待子进程结束
        • IPC
          • 管道
            • 概念:内核管理的数据结构,用于进程之间通信
            • 特性
              • 字节流
                • 数据是没有边界的
                • 接收的顺序就是发送的顺序
                • 不能够用 lseek() 移动位置
              • 半双工
              • 管道的容量是有限的
                • 如果管道已经满了,写管道会引发阻塞
              • 读管道
                • 如果管道里面没有数据,读管道会引发阻塞
            • pipe
              • 作用:创建管道
              • 参数
                • int fields[2]
                  • 传出参数:fields[0]会关联到管道的读端;fields[1]会关联到管道的写端
              • 返回值
                • 成功: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
              • 硬件
                • 执行了非法的指令
                  • 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
                        • 自定义处理函数
                  • 返回值
                    • 成功:旧的信号处理函数
                    • 失败:SIG_ERR
              • 发送信号
                • kill
                  • 参数
                    • pid
                      • 0: 给进程ID为pid的进程发送信号

                      • =0: 给同进程组的进程发送信号
                      • =-1:给权限允许的所有进程发送信号
                      • <-1: 给进程组 id 为 |pid| 的进程发送信号
                    • signo
                  • 返回值
                    • 成功 (至少发送了一次信号):0
                    • 失败:-1,并设置 errno
                • raise
                  • kill(getpid(), signo)
          • 套接字 (socket)
          • 共享内存 + 信号量 (semaphor)
          • 消息队列
    • I/O多路复用
      • 最佳实践:一个执行流程最多只能有一个阻塞点
      • 作用:同步的I/O事件监听机制
      • API
        • select
          • 参数
            • nfds
              • 监听的最大文件描述符 + 1
            • readfds
              • 传入:要监听读事件的文件描述符集合
              • 传出:读事件已就绪的文件描述符集合
            • writefds
              • 传入:要监听写事件的文件描述符集合
              • 传出:写事件已就绪的文件描述符集合
            • exceptfds
              • 传入:要监听看异常事件的文件描述符集合
              • 传出:异常事件已就绪的文件描述符集合
            • timeout
              • 传入:超时时间
              • 传出:剩余的时间
              • NULL: 一直阻塞
              • {0, 0}:不阻塞
          • 返回值
            • 成功
              • 返回就绪的 I/O 事件个数;如果超时,返回0
            • 失败
              • -1,并设置errno
          • 缺陷
              1. 监听的文件描述符个数是受限的
              1. 轮询fd_set,已判断哪些文件描述符就绪
        • poll
        • epoll
    • 线程
      • 什么是线程
        • 进程的一条执行流程,它是CPU调度的最小单位
      • 为什么引入线程
        • 相对进程而言,线程是轻量级的
        • 进程之间通信需要打破隔离的壁障,线程之间通信的代价几乎没有
        • 线程之间切换,不会导致 CPU 高速缓存和 TLB 的失效
      • API
        • 基本操作 (控制原语)
          • 获取线程的标识
            • pthread_self
          • 创建线程
            • pthread_create
              • 参数
                • tid: 传出参数,用来接收新线程的 ID
                • attr: 线程的属性,一般填NULL,表示采用默认属性
                • void* (start_routine) (void): 线程的入口函数
                • arg: 线程入口函数的实参
              • 返回值
                • 成功:0
                • 失败:返回错误码,不设置errno
          • 终止线程
            • 进程终止,会导致该进程的所有线程终止
            • 从线程入口函数返回
            • 调用 pthread_exit()
            • 响应 pthread_cancel()
          • 退出线程
            • pthread_exit
              • 参数
                • void* retval
          • 连接线程
            • pthread_join
              • 参数
                • tid
                • void** retval
                  • 传出参数,用来接收线程的返回值
                    • 如果线程是因为响应取消请求而终止,线程会返回 PTHREAD_CANCELLED
              • 返回值
                • 成功:0
                • 失败:返回错误码,不设置errno
          • 分离线程
            • pthread_detach
              • 参数
                • tid
              • 处于游离态的进程不可以被 join, 也不能重新恢复到 joinable 状态
          • 发送取消请求
            • pthread_cancel
              • 参数
                • tid: 目标线程
              • 目标线程是否响应取消请求,以及何时响应
                • 取消状态
                  • ENABLE (默认)
                  • DISABLE
                • 取消类型
                  • DEFFERED (默认)
                    • 取消点
                  • ASYNCHROUNS
                    • 任意时候
          • 清理线程
            • 线程的清理函数栈
              • pthead_cleanup_push
                • 参数
                  • void (routine) (void)
                  • void* arg
              • pthread_cleanup_pop
                • 参数
                  • execute
                    • 0: 不执行
                    • 非0:执行
            • 执行时机
              • 响应取消请求
              • 调用 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)
                        1. 释放锁
                        1. 陷入阻塞状态
                        1. 当pthread_cond_wait返回,当前线程一定再一次获取了锁
                      • 注意事项:返回时,条件不一定成立!存在虚假唤醒现象。
                    • pthread_cond_timedwait(&cond, &mutex, abstime)
                  • 唤醒
                    • pthread_cond_signal(&cond)
                    • pthread_cond_broadcast(&cond)
                  • 销毁
                    • pthread_cond_destroy
        • 生产者消费者模型
          • 生产者
            • 队列不满
              • 生产商品,入队列;队列不空,唤醒消费者
            • 队列满了
              • 等待
          • 消费者
            • 队列不空
              • 消费商品,出队列。队列不满,唤醒生产者
            • 队列空了
              • 等待
          • 阻塞队列