前言:在上一篇了解完进程状态后,我们简单了解了进程优先级,然后遗留了一点内容,本篇我们就来研究进程间的切换,来理解上篇提到的并发。如果对进程优先级还有没理解的地方可以先阅读:
Linux进程优先级
本篇主要内容:
- 进程间的切换
- Linux进程调度队列
- 命令行参数
- 了解环境变量
1. 进程间的切换
在之前我们提到过CPU在调度进程时,每一个进程不是占有CPU就一直运行,每隔一段时间,自动被从CPU上剥离下来,因此必定会有进程与进程之间的切换!
但是进程没有运行结束就被剥离下来,那它的数据怎么办?
事实上:进程在切换时会不断对自己的数据进行保存和恢复,保存是保存寄存器中的数据,而不是寄存器本身,并且这些数据会被保存到进程的PCB里面
进程在进行高并发运行时,是通过多个进程间来回快速的切换,在一个时间段内,让多个代码同时推进这就是
并发
而在CPU中当另一个进程切换过来时,CPU不会删除之前的数据,之前的数据会被新切换过来的数据覆盖!
2. Linux进程调度队列
下图是Linux2.6内核中进程队列的数据结构,之间关系也已经给大家画出来,方便大家理解,而我们主要讲的就是两个小框,也就是活跃进程
和过期进程
2.1 活跃队列
在LInux进程优先级那里我们提到过优先级的范围是[60 , 99]这40个等级,而在调度队列的140个等级中,前100个我们不用管,后面40个等级正好对应我们普通优先级的等级
CPU运行时,会从优先级高的往下扫描,依次扫描完队列,如果存在进程则运行。
当我们在运行到优先级为
99
的时候,这时出现了一个优先级为80
的进程,该怎么办难道我们要重新返回吗?
- 当然是不可能返回的,所以我们需要第二个队列 – 过期队列
2.2 过期队列
如果一个活跃进程的一个进程正在运行,但又出现了一个优先级高的进程,该进程是不会放入活跃队列中的,而是放入过期队列来等待调度。
- 如果一直放入活跃队列的话,优先级低的可能永远不会调度,会造成进程饥饿
- 所以一般出现优先级高的新进程时,只允许往过期队列插入
因此,在不断调度的过程中,活跃进程里面的进程不断在减少,过期队列的进程一直在增加,那么如果活跃进程里的进程调度完之后,该怎么继续调度呢?
void *active // 活跃队列 void *expired // 过期队列
事实上,我们活跃队列和过期队列是由这两个指针控制
当我们在调度完一个队列时,将活跃进程和过期进程的内容交换,CPU依然处理活跃队列的进程
3. 命令行参数
在我们之前的学习时,我们写的main
函数都是不带参数的,但是其实它也是可以带参数的,那么让我们来了解以下!
1 int main(int argc, char *argv[]) 2 { 3 return 1; 4 }
而这两个参数就是我们要学习的命令行参数。这两个参数:
argv
表示指针数组,
argc
表示数组元素的个数
让我们通过一个例子来更好的了解命令行参数
1 #include<stdio.h> 2 #include<stdlib.h> 3 4 int main(int argc, char *argv[]) 5 { 6 int i = 0; 7 for(i = 0; i < argc; i++) 8 { 9 printf("%d: %s\n",i, argv[i]); 10 } 11 return 1; 12 }
当我们运行可执行程序时,它将所有的内容当成一个大的字符串,以空格为分隔符,分割多个字串
将命令行输入的字符串放入argv数组是操作系统干的
既然main
函数有这个功能,我们不妨来写一段有趣的代码
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 int main(int argc, char *argv[]) 5 { 6 if(argc != 4) 7 { 8 printf("Use error\nUsage: %s op[-add|sub|mul|div] d1 d2\n", argv[0]); 9 return 1; 10 } 11 // 程序一定会有4个命令行参数,第一个参数是程序名 12 int x = atoi(argv[2]); 13 int y = atoi(argv[3]); 14 if(strcmp(argv[1],"-add") == 0) 15 { 16 printf("%d + %d = %d\n", x, y, x+y); 17 } 18 else if(strcmp(argv[1],"-sub") == 0) 19 { 20 printf("%d - %d = %d\n", x, y, x-y); 21 } 22 else if(strcmp(argv[1],"-mul") == 0) 23 { 24 printf("%d * %d = %d\n", x, y, x*y); 25 } 26 else if(strcmp(argv[1],"-div") == 0) 27 { 28 if(y == 0) 29 { 30 printf("div zero error\n"); 31 } 32 else{ 33 printf("%d / %d = %d\n", x, y, x/y); 34 } 35 } 36 else{ 37 printf("Use error\n"); 38 } 39 return 1; 40 }
让我们来看一下:
命令行参数实现计算机程序
当然,命令行参数使用必须带选项
我们也可以是用命令行参数来实现一个自己的touch
指令
1 #include<stdio.h> 2 #include<stdlib.h> 3 4 int main(int argc, char *argv[]) 5 { 6 if(argc != 2) 7 { 8 printf("touch: missing file operand\n"); 9 return 1; 10 } 11 FILE *fp = fopen(argv[1], "w"); 12 fclose(fp); 13 return 1; 14 }
我们来执行一下命令行参数下的touch
指令
命令行参数实现touch
命令行参数,可以支持各种指令级别的命令行选项的设置
4. 环境变量
在学习环境变量之前,先解决几个问题。
- 为什么在执行系统的指令时可以直接使用
- 而我们自己写的程序需要加
./
呢
而这些其实都和环境变量有关系
抛开环境变量,直接来看,
.
代表当前目录,实则是让操作系统能够找到我们程序的位置。而系统指令正是由于环境变量的存在能让操作系统能够直接找到并执行。
而与这有联系的环境变量是PATH
:保存程序的默认搜索路径的环境变量
我们可以用指令查看环境变量
PATH
指令:
echo $PATH
每个冒号表示一段路径,执行程序时会默认搜索这些路径,而我们的程序不在这些路径所以要加上./
,那如果我们将程序所在路径添加到环境变量下,能不能脱离./
?
显而易见,当然是可以的!!!
那我们如何将自己的路径添加到环境变量中呢?
- 其实环境变量是可以通过指令来修改的!
指令: PATH=$PATH:路径
先用pwd指令找到当前路径
再使用指令将当前位置添加到环境变量中
最后我们不带./
执行一下可执行程序
发现我们在执行刚刚编写的可执行程序时,已经不需要带上./
了
注意:
在每次重启Xshell时,都会恢复默认路径!!!
5. 总结
本篇我们主要了解了以下知识:进程间的切换,Linux进程调度队列,命令行参数,环境变量,当然环境变量我们还只接触到了它的冰山一角,还有很多知识等待着我们发掘!让我们下篇了解剩下的几个环境变量!
谢谢大家支持本篇到这里就结束了