提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
世上有两种耀眼的光芒,一种是正在升起的太阳,一种是正在努力学习编程的你!一个爱学编程的人。各位看官,我衷心的希望这篇博客能对你们有所帮助,同时也希望各位看官能对我的文章给与点评,希望我们能够携手共同促进进步,在编程的道路上越走越远!
提示:以下是本篇文章正文内容,下面案例可供参考
一、进程优先级
1.1、进程优先级的基本概念
- 指定进程获取某种资源(如:CPU等)的先后顺序。
- 优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。
- 还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。
- Linux中优先级数字越小,优先级越高。
要维护一个进程的优先级:task_struct 进程控制块(是描述进程,里面包含了进程的所有属性) ---> struct ----> 内部字段 ----> (default_proi = 80) + (nice = ??) = proi(新的优先级)。
进程的优先级也是进程控制块中所有属性的一种(是内部字段中的一部分)。
1.2、优先级 VS 权限
权限:是能不能的问题。
优先级:已经能了,我们获取资源的顺序。
1.3、为什么要有优先级?
进程访问的资源(CPU)始终都是有限的,系统中进程大部分情况都是较多的。
操作系统关于调度和优先级的原则:分时操作系统,基本的公平,如果进程因为长时间不被调度,就造成了饥饿问题。
1.4、Linux的优先级的特点 && 查看方式
- ps -l:只能查看当前终端(当前所在分频页面)下启动的进程。
- ps -al:查看所有终端下启动的进程。
我们很容易注意到其中的几个重要信息,有下:
- UID : 代表执行者的身份
- PID : 代表这个进程的代号
- PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
- PRI :代表这个进程可被执行的优先级,其值越小越早被执行
- NI :代表这个进程的nice值
- 每次调整优先级,都是从80开始的
- nice/renice:可以对一个指定的进程,在启动前或运行时,对进程的优先级做动态调整
- nice并不能让你任意调整,而是有范围的!若任意调整的话,会打破原则中基本的公平
- 一个进程的优先级是不能频繁更改的
NI:进程优先级的修正数据,nice值,新的优先级 = 优先级 + nice,达到对于进程优先级动态修改的过程。
nice并不能让你随意调整,而是有范围的。[-20~19]
1.5、PRI and NI
- PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小 进程的优先级别越高
- 那NI呢?就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值
- PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice
- 这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行
- 所以,调整进程优先级,在Linux下,就是调整进程nice值
- nice其取值范围是-20至19,一共40个级别。
1.6、用top命令更改已存在进程的nice
- top
- 进入top后按“r”–>输入进程PID–>输入nice值
1.7、其他概念
- 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级。
- 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰。
- 并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行。
- 并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发。
多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进的方式:
如果这种方式下,进程过多,CPU切换负担过重,那么这些进程会出现卡顿的情况,本质是每一个进程被CPU调度的周期变长了。
二、命令行参数
vim Makefile
myprocess : myprocess.c
gcc - o $@ $ ^ -g
.PHONY:clean
clean :
rm - f myprocess
:%s/testStatus/myprocess/ //:将testStatus改成myprocess
vim myprocess.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
//第一个参数argc代表指针数组中参数的个数有多少
//第二个参数类型:指针数组,char *指向的是字符串首字符的地址
int main(int argc, char* argv[])
{
if (argc != 2)
{
printf("Usage: %s -[a,b,c,d]\n", argv[0]);//下标为0的位置,始终会有元素(./程序名)
return 1;
}
if(strcmp(argv[1], "-a") == 0)
{
printf("this is function1\n");
}
else if(strcmp(argv[1], "-b") == 0)
{
printf("this is function2\n");
}
else if(strcmp(argv[1], "-c") == 0)
{
printf("this is function3\n");
}
else if(strcmp(argv[1], "-d") == 0)
{
printf("this is function4\n");
}
else
{
printf("no this function!!\n");
}
for(int i = 0; i < argc; i++)
{
printf("argv[%d]->%s\n", i, argv[i]);
}
}
为什么要有命令行参数:
本质:命令行参数本质是交给我们程序不同的选型,用来定制不同的程序功能。命令行中会携带很多的选项。就像 switch 和 case一样。
命令行解释器(bash)在启动时,会为我们维护一张指针数组表,bash将 ./myprocess -a -b -c -d这条命令,遇到这条命令的空格就分成了 n 个子字符串,将子字符串转换成指针数组表里的元素。
我们再来看接下来的代码:
vim myprocess.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 100000;
int main()
{
printf("I am father process, pid: %d, ppid: %d, g_val: %d\n", getpid(), getppid(), g_val);
sleep(5);
pid_t id = fork();
if(id == 0)
{
//child
while(1)
{
printf("I am child process, pid: %d, ppid: %d, g_val: %d\n", getpid(), getppid(), g_val);
sleep(1);
}
}
else
{
// father
while(1)
{
printf("I am father process, pid: %d, ppid: %d, g_val: %d\n", getpid(), getppid(), g_val);
sleep(1);
}
}
}
父进程的数据,默认能被子进程看到并访问。
命令行中启动的程序,都会变成进程,其实都是bash的子进程。
三、环境变量
3.1、基本概念
- 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
- 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性。
3.2、常见环境变量
- PATH : 指定命令的搜索路径
- HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
- SHELL : 当前Shell,它的值通常是/bin/bash
- HISTSIZE:默认记录用户输入的最新的1000条的历史命令
3.3、查看环境变量方法
echo $PATH
//显示 PATH 环境变量的内容
3.4、有什么方法可以不用带路径,直接就可以运行呢?
我们自己生成的可执行程序,必须加程序的路径(./)才可以执行;但是Linux中的ls、pwd等这些可执行程序不需要加程序的路径,都可以执行,为什么呢?
Linux中,存在一些全局的设置,表明,告诉命令行解释器,应该去那些路径下,去寻找可执行程序。ls命令的路径是在PATH环境变量当中的,所以执行ls命令,就不用带./
echo $PATH
我们一般执行那个命令的时候,我们的命令行解释器(bash)会先去 PATH 这个环境变量下的所有路径里去找有没有我们对应的可执行程序,如果有,就不用加程序的路径(./)了;如果没有,就得加程序的路径(./)。
强硬的方法:
sudo cp myprocess usr/bin/
我们把我们写的myprocess可执行程序拷贝(安装)到Linux系统里指定的PATH环境变量下的 usr/bin 路径下。
温柔的方法:
把我们写的可执行程序myprocess的路径添加到PATH环境变量里呢?
PATH=/home/song/111/code/lesson
因为PATH是变量,我们可以直接让路径赋值给环境变量,但是我们会把PATH环境变量下的所有路径都给覆盖了,不能这么干。
但是不用担心,因为目前PATH环境变量是我们登录Linux系统的时候,就已经被加载到bash进程当中(内存),现在的环境变量是内存级的(默认我们查到的环境变量是内存级的);实际上环境变量的设置,不仅仅是在内存里,在系统的配置文件里也是有的,我们再重新登录一下Linux系统就可以了。
但是我们依然想要将可执行程序myprocess的路径添加到PATH环境变量里?
PATH=$PATH:/home/song/111/code/lesson
保留原来PATH里的路径,再添加一个路径。
但是这个只是在内存级里设置的,Linux系统重新登录一下,我们添加的路径又会消失。
我们想要我们添加的路径,永久的保存在PATH环境变量里,怎么做呢?
最开始的环境变量不是在内存中,而是默认在系统对应的配置文件中。在登录Linux系统的时候,会创建一个bash进程,系统里的配置文件会被bash进程拷贝一份,所以,我们每次重新登录的时候,PATH下的路径都会回复原样。
系统的配置文件在哪里?
这些配置文件都在属于自己的家目录下。
PATH=$PATH:/home/song/111/code/lesson
我们可以把我们添加的路径,可以通过 PATH=$PATH:/home/song/111/code/lesson它,添加到系统的配置文件中,这样我们每次重新打开Linux系统,我们都可以不带路径(./)来执行可执行程序(myprocess)。
3.5、和环境变量相关的命令
- echo + $xxx: 显示某个环境变量值
- export + name=val: 导入一个新的环境变量name
- env: 显示所有环境变量
- unset + name: 清除环境变量
- set: 显示本地定义的shell变量和环境变量
我们导入一个变量 hello=123456 ,但是我们没有写export,我们在所有的环境变量中搜索,并没有找到,但是我们却能够单独把这个变量的内容打印到屏幕上。虽然 hello 变量在环境变量中,但是叫本地变量(身在曹营,心在汉)。
3.6、环境变量的组织方式
每个程序都会收到一张环境变量表,环境变量表是一个字符指针数组,每个指针指向一个以’\0’结尾的环境字符串。
3.7、通过代码如何获取环境变量
通过第三方变量environ获取
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
// 获取一下环境变量
extern char** environ;//声明一下 environ
for(int i = 0; environ[i]; i++)
{
printf("env[%d]->%s\n", i, environ[i]);
}
}
//字符串可以理解为数组,数组名就是数组首元素的地址
//environ[i]:是指针数组里面的元素,元素是环境变量(字符串)的地址,所以元素就是字符串
libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时 要用extern声明。
命令行第三个参数
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[], char *env[])
{
int i = 0;
for(; env[i]; i++){
printf("%s\n", env[i]);
}
return 0;
}
磁盘中有系统的配置文件,而配置文件里面包含了环境变量;Linux系统登录,会在内存中加载bash进程。bash进程将配置文件(里面包含,环境变量)里的数据拷贝了一份。
环境变量可是很多的,bash内部是如何组织的呢?
bash在启动时,也会为我们维护一张表,这张表是一个指针数组表(char *env[]),这张表指向的内容都是char*的,所以,每当我们有一个环境变量(就是字符串,如:PATH=/usr/bin:/a/b/c/...),环境变量把对应的字符串,从配置文件加载进来,字符串信息就有了,把字符串的地址填到我们的环境变量表里,环境变量表的最后一位以 NULL 结尾。
bash进程启动的时候,默认会给我们子进程形成两张表:argv[]命令行参数表,env[]环境变量表(从OS的配置文件夹来)。bash通过各种方式交给子进程。
3.8、通过系统调用获取或设置环境变量
putenv , 后面讲解
getenv , 根据环境变量名拿到环境变量的内容,本次讲解
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char* argv[], char* env[])
{
char* path = getenv("PATH");
if (path == NULL)
return 1;
printf("path: %s\n", path);
for (int i = 0; env[i]; i++)
{
printf("env[%d]->%s\n", i, env[i]);
}
fork(); // 能!子进程能获得命令行参数和全局变量 bash(父进程) ---> main(子进程) ---> fork()(创建子进程的子进程)
}
常用getenv和putenv函数来访问特定的环境变量。
3.9、环境变量通常是具有全局属性的
- 环境变量通常具有全局属性,可以被子进程继承下去
#include <stdio.h>
#include <stdlib.h>
int main()
{
char * env = getenv("MYENV");
if(env){
printf("%s\n", env);
}
return 0;
}
直接查看,发现没有结果,说明该环境变量根本不存在
- 导出环境变量 export MYENV="hello world"
- 再次运行程序,发现结果有了!说明:环境变量是可以被子进程继承下去的!想想为什么?
3.10、内建命令
export myval=111111
//将导入myval成环境变量
echo $myval
//也能将环境变量myval的内容打印出来(111111)
那么export命令被命令行解释器(bash)运行的时候,不会创建子进程吗?
如果创建子进程,那么export导出来的环境变量就不应该被bash看到。
因为父进程的数据,默认能被子进程看到并访问,是因为创建子进程的时候,子进程是通过fork()继承的方式,看到父进程的。如果子进程导入了一些环境变量做修改了,父进程是看不到的。
但是,我们这里的父进程是能够看到导入环境变量的内容的,这是怎么回事?
那是因为 export 、echo是内建命令,由父进程(bash)亲自执行,并不会创建子进程。
总结
好了,本篇博客到这里就结束了,如果有更好的观点,请及时留言,我会认真观看并学习。
不积硅步,无以至千里;不积小流,无以成江海。