什么是进程?
简单来说, 进程就是运行中的程序
操作系统如何管理进程?
什么是操作系统?
操作系统是一个软件, 为了让计算机更好用, 安装在计算机中, 统筹管理软硬件资源
如何管理进程
先描述再组织
先对进程进行描述, 再对进程进行组织
Linux中, 进程的描述使用PCB(Process Control Block), Linux下就是一个结构体task_struct;
操作系统对进程的组织就是使用双向链表的形式将PCB组织起来
PCB中描述信息有什么?
标示符: 描述本进程的唯一标示符, 用来区别其他进程
状态: 任务状态, 退出代码, 退出信号等
优先级: 相对于其他进程的优先级
程序计数器: 程序中即将被执行的下一条指令的地址
内存指针: 包括程序代码和进程相关数据的指针, 还有和其他进程共享的内存块的指针
上下文数据: 进程执行时处理器的寄存器中的数据
I/O状态信息: 包括显示的I/O请求, 分配给进程的I/O设备和被进程使用的文件列表
记账信息: 可能包括处理器时间总和, 使用的时钟数总和, 时间限制, 记账号等
其他信息
进程查看?
/proc 进程信息存放目录
ps 命令
进程状态?
R运行状态(running): 并不意味着进程一定在运行中, 它表明进程要么是在运行中要么在运行队列里
S睡眠状态(sleeping): 进程在等待时间完成(这里的睡眠有时候也叫作可中断睡眠(interrupltible sleep))
D磁盘休眠状态(Disk sleep): 有时候也叫作不可中断睡眠状态(uninterruptible sleep), 在这个状态的进程通常会等待IO的结束
T停止状态(stopped): 可以通过发送SIGSTOP信号给进程来停止进程, 这个被暂停的进程可以通过发送SIGCONT信号让进程继续运行
X死亡状态(dead): 这个状态只是一个返回状态, 不会在任务列表里看到这个状态
两种特殊的进程?
僵尸进程
僵死状态是一个标胶特殊的状态. 当子进程退出并且父进程没有读取到子进程退出的返回代码时, 操作系统不会完全释放子进程的资源, 子进程就称为僵尸进程.
僵尸进程会以僵死状态保持在进程表中, 直到父进程读取到子进程的退出返回代码
僵尸进程的危害: 资源泄露
僵尸进程的处理方式: 关闭父进程
僵尸进程的避免方式: 进程等待
孤儿进程
父进程先于子进程退出, 父进程退出后, 子进程成为后台进程, 并且被1号进程领养
进程优先级
进程优先级是什么: 决定了资源的优先分配权的等级划分
为什么要有进程优先级: 让操作系统运行的更加合理
PRI 和 NI
PRI比较好理解, 就是进程的优先级, 或者通俗点说就是程序被CPU执行的先后顺序, 此值越小, 进程的优先级别越高
NI就是我们常说的nice值了, 其表示进程可被执行的优先级的修正数值
PRI值越小越快被执行, PRI的值不能直接修改, 但可以通过修改nice值来间接的修改PRI, PRI = PRI + NI
nice值的取值范围: -20 ~ 19, 一共40个级别
注意: 进程的nice值不是进程的优先级, 它们不是一个概念, 但是进程的nice值会影响到进程的优先级变化, 可以将nice值理解为进程优先级的修正数据
查看进程优先级的命令: top
程序地址空间
C语言中我们常见的内存空间分布如下
下面我们来段代码感受一下
#include
#include
int main(){
int a = 10;
pid_t pid = fork();
if(pid < 0){
perror("fork error");
return -1;
}
else if(pid == 0){
printf("[child process]: %d %p
", a, &a);
}
else{
sleep(1);
printf("[parent process]: %d %p
", a, &a);
}
return 0;
}
运行结果如下:
[child process]: 10 0x7fff93c78d78
[parent process]: 10 0x7fff93c78d78
可以发现输出的结果一模一样, 很好理解, 因为子进程是父进程的拷贝, 所以数据应该是一样的, 下面我们将代码改动一下
#include
#include
int main(){
int a = 10;
pid_t pid = fork();
if(pid < 0){
perror("fork error");
return -1;
}
else if(pid == 0){
a = 20;
printf("[child process]: %d %p
", a, &a);
}
else{
sleep(1);
printf("[parent process]: %d %p
", a, &a);
}
return 0;
}
输出结果如下:
[child process]: 20 0x7ffc6f5ade48
[parent process]: 10 0x7ffc6f5ade48
我们发现, 父子进程, 输出地址是一样的, 但是变量的内容不一样!
所以我们可以得出一下结论:
变量内容不同, 所以父子进程输出的变量绝对不是同一个变量
但是地址是一样的, 说明, 该地址不是物理地址
我们看到的程序地址空间都是假的, 是虚拟地址!
下面是进程地址空间的示意图
由上图可以看出, 同一个变量地址相同, 其实是虚拟地址相同, 内容不同其实是被映射到了不同的物理地址上!
页表: 记录虚拟地址和物理地址之间的映射关系; 并且对虚拟地址进行访问控制,
程序地址空间优点: 内存充分利用, 内存访问控制, 保持进程独立性
环境变量
什么是环境变量?
环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数
例如: 我们在编写C/C++代码的时候, 从来不知道我们的所链接的动态静态库在哪里, 但是照样可以链接成功, 生成可执行程序, 原因就是有相关环境变量帮助编译器进行查找
环境变量通常具有某些特殊用途, 还有在系统中通常具有全局特性
常见环境变量
PATH: 指定命令的搜索路径
HOME: 指定用户的主工作目录(即用户登录到Linux系统中时, 默认的目录)
SHELL: 当前shell, 它的值通常是/bin/bash
查看环境变量的方法
echo $NAME
环境变量相关命令
echo: 显示某个环境变量值
export: 设置一个新的环境变量
env: 显示所有环境变量
unset: 清除环境变量
set: 显示本地定义的shell变量和环境变量
通过代码获取环境变量的三种方法
方法一:
#include
int main(int argc, char* argv[], char* env[]){
int i = 0;
for(i = 0; env[i] != NULL; ++i){
printf("[%d]: [%s]
", i, env[i]);
}
return 0;
}
方法二:
#include
extern char** environ;
int main(){
int i = 0;
for(i = 0; environ[i] != NULL; ++i){
printf("[%d]: [%s]
", i, environ[i]);
}
return 0;
}
方法三:
char *getenv(const char *name);
其他概念?
并行: CPU资源足够, 多个程序同时运行
并发: CPU资源不够, 多个程序切换运行, 同时推进
独立性: 进程间相互独立
竞争性: 进程多资源少, 因此每个进程之间都具有竞争性