目录
参考
概述
我们平时写的 C 语言代码,通过编译器编译,最终它会成为一个可执行程序,当这个可执行程序运行起来后(没有结束之前),它就成为了一个进程。
程序是存放在存储介质上的一个可执行文件,而进程是程序执行的过程。进程的状态是变化的,其包括进程的创建、调度和消亡。程序是静态的,进程是动态的。
示例:
演程序就类似于剧本(纸),进程类似于戏(舞台、演员、灯光、道具…),同一个剧本可以在多个舞台同时上演。同样,同一个程序也可以加载为不同的进程(彼此之互不影响)。
在Linux系统中,操作系统是通过进程去完成一个一个的任务,进程是管理事务的基本单元。
进程拥有自己独立的处理环境(如:当前需要用到哪些环境变量,程序运行的目录在哪,当前是哪个用户运行此程序等)和系统资源 (如: 处理器 CPU 占用率、存储器、1/0设备、数据、程序)。
我们可以这么理解,公司相当于操作系统,部门相当于进程,公司通过部门来管理(系统通过进程管理),对于各个部门,每个部门有各自的资源,如人员、电脑设备、打印机等。
并行和并发
并行(parallel):指同一时刻,有多条指令在多个处理器上同时执行。
并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成多干个片段,十多个进程快速交替的执行。
举例:
并行是两个队列同时各自使用一台咖啡机
并发是两个队列同时交替使用一台咖啡机
MMU (内存管理单元)
MMU是Memory Management Unit的缩写,中文名是内存管理单元,它是中央处理器(CPU) 中用来管理虚拟存储器、物理存储器的控制线路,同时也负责虚拟地址映射为物理地址以及提供硬件机制的内存访问授权,多用户多进程操作系统。
进程控制块PCB
进程运行时,内核为进程每个进程分配一个PCB(进程控制块),维护进程相关的信息,Linux内核的进程控制块是task_struct结构体。
在**/usr/src/linux-headers-xxx/include/linux/sched.h** 文件中可以查看struct task_struct结构体定义:
$ vim /usr/src/linux-headers-4.10.0-28/include/linux/sched.h
其内部成员有很多,我们掌握以下部分即可:
- 进程id。系统中每个进程有唯一的id,在C语言中用pid_t类型表示,其实就是一个非负整数
- 进程的状态,有就绪、运行、挂起、停止等状态。
- 进程切换时需要保存和恢复的一些CPU寄存器
- 描述虚拟地址空间的信息。
- 描述控制终端的信息。
- 当前工作目录 (Current Working Directory)。
- umask掩码
- 文件描述符表,包含很多指向file结构体的指针.
- 和信号相关的信息。
- 用户id和组id。.
- 会话 (Session) 和进程组
- 进程可以使用的资源上限 (Resource Limit)。
进程的状态
进程状态反映进程执行过程的变化。这些状态随着进程的执行和外界条件的变化而转换。
在三态模型中,进程状态分为三个基本状态,即运行态,就绪态,阻塞态
在五态模型中,进程分为新建态、终止态,运行态,就绪态,阻塞态
- TASK_RUNNING:进程正在被CPU执行。当一个进程刚被创建时会处于TASK_RUNNABLE,表示已经准备就绪,正等待被调度。
- TASK_INTERRUPTIBLE (可中断):进程正在睡眠(也就是说它被阻塞)等待某些条件的达成。一旦这些条件达成,内核就会把进程状态设置为运行。处于此状态的进程也会因为接收到信号而提前被唤醒,比如给一个TASK_INTERRUPTIBLE状态的进程发送SIGKILL信号,这个进程将先被唤醒(进入TASK_RUNNABLE状态),然后再响应SIGKILL信号而退出(变为TASK_ZOMBIE状态),并不会从TASK_INTERRUPTIBLE状态直接退出。
- TASK_UNINTERRUPTIBLE (不可中断):处于等待中的进程,待资源满足时被唤醒,但不可以由其它进程通过信号或中断唤醒。由于不接受外来的任何信号,因此无法用kill杀掉这些处于该状态的进程。而TASK_UNINTERRUPTIBLE状态存在的意义就在于,内核的某些处理流程是不能被打断的。如果响应异步信号,程序的执行流程中就会被插入一段用于外理异步信号的流程,于是原有的流程就被中断了,这可能使某些设备陷入不可控的状态。处于TASK _UNINTERRUPTIBLE状态一般总是非常短暂的,通过ps命令基本上不可能捕捉到。
- TASK ZOMBIE (僵死):表示进程已经结束了,但是其父进程还没有调用wait4或waitpid()来释放进程描述符。为了父进程能够获知它的消息,子进程的进程描述符仍然被保留着。一旦父进程调用了wait4(),进程描述符就会被释放。
- STASK STOPPED (停止):进程停止执行。当进程接收到SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU等信号的时候。此外,在调试期间接收到任何信号,都会使进程进入这种状态。当接收到SIGCONT信号,会重新回到TASK_RUNNABLE。
ps命令
进程是一个具有一定独立功能的程序,它是操作系统动态执行的基本单元。ps命令可以查看进程的详细状况,常用选项(选项可以不加"-")如下:
选项 | 含义 |
---|---|
-a | 显示终端上的所有进程,包括其他用户的进程 |
-u | 显示进程的详细状态 |
-x | 显示没有控制终端的进程 |
-w | 显示加宽,以便显示更多的信息 |
-r | 只显示正在运行的进程 |
用法
ps -a
ps -ef
ps -aux
# 根据进程号查询
ps -aux |grep 5710
进程号和相关函数
每个进程都由一个进程号来标识,其类型为 pid_t(整型),进程号的范围:0~32767。进程号总是唯一的,但进程号可以重用。当一个进程终止后,其进程号就可以再次使用。
接下来,再给大家介绍三个不同的进程号。
进程号 (PID)
标识进程的一个非负整型数
父进程号 (PPID)
任何进程(除init 进程)都是由另一个进程创建,该进程称为被创建进程的父进程,对应的进程号称为父进程号(PPID)。如,A进程创建了B进程,A的进程号就是B进程的父进程号.
进程组号 (PGID)
进程组是一个或多个进程的集合。他们之间相互关联,进程组可以接收同一终端的各种信号,关联的进程有个进程组号(PGID)。这个过程有点类似于QQ 群,组相当于QQ群,各个进程相当于各个好友,把各个好友都拉入这个QQ 群里,主要是方便管理,特别是通知某些事时,只要在群里吼一声,所有人都收到,简单粗暴。但是,这个进程组号和QQ群号是有点区别的,默认的情况下,当前的进程号会当做当前的进程组号。大部分情况下,启动一个进程,进程号和进程组号是一样的
getpid函数
这个函数返回值永远成功
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
功能:
获取本进程号 (PID)
参数:
无
返回值:
本进程号
// 调用
pid_t pid = -1;
pid = getpid();
printf("进程号:%d\n",pid);
getppid函数
这个函数返回值永远成功
#include