目录
冯诺依曼体系组成
创建进程 fork
环境变量:
程序地址空间
进程等待 wait
进程替换exec
想了解进程,要先了解操作系统(用来管理计算机的软硬件,为了更好的使用),要知道绝大多数电脑是满足冯诺依曼体系结构的,进程是执行的程序。
五大控制单元 输入设备 存储器 输出设备 运算器 控制器
操作系统:用来管理计算机的软硬件
如何理解管理呢?
可分为两步,一.描述被管理对象
二.组织被管理对象
那如何描述与组织呢?
1.描述:用struct结构体描述。
2.组织:用链表或其他数据结构进行组织。
系统调用和库函数有什么关系呢?
用户使用操作系统暴露的接口开发使用,系统调用适度封装形成了库,库函数是系统调用的产物。
什么是进程呢?是内存中被触发的的程序。这也是我们上文提到冯诺依曼体系的原因。
PCB是进程控制块,本质上是一种数据结构,Linux中名为struct task_struct,用来描述进程的信息。
组织进程则是用数组或者链表等形式来处理一个个进程控制块(PCB)。
ls /proc 是查看进程
ps aux和top命令
ps aux 查看静态信息
第一行对应意义
USER:启动该进程的用户账号名称;
PID:该进程在系统中的数字 ID 号,在当前系统中是唯一的;
%CPU:CPU占用的百分比;
%MEM:内存占用的百分比;
VSZ:占用虚拟内存(swap 空间)的大小;
RSS:占用常驻内存(物理内存)的大小;
TTY:表明该进程在哪个终端上运行。“ ?” 表示未知或不需要终端;
STAT:显示进程当前的状态,S(休眠)、R(运行)、Z(僵死)、<(高优先级)、N(低优先级)、s(父进程)、+(前台进程);
START:启动该进程的时间;
TIME:该进程占用的CPU时间;
COMMAND:启动该进程的命令的名称;
top 查看动态信息
根据父进程信息创建一个子进程(PCB)从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
还要了解进程处于什么样的状态,分为入下几类
R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
我们还要了解僵尸进程的概念,什么是僵尸进程
当子进程退出,而父进程没有接收到子进程退出的返回值,那么子进程就是僵尸进程。
僵尸进程有危害?会占用浪费内存资源,可能造成空间浪费。严重导致内存泄露
了解了僵尸进程后,如果情况相反呢?
父进程先退出,这就是孤儿进程,父进程退出后,孤儿进程被1号init进程领养
查看系统进程 ps -l
UID : 代表执行者的身份
PID : 代表这个进程的代号
PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
PRI :代表这个进程可被执行的优先级,其值越小越早被执行(进程的优先级)
NI :代表这个进程的nice值(取值范围是-20至19),用来修正进程的优先级。
进程的优先级决定了进程的执行
PRI(新) = PRI(旧) + NI
那么如何改变nice值呢?
就要用到上边讲的top了
进入top后按“r”–>输入进程PID–>输入nice值
进程之间的属性
竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高
效完成任务,更合理竞争相关资源,便具有了优先级
独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发
操作系统运行环境的一些参数。常用的有,
PATH : 指定命令的搜索路径
HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
SHELL : 当前Shell,它的值通常是/bin/bash。
那如何查看环境变量呢?
用echo $NAME //NAME:你的环境变量名称
环境变量相关命令
1. echo: 显示某个环境变量值
2. export: 设置一个新的环境变量
3. env: 显示所有环境变量
4. unset: 清除环境变量
5. set: 显示本地定义的shell变量和环境变量
获取环境变量 : printf("%s\n", getenv("PATH"));
程序地址空间是虚拟空间,保护实际空间
再次认识进程创建fork
从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
通过内核空间开辟进程,复制父进程参数,fork有两个返回值,子进程返回值为0,父进程返回值为子进程pid。
接下来我们来认识写时拷贝,是等到修改数据时才真正分配内存空间,之前共用物理内存页。
进程退出有三种场景
1.代码运行完毕,结果正确
2.代码运行完毕,结果不正确
3.代码异常终止
_exit函数 VS exit函数
前者是直接结束进程,后者在调用exit之前,还做了其他工作:
1. 执行用户通过 atexit或on_exit定义的清理函数。
2. 关闭所有打开的流,所有的缓存数据均被写入
3. 调用_exit
而return和exit有什么区别呢?
return n等同于执行exit(n),因为调用main的运行时函数会将main的返回值当做 exit的参数。
exit是结束整个进程,return是结束所在函数,当return main函数时,就是结束当前进程。
进程等待 wait
用来接收子进程退出信息。
如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退
出信息。
如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
如果不存在该子进程,则立即出错返回。成功返回被等待进程pid,失败返回-1。
进程替换exec
进程程序替换,那进程替换的概念是怎么样的,又要怎么实现呢?
父子进程是代码共享的,所以实际上子进程执行的是父进程的一部分,那又如何使子进程执行与父进程不同的程序呢?这里就需要程序替换函数来替换子进程的代码,使父子进程的代码不同。
函数是exec函数,有六个,根据情况使用,分别是
#include
`
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);int execve(const char *path, char *const argv[], char *const envp[]);