APUE重读笔记

Posted by jabin on June 28, 2020

一、结构划分

  1. 概述: 第一、第二章
  2. I/O: 第三、第四、第五章
  3. 进程、线程:第七、八、九、十章–进程; 第十一、十二章–线程
  4. 更多I/O:第十三(守护进程)、十四、十八章
  5. IPC:第十五、十六、十七
  6. 实例: 十九(伪终端)、二十(数据库函数库)、二十一(网络打印机通信)

二、概述

  1. unix提供的服务
  2. UNIX的体系结构图 内核->系统调用->shell/公共函数库/应用

三、Chapter one

  1. Kernel->system call -> shell、lib、app unix体系结构图
  2. 文件包含目录和文件, type can be file or diretory
  3. The instance of App consider as process
  4. Fork: 返回两次, 给父进程返回子进程id, 给子进程返回0; 子进程需要用exit退出, 父进程通过waitpid等待子进程终止。Shell1.c
  5. 线程共享一个进程内的资源, 包括地址空间、进程属性等,所以共享数据需要考虑并发问题(同步化)
  6. 出错恢复->程序的健壮性。 重试、错误处理等
  7. 信号。 A进程可以通过向有权限的B进程, 发送信号,来控制B进程; 信号默认动作,也可以自定义信号处理函数
  8. 时间。日历时间和进程时间。 进程时间(real time(包含其他进程的运行时间), Cpu time(user, sys))
  9. 系统调用和库函数。 库函数通常是对系统调用的封装。 如库函数malloc。 内核、用户
  10. 若不希望修改函数的参数值。 若参数为指针,则考虑用const修饰, 否则因为C是值传递, 所以不加const修饰,也不会修改原值

四、Chapter tree

  1. Close函数:close会释放文件的所有记录锁; 进程终止时, 内核会自动close该进程打开的的文件
  2. Lseek: 设置文件打开、写入的锚点
  3. 文件操作函数组: open, lseek, write, read, close
  4. I/0效率:缓冲区的大小设置与bocksize, 一般设置为2的N次方
  5. 文件共享: proc table entry -> file table entry(文件偏移量) -> v-node table entry(实际的文件内容,共享)->i-node ; 图3-8
  6. 延迟写: 写入文件时, 内核会先复制到缓冲区,待缓冲区满/tick到了, 再按照队列将数据写入磁盘; 同步写:fcntl 设置某个md, 开启O_SYNC同步写。 如数据库, 需要等返回写入磁盘成功了, 才算成功

五、Chapter four

  1. move操作, 只需将新的目录项,指向老的i节点,删除老的目录项,无需移动文件内容
  2. 软链接(符号链接): ln -s myfile soft; soft 指向myfile的绝对路径,删了myfile, 找不到; 硬链接:ln myfile hard; 普通文件, 指向同一个i节点, 删了myfile 无影响

六、Chapter five

  1. Fread & fwrite: 一次读写一个完整的结构, sizeof(struct)
  2. Snprintf: buff size(显示指定,超过被截断), 若返回值(实际要写入的字符串长度)小于size, 则没有被截断

七、Chapter six

  1. Time func: time_t 秒数since格林威治时间; time(&t); struct tm *tmp, tmp = localtime(&t) 年月日周日时分秒; strftime & strptime

八、Chapter seven

  1. 动态库:多个进程用共享库函数。 直接用新版本替代老版本的共享库函数, 例如so
  2. 环境表: environ list https://code.woboq.org/userspace/glibc/stdlib/setenv.c.html#__add_to_environ

九、Chapter eight

  1. 父进程与子进程不共享堆栈空间,子进程需要修改时,进行写时复制
  2. 解决竞争:轮询(耗cpu) & 信号、异步
  3. 解释器文件:1. 子进程exec,执行系统调用; 2. 发现非内核可执行程序,查找第一行作为解释器, 执行解释器文件
  4. system函数:equal to fork + exec + waitpid
  5. 进程调度优先级: 内核决定, 调整nice值

十、Chapter nine

  1. 会话->进程组(后台进程组&、前台进程组(无&)) 图9-7

十一、Chapter ten

  1. 概念。异步, 信号处理(忽略,捕捉,默认操作)
  2. 信号处理。 fork时,因为公用了进程空间, 所以子进程继承父进程的处理方式。 在子进程中, 执行exec, 则改为默认, 因为exec会覆盖原有的堆栈、数据,那么信号处理函数,也就是凉凉了。

十二、Chapter eleven

线程同步–不同线程间,共享资源的竞争

  1. 互斥量(锁)
  2. 死锁。 都在等待对方释放锁,锁依赖。 Pthread_mutex_try_lock, 获取锁失败, 则先释放已经占用的锁, 过后重试
  3. 读写锁。 读锁状态–读加锁状态ok, 写加锁阻塞; 写锁状态–读写都阻塞。使用读多,写少的业务场景。
  4. 条件变量。线程间交互时, 如果B进程的逻辑,依赖于A进程的一个条件(比如某个变量大于某个值),如果没有条件变量,那么B进程需要不断地加锁、判断、释放锁,有比较大的开销。 Pthread_cond_wait + pthread_cond_signal
  5. 自旋锁。B线程访问临界资源, 发现被A线程锁了,就地自旋等待A释放,避免调度的系统消耗,适用锁占用时间短的场景。 需要关闭内核抢占,否则可能会造成死锁,B抢占了A,但是A的锁还没释放,B又依赖于A的锁。
  6. 屏障(barriers)。多线程处理多个子任务计算时, 需要等待所有的进程都执行完,才往下走(计数)。pthread_barrier_init(, tread_num) + pthread_barrier_wait

十三、Chapter fourteen

  1. 将fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd,一是用于再select返回后,array作为源数据和fd_set进行FD_ISSET判断。二是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始 select前都要重新从array取得fd逐一加入(FD_ZERO最先),扫描array的同时取得fd最大值maxfd,用于select的第一个参数。 来自 https://www.cnblogs.com/alantu2018/p/8612722.html

十四、Chapter fifteen

  1. 管道:Popen(); pclose(); pipe()。 返回两个文件描述符,分别指向管道的读端和写端,如父进程创建一个管道,那么父子进程可以通过管道进行通信
  2. FIFO(命名管道): 不相干的进程可以进行数据交换。 Mkfifo(path, mode)
  3. 信号量:通过计数器实现对某个共享资源的操作控制;信号量大于1, 则表示可以用, 并且–
  4. 共享内存: shmget(申请一块共享内存,指定key, ftok)-> shmat(将不同的进程绑定到同个共享内存)