一、结构划分
- 概述: 第一、第二章
- I/O: 第三、第四、第五章
- 进程、线程:第七、八、九、十章–进程; 第十一、十二章–线程
- 更多I/O:第十三(守护进程)、十四、十八章
- IPC:第十五、十六、十七
- 实例: 十九(伪终端)、二十(数据库函数库)、二十一(网络打印机通信)
二、概述
- unix提供的服务
- UNIX的体系结构图 内核->系统调用->shell/公共函数库/应用
三、Chapter one
- Kernel->system call -> shell、lib、app unix体系结构图
- 文件包含目录和文件, type can be file or diretory
- The instance of App consider as process
- Fork: 返回两次, 给父进程返回子进程id, 给子进程返回0; 子进程需要用exit退出, 父进程通过waitpid等待子进程终止。Shell1.c
- 线程共享一个进程内的资源, 包括地址空间、进程属性等,所以共享数据需要考虑并发问题(同步化)
- 出错恢复->程序的健壮性。 重试、错误处理等
- 信号。 A进程可以通过向有权限的B进程, 发送信号,来控制B进程; 信号默认动作,也可以自定义信号处理函数
- 时间。日历时间和进程时间。 进程时间(real time(包含其他进程的运行时间), Cpu time(user, sys))
- 系统调用和库函数。 库函数通常是对系统调用的封装。 如库函数malloc。 内核、用户
- 若不希望修改函数的参数值。 若参数为指针,则考虑用const修饰, 否则因为C是值传递, 所以不加const修饰,也不会修改原值
四、Chapter tree
- Close函数:close会释放文件的所有记录锁; 进程终止时, 内核会自动close该进程打开的的文件
- Lseek: 设置文件打开、写入的锚点
- 文件操作函数组: open, lseek, write, read, close
- I/0效率:缓冲区的大小设置与bocksize, 一般设置为2的N次方
- 文件共享: proc table entry -> file table entry(文件偏移量) -> v-node table entry(实际的文件内容,共享)->i-node ; 图3-8
- 延迟写: 写入文件时, 内核会先复制到缓冲区,待缓冲区满/tick到了, 再按照队列将数据写入磁盘; 同步写:fcntl 设置某个md, 开启O_SYNC同步写。 如数据库, 需要等返回写入磁盘成功了, 才算成功
五、Chapter four
- move操作, 只需将新的目录项,指向老的i节点,删除老的目录项,无需移动文件内容
- 软链接(符号链接): ln -s myfile soft; soft 指向myfile的绝对路径,删了myfile, 找不到; 硬链接:ln myfile hard; 普通文件, 指向同一个i节点, 删了myfile 无影响
六、Chapter five
- Fread & fwrite: 一次读写一个完整的结构, sizeof(struct)
- Snprintf: buff size(显示指定,超过被截断), 若返回值(实际要写入的字符串长度)小于size, 则没有被截断
七、Chapter six
- Time func: time_t 秒数since格林威治时间; time(&t); struct tm *tmp, tmp = localtime(&t) 年月日周日时分秒; strftime & strptime
八、Chapter seven
- 动态库:多个进程用共享库函数。 直接用新版本替代老版本的共享库函数, 例如so
- 环境表: environ list https://code.woboq.org/userspace/glibc/stdlib/setenv.c.html#__add_to_environ
九、Chapter eight
- 父进程与子进程不共享堆栈空间,子进程需要修改时,进行写时复制
- 解决竞争:轮询(耗cpu) & 信号、异步
- 解释器文件:1. 子进程exec,执行系统调用; 2. 发现非内核可执行程序,查找第一行作为解释器, 执行解释器文件
- system函数:equal to fork + exec + waitpid
- 进程调度优先级: 内核决定, 调整nice值
十、Chapter nine
- 会话->进程组(后台进程组&、前台进程组(无&)) 图9-7
十一、Chapter ten
- 概念。异步, 信号处理(忽略,捕捉,默认操作)
- 信号处理。 fork时,因为公用了进程空间, 所以子进程继承父进程的处理方式。 在子进程中, 执行exec, 则改为默认, 因为exec会覆盖原有的堆栈、数据,那么信号处理函数,也就是凉凉了。
十二、Chapter eleven
线程同步–不同线程间,共享资源的竞争
- 互斥量(锁)
- 死锁。 都在等待对方释放锁,锁依赖。 Pthread_mutex_try_lock, 获取锁失败, 则先释放已经占用的锁, 过后重试
- 读写锁。 读锁状态–读加锁状态ok, 写加锁阻塞; 写锁状态–读写都阻塞。使用读多,写少的业务场景。
- 条件变量。线程间交互时, 如果B进程的逻辑,依赖于A进程的一个条件(比如某个变量大于某个值),如果没有条件变量,那么B进程需要不断地加锁、判断、释放锁,有比较大的开销。 Pthread_cond_wait + pthread_cond_signal
- 自旋锁。B线程访问临界资源, 发现被A线程锁了,就地自旋等待A释放,避免调度的系统消耗,适用锁占用时间短的场景。 需要关闭内核抢占,否则可能会造成死锁,B抢占了A,但是A的锁还没释放,B又依赖于A的锁。
- 屏障(barriers)。多线程处理多个子任务计算时, 需要等待所有的进程都执行完,才往下走(计数)。pthread_barrier_init(, tread_num) + pthread_barrier_wait
十三、Chapter fourteen
- 将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
- 管道:Popen(); pclose(); pipe()。 返回两个文件描述符,分别指向管道的读端和写端,如父进程创建一个管道,那么父子进程可以通过管道进行通信
- FIFO(命名管道): 不相干的进程可以进行数据交换。 Mkfifo(path, mode)
- 信号量:通过计数器实现对某个共享资源的操作控制;信号量大于1, 则表示可以用, 并且–
- 共享内存: shmget(申请一块共享内存,指定key, ftok)-> shmat(将不同的进程绑定到同个共享内存)