进程相关概念
进程相关概念
程序和进程
程序: 编译好的二进制文件, 在磁盘上, 不占用系统资源(cpu, 内存, 打开的文件, 设备, 锁)
进程: 一个抽象的概念, 与操作系统原理联系紧. 进程是活跃的程序, 占用系统资源. 在内存中执行. 程序运行起来产生一个进程
程序-->剧本(纸), 进程-->戏(舞台, 演员, 灯光, 道具...): 同一个剧本可以在多个舞台同时上演. 同样, 同一个程序也可以加载为不同的进程(彼此之间互不影响)
并发
并发: 在操作系统中, 一个时间段中有多个进程都处于已启动运行到运行完毕之间的状态. 但在人一个时间点上只有一个程序在运行. 实质上并发是宏观并行, 微观串行
并发是时间段的概念, 单位时间内
单道程序设计
所有进程一个一个排队执行. 若A阻塞, B只能等待, 即使CPU属于空闲状态. 而在人机交互时阻塞的出现是必然的. 所有这种模型在系统资源利用上及其不合理. 在计算机发展历史上存在不久, 大部分便被淘汰
多道程序设计
在计算机内存中同时存放几道互相独立的程序, 它们在管理程序控制之下, 互相穿插的运行. 多道程序设计必须有硬件基础作为保证
时钟中断: 多道程序设计模型的理论基础. 并发时, 任意进程在执行期间都不希望放弃cpu. 因此系统需要一种强制让进程让出cpu资源的手段. 时钟中断有硬件基础作为保障, 对进程而言不可抗拒. 操作系统中的中断处理函数来负责调度程序执行
进程控制块PCB
每个进程在内核中都有一个进程控制块(PCB)来维护相关的信息, linux内核的进程控制块是task_struct结构体, 重点掌握:
- 进程id. 系统中每一个进程都有唯一的id, 在C语言中用pid_t类型表示, 其实就是一个非负整数
- 进程的状态. 就绪, 运行, 挂起, 停止等状态
- 进程切换时需要保存和恢复的一些cpu寄存器
- 描述虚拟地址空间的信息
- 描述控制终端的信息
- 当前工作目录(Current Working Directory)
- umask掩码
- 文件描述符, 包含很多指向file结构体的指针
- 和信号相关的信息
- 用户id和组id
- 会话(Session)和进程组
- 进程可以使用的资源上限(Resource Limit)
进程状态
进程基本的状态有5种. 分别为: 初始态, 就绪态, 运行态, 挂起态, 终止态
孤儿进程
父进程先于子进程结束, 孤儿进程被init进程领养, init进程变为孤儿进程的父亲
这是为了释放进程占用的系统资源
进程被接收之后, 能够释放用户空间
释放不了pcb, 必须由父进程释放
僵尸进程
子进程死了, 父进程还活着, 父进程不去释放子进程的pcb, 子进程就变成了僵尸进程
是一个已经死掉的进程
进程的回收
一个进程在终止时会关闭所有文件描述符, 释放用户空间分配的内存, 但它的PCB还保留着, 内核在其中保存了一些信息: 如果是正常终止则保存着退出状态, 如果是异常终止则保存着退出状态, 如果是异常终止则保存着导致该进程终止的信号是哪个. 这个进程的父进程可以调用wait或waitpid获取这些信息, 然后彻底清除掉这个进程. 一个进程的退出状态可以在shell中用特殊变量$?
查看, 因为shell是它的父进程, 当它终止时shell调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程
父进程调用wait函数可以回收子进程终止信息, 该函数有三个功能:
- 阻塞等待子进程退出
- 回收子进程残留资源
- 获取子进程结束状态(退出原因)
当进程终止时, 操作系统的隐式回收机制: (1) 关闭所有文件描述符; (2) 释放用户空间分配的内存. 内核的PCB仍存在. 其中保存该进程的退出状态(正常终止-->退出值; 异常终止-->终止信号)
进程间通信
linux环境下, 进程地址空间相互独立, 每个进程各自有一个不同的用户地址空间. 任何一个进程的全局变量在另一个进程中都看不到, 所以进程之间不能相互访问, 要交换数据必须通过内核. 在内核中开辟一块缓冲区, 进程1把数据从用户空间拷贝到内核缓冲区, 进程2再从内核缓冲区把数据读走, 内核提供的这种机制称为进程通信(IPC, InterProcess Communication)
在进程间完成数据传递需要借助操作系统提供的特殊方法, 如: 文件, 管道, 信号, 共享内存, 消息队列, 套接字, 命名管道. 随着计算机的蓬勃发展, 一些方法由于自身设计缺陷被淘汰或者弃用. 现今常用的进程间通信方式有:
- 管道(使用最简单, 有血缘关系, 无血缘关系时使用命名管道)
- 信号(开销最小)
- 共享内存映射区(无血缘关系)
- 本地套接字(最稳定)
琐碎知识点
父子进程相同之处: 全局变量, .data, .text, 栈, 堆, 环境变量, 用户ID, 宿主目录, 进程工作目录, 信号处理方式...
父子进程不同之处: 进程ID, fork返回值, 父进程ID, 进程运行时间, 闹钟(定时器), 未决信号集
似乎子进程复制了父进程0-3G用户空间内容, 以及父进程的PCB, 但pid不同. 每fork一个子进程都要将父进程的0-3G地址空间完全拷贝一份然后再映射至物理内存?
父子进程间遵循读共享写复制的原则. 这样设计(全局变量不能作为进程之间通信管道), 无论子进程执行父进程的逻辑还是执行自己的逻辑都能节省内存开销
刚fork出来之后: 两个地址空间用户区数据完全相同
后续各自进行了不同的操作: 各个进程的地址空间中的数据是完全独立的
问题:
fork函数的返回值: >0, 父进程的返回值; =0子进程的返回值
子进程创建成功之后, 代码的执行位置? 父进程执行到什么地方, 子进程就从什么位置开始
父子进程的执行顺序? 不能确定谁先执行
如何区分父子进程: 通过fork函数的返回值
父子进程永远共享的:
文件描述符
内存映射区
进程相关命令
ps:
ps aux | grep "XXX"
ps ajx | grep "XXX"
kill
kill -l
查看信号
kill -9 PID