进程四个要素:
1. task_struct
2. 可执行的代码
3. 独立的地址空间
4. 独立的堆栈
内存复制与 COW copy-on-write
fork 之后两个进程共用同一内存
COW 基于页而不基于段(进程)
fork 与多线程
线程: 调用 fork 的线程外, 其他线程在子进程中 "蒸发" 了
锁: 锁的实现在用户态, 父进程的所有锁都会被复制(子进程可以 lock 已经在父进程中被 lock 的锁)
FD_CLOEXEC
fork 出的子进程执行 exec 之前就不能读写父进程中已打开的文件
pthread_atfork
prepare 处理函数由父进程在 fork 创建子进程前调用
parent 处理函数是在 fork 创建了子进程以后, 但在 fork 返回之前在父进程环境中调用的
child 处理函数在 fork 返回之前在子进程环境中调用
https://blog.csdn.net/yangbodong22011/article/details/78648419
exec 函数族
exec 函数执行成功后不会返回, 因为调用进程的实体, 包括代码段, 数据段和堆栈等都已经被新的内容取代, 只留下进程 ID 等一些表面上的信息仍保持原样
常与 vfork 混合使用
wait 函数族
http://man7.org/linux/man-pages/man2/waitid.2.html
孤儿进程与僵尸进程
孤儿进程: 父进程先结束, init 收养子进程, 并释放其资源
僵尸进程: 子进程先结束, 且父进程未收集其状态(子进程运行时才可能被收养, 僵尸进程无法转化为孤儿进程)
僵尸进程解决方案:
1. fork 两次, 将僵尸进程转化为孤儿进程
2. 忽略 SIGCLD(子进程状态改变时产生此信号)
task_struct thread_info 内核栈
内核栈: 内核态中, 进程使用内核空间中的栈, 而不是原用户空间中的栈
task_struct: 描述进程的内核栈
thread_info: 记录部分进程信息, 保存了进程描述符中需要频繁访问和快速访问的字段
- https://blog.csdn.net/tiankong_/article/details/75647488
- O_NONBLOCK
read 调用阻塞, 即进程暂停执行, 一直等到有数据来到为止
write 调用阻塞, 直到有进程读走数据
硬链接, 软链接
硬链接是一个指向 i-node 的路径
软链接是一个文件, 文件内容为另一个文件路径
select,poll,epoll
epoll 是 Linux 所特有, 而 select 则应该是 POSIX 所规定
select:
单个进程所打开的 FD 是有一定限制的, 它由 FD_SETSIZE 设置, 默认值是 1024
对 socket 进行扫描时是线性扫描, 即采用轮询的方法, 效率较低
需要维护一个用来存放大量 fd 的数据结构, 这样会使得用户空间和内核空间在传递该结构时复制开销大
poll:
本质上和 select 没有区别
没有最大连接数的限制, 原因是它是基于链表来存储的
epoll:
没有最大并发连接的限制
效率提升, 不是轮询的方式, 不会随着 FD 数目的增加效率下降
内存拷贝, 将用户关心的文件描述符的事件存放到内核的一个事件表中, 这样在用户空间和内核空间的 copy 只需一次
水平触发, 边缘触发
水平触发: 如果没有把数据一次性全部读写完, epoll_wait()会一直通知你
边缘触发: 对比于水平触发, epoll_wait()不会一直通知你
select(),poll()模型都是水平触发模式, 信号驱动 IO 是边缘触发模式, epoll()模型即支持水平触发, 也支持边缘触发, 默认是水平触发
- https://www.jianshu.com/p/dfd940e7fca2
- https://www.cnblogs.com/creazylinux/p/7364685.html
- http://www.cnblogs.com/yuuyuu/p/5103744.html
内建命令与外部命令
内建命令: 由 Shell 本身所执行的命令
外部命令: 由 Shell 副本 (新的进程) 所执行的命令
- mmap
- https://www.cnblogs.com/huxiao-tee/p/4660352.html#undefined
简化文件读写的操作, 提高性能
多进程共享文件
- https://www.cnblogs.com/huxiao-tee/p/4660352.html
- ptrace
提供了父进程可以观察和控制其子进程执行的能力, 并允许父进程检查和替换子进程的内核镜像 (包括寄存器) 的值
所有发送给被跟踪的子进程的信号(除了 SIGKILL), 都会被转发给父进程, 而子进程则会被阻塞, 这时子进程的状态就会被系统标注为 TASK_TRACED
父进程收到信号后, 就可以对停止下来的子进程进行检查和修改, 然后让子进程继续运行
实现:
strace 跟踪程所执行的系统调用
gdb 程序调试器
来源: http://www.bubuko.com/infodetail-2595689.html