【Linux】进程的优先级&&环境变量-程序员宅基地

技术标签: chrome  c语言  运维  Linux从入门到入土  linux  开发语言  

个人主页zxctscl
如有转载请先通知

1. 前言

上一篇在进程中提到了 【Linux】进程状态&&僵尸进程和孤儿进程&&阻塞、挂起和运行,这次来继续来谈进程。

2. 进程的优先级

2.1 什么是优先级

在进程的PCB中存在一个进程的优先级,那么什么是进程的优先级?
进程的优先级就是指定一个进程获取某种资源的顺序。

进程中使用task_struct进程控制块结构体中的内部字段用一个整型prio表示优先级。
Linux中优先级数字越小,优先级越高

比较一下优先级和权限:权限决定一件事能不能做,而有优先级就表示一件事情能做只是代表获取资源的顺序。

2.2 为什么要有优先级

因为进程访问的资源(CPU)时有限的,系统中进程大部分情况都是有较多的。
就像打游戏时候,键盘、鼠标等硬件只有一套,不同的进程本质上对系统硬件资源本来就是通过操作系统方式来实现资源的共享。
更形象就像在下课高峰期,食堂买饭要排队。

操作系统关于调度和优先级的原则:分时操作系统,要保证基本的公平。如果进程因为长时间不被调度,就造成了饥饿问题。

2.3 优先级的查看方式

为了方便查看,先用C语言写测试代码:
Makefile:

  1 myprocess:myprocess.c
  2   gcc -o $@ $^ 
  3 .PHONY:clean
  4 clean:
  5   rm -f myprocess

myprocess.c:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3
  4 int main()
  5 {
    
  6   while(1)
  7   {
    
  8     printf("I am a process,pid:%d\n",getpid());
  9     sleep(1);
 10   }
 11 }

在Linux中查看优先级的方式用到命令:

ps -al

在这里插入图片描述
PRI:进程的优先级
NI:进程优先级的修正数据,N是nice值。新的优先级=优先级+nice值,达到对于进程优先级动态修改的过程。
UID : 代表执行者的身份
PID : 代表这个进程的代号
PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号

2.4 对优先级调整

改之前的NI:
在这里插入图片描述

想要对进程优先级调整就想要用到命令:

top

在这里插入图片描述
然后输入:r
在这里插入图片描述

再输入进程的pid:
在这里插入图片描述

最后输入要修改的nice值:
先修改为100:
在这里插入图片描述

再来查看一下这个进程的NI:
在这里插入图片描述
所以说:nice值不能让用户任意调整,而是有范围的,如果随便写就有可能使操作系统调度出现不平衡,必须在可控范围内调整。
nice其取值范围是-20至19,一共40个级别。
在这里插入图片描述

当再想要修改nice值的时候,就不能修改了,普通用户不能频繁修改nice值,把账号先切换为root,再把nice值修改为-10:
此时PRI就变为了70:
在这里插入图片描述
每次调整优先级PRI都是从80开始范围是从60到99

新的优先级=优先级+nice值
一般不推荐用户调整进程的优先级

在这里插入图片描述
竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级
独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰

并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发。

3. 命令行参数

在C语言中会出现main函数带参的情况。
像:

int main(int argc,char *argv[])
{
    }

main函数的参数可以带可不带。
这些参数

在Linux里面写一个测试代码:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3
  4 int main(int argc,char *argv[])
  5 {
    
  6   int i;
  7   for(i=0;i<argc;i++)
  8   {
    
  9     printf("argv[%d]->%s\n",i,argv[i]);
 10   }
 11 }

argc表示数组中元素的个数,argv[]是一个指针数组。

如果直接执行程序不带参数,那么默认argv[0]执行的就是执行的程序,而且就是有它一个。
如果带1参数,会自动在将参数变成2个,0号下标指向执行的程序,1号下标就只向这个参数。

在这里插入图片描述

这就是命令行字符串。

在这里插入图片描述

这里的argv是一个变成数组,把命令行字符串以空格为分隔符放在argv里面,下标与输入顺序匹配。再将这些以参数的形式传给了main函数,有几个这样的字符串,那么argc就是几。argv数组最后必须以NULL结尾。

在这里插入图片描述
修改一下代码:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<string.h>
  4
  5 int main(int argc,char *argv[])
  6 {
    
  7   if(argc!=2)
  8   {
    
  9     printf("Usage: %s -[a,b,c,d]\n",argv[0]);
 10     return 1;
 11   }
 12
 13   if(strcmp(argv[1],"-a")==0)
 14   {
    
 15     printf("this is function1\n");
 16   }
 17
 18   else if(strcmp(argv[2],"-b")==0)
 19   {
    
 20     printf("this is function2\n");
 21   }
 22
 23   else if(strcmp(argv[3],"-c")==0)
 24   {
    
 25     printf("this is function3\n");
 26   }
 27
 28   else if(strcmp(argv[4],"-d")==0)
 29   {
    
 30     printf("this is function4\n");
 31   }
 32   else{
    
 33   printf("no this function\n");
 34   }
 35 }
 36

编译运行一下,发现必须要带选项,那就带上:

在这里插入图片描述
同一个程序可以通过不同的选项来执行同一个程序内的不同的功能。

就像是ls带不同选项,功能不一样:
在这里插入图片描述

命令行参数本质交给我们程序的不同选项,用来定制不同的程序功能。命令中就会携带很多的选项

在这里插入图片描述
写一个父子进程的代码:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<string.h>
  4
  5 int g_val=100000;
  6
  7 int main()
  8 {
    
  9   printf("I am a father process,pid:%d,ppid:%d,g_val:%d\n",getpid(),getppid(),g_val);
 10   sleep(5);
 11
 12   pid_t id=fork();
 13   if(id==0)
 14   {
    
 15     while(1)
 16     {
    
 17      printf("I am a child process,pid:%d,ppid:%d,g_val:%d\n",getpid(),getppid(),g_val);
 18      sleep(1);
 19
 20     }
 21   }
 22   else
 23   {
    
 24   while(1)
 25   {
    
 26   printf("I am a father process,pid:%d,ppid:%d,g_val:%d\n",getpid(),getppid(),g_val);
 27   sleep(1);
 28
 29   }
 30 }
 31 }

编译运行:
在这里插入图片描述
发现父进程的数据,默认能被子进程看到并访问。
父进程的ppid是谁的?

再修改一下代码:把创建子进程的代码注释
在这里插入图片描述
再编译运行一下:
发现这个ppid还是24361

在这里插入图片描述

查看一下:
发现它是bash

在这里插入图片描述

当把代码改回之前的:
在这里插入图片描述

然后重新编译运行:发现ppid还是24361,还是bash:
在这里插入图片描述

说明命令行中启动的程序,都会变成进程,其实都是bash的子进程。输入的命令行字符串,默认是输入给父进程bash的。

就像ls,本身也是一个可执行程序,启动它就和启动自己写的程序是一样的
在这里插入图片描述

4. 环境变量

4.1 环境变量与配置文件

4.1.1 环境变量初步介绍

为什么执行自己写的程序时候要带路径:
在这里插入图片描述
而ls就不需要:
在这里插入图片描述
也可以带路径执行:
在这里插入图片描述
这个主要是因为在Linux系统中,存在一些全局的设置,表明,告诉命令行解释器,应该去哪些路径下去寻找可执行程序。

这些设置在PATH里面保存,查看的话就用:

echo $PATH

在这里插入图片描述

系统中很多配置,在我们登录Linux系统的时候,就已经被加载到bash进程中,而bash进程就在内存中。

PATH:环境变量
要打印环境变量的内容用:$PATH

bash在执行命令的时候,想要先找到命令,因为未来要加载。
命令会在对应的路径下找,如果没有找到就报:
在这里插入图片描述
如果找到了就会加载并运行这个程序:
在这里插入图片描述
在这里插入图片描述
如果不加路径来执行myprocess会报:找不到
在这里插入图片描述
如果需要把自己写的程序执行和系统的一样,可以可执行程序添加到bash路径下:
在这里插入图片描述

但是一些命令就不能了,PATH里面就只剩下刚才加的路径:

在这里插入图片描述
因为对环境变量直接赋值,就相当于把环境变量直接覆盖了
自己写的程序到是可以直接运行了:
在这里插入图片描述

直接重新登录就可以了:
此时路径又回来了,刚才设置的环境变量就没有了
在这里插入图片描述
默认我们查到的环境变量是内存级的。

不用覆盖,怎么添加环境变量:
用:

PATH=$PATH:路径

在这里插入图片描述
此时再查看环境变量时候:这个环境变量就有了
在这里插入图片描述
再执行程序的时候带不带路径都可以:
在这里插入图片描述

4.1.2 配置文件

最开始的环境变量不是在内存中,而是在系统对应的配置文件中,用户在登录时候,会创建一个bash进程,此时bash就会读取配置文件,就会把配置文件里面的环境变量在bash里面拷贝一份。
这就是为什么重新登陆的时候之前在PATH加到就不存在了,因为配置文件没有改。

这个配置文件在哪里呢?
切换到家目录下,有与配置文件有关的文件:.bash_profile.bashrc还有/etc/bashrc
在这里插入图片描述

打开.bash_profile
在这里插入图片描述
环境变量默认是在配置文件里面的

打开.bash_profile后把路径加上去:
在这里插入图片描述
此时不加路径也可以运行:
在这里插入图片描述

程序登陆:不加路径还是可以运行
在这里插入图片描述

4.2 更多环境变量

env可以查看其他环境变量
在这里插入图片描述
来看看PWD:
会随着路径变化,把当前路径会记录在PWD的环境变量中:
在这里插入图片描述

系统启动的时候会把改程序的shell运行起来,这就有了命令行解释器:
在这里插入图片描述

曾经输入的命令是记录下来的,不可能全部都记住,HISTSIZE默认记录新的3000条历史命令
在这里插入图片描述
history可以查看历史命令:
在这里插入图片描述
是目前维护历史命令的数目:
在这里插入图片描述
这些环境变量是在用户登陆的时候系统自动加载的:
在这里插入图片描述

在这里插入图片描述
我们可以自己导入环境变量:

export THIS_IS_MY_ENV=value

在这里插入图片描述
此时就存在了这个环境变量:
在这里插入图片描述
取消环境变量:

unset THIS_IS_MY_ENV

此时就查不到了:
在这里插入图片描述
在这里插入图片描述
当加环境变量时候不加export,直接写,不会报错:
也可以查出来
在这里插入图片描述
这种变量叫做本地变量。

4.3 整体理解环境变量与系统

先来写一个获取环境变量的测试代码:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<string.h>
  4
  5
  6 int main()
  7 {
    
  8   extern char** environ;
  9   int i;
 10   for(i=0;environ[i];i++)
 11   {
    
 12     printf("env[%d]->%s\n",i,environ[i]);
 13   }
 14 }

在这里插入图片描述
这些环境变量就是和用env指令查到的一样。
环境变量默认也是可以被子进程拿到的,就说明本身不在子进程里面,而环境变量们默认是在bash内部

解释一下代码:
在这里插入图片描述

在磁盘中:有系统级的配置文件,有全局的也有当前进程的,还包含了环境变量。

在内存中:当有一天我们登陆时,就会在内存中给当前用户创建一个进程,就是bash/shell,登陆的时候把这些配置文件信息就加载到bash内部。
所有环境变量的数据都在bash里面。
环境变量有变量名有变量内容,环境变量的本质就是数据。当启动我们自己的程序时,就会在内存中创建一个子进程./myprocess,而父进程的数据,默认能被子进程看到并访问,环境变量是全局的。

环境变量如此多,那么在bash内部如何组织环境变量?
实际上bash在启动的时候,会维护一张表,这张表是一个指针数组char *env[],指向内容全是char*的。每当有一个环境变量,像PATH=/usr/bin:a/b/c…,也就是字符串,环境变量就可以把对应的字符串从配置文件加载过来,配置信息就有了。把环境变量的地址填到环境变量表里面,这个环境变量就纳入到了bash里面。每一个环境变量都是字符串等于内容,最后在以NULL结尾。

bash进程在启动的时候,默认会给我子进程的两张表:argv[]命令行参数表,env[]环境变量表。bash通过各种方式交给子进程。argv[]命令行参数表来自用户输入的命令行;env[]环境变量表从配置文件来

系统为了方便用户找到环境变量表, 就提供了extern char **environ指针就能被全局看到。就可以用[]来访问指针字符串的内容。
在这里插入图片描述
在这里插入图片描述

还可以在main函数里面加三个参数来获取环境变量:

    1 #include<stdio.h>
    2 #include<unistd.h>
    3 #include<string.h>
    4
    5
    6 int main(int argc,char *argv[],char *env[])
    7 {
    
    8   int i;
    9   for(i=0;env[i];i++)
   10   {
    
   11     printf("env[%d]->%s\n",i,env[i]);
   12   }
   13 }

编译运行:
在这里插入图片描述
在这里插入图片描述

exoprt导环境变量本质上就是在env[]环境变量表中找到一个没有被使用的位置,然后把它指向对应的环境变量。导环境变量就是把字符串添加到表里。

4.4 环境变量的特性

环境变量具有系统级的全局属性,因为环境变量本身会被子进程继承
系统刚开始启动时候,启动了bash,bash可以启动很多进程,进程可以继续通过代码创建子进程。但是不管有多少给子进程,bash都能拿到所有子进程对应的环境变量,也就是环境变量具有系统级的全局属性。

系统中还存在getenv,可以根据环境变量直接拿到环境变量的内容,可以单个获取环境变量的内容。
在这里插入图片描述
putenv就是用来导环境变量
在这里插入图片描述

用代码来获取一下:

    1 #include<stdio.h>
    2 #include<unistd.h>
    3 #include<string.h>
    4 #include<stdlib.h>
    5
    6 int main(int argc,char *argv[],char *env[])
    7 {
    
    8  char *path=getenv("PATH");
    9  if(path==NULL)return 1;
   10  printf("path:%s\n",path);
   11 }

编译运行之后,就可以单独获取PATH一个环境变量了:
在这里插入图片描述
获取环境变量的三种方式:

  1. extern char **environ;
  2. 通过main函数参数
  3. getenv(“path”);

在这里插入图片描述
用export导的环境变量也会创建子进程吗?
在这里插入图片描述
并不是,export导的环境变量不会创建子进程。它是内建命令。
在Linux中百分之八十的美丽都是bash创建子进程执行的。还有一些命令是由bash自己亲自去执行的。bash也是用c语言写的,就相当于在bash里面有个export()函数,直接函数调用就执行export了。

把环境变量先清空:
在这里插入图片描述
再在执行其他命令,能被执行的都是内建命令
在这里插入图片描述

本地变量只在本bash内部有效,无法被子进程继承下去。导成环境变量,此时才能被获取。

有问题请指出,大家一起进步!!!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zxctsclrjjjcph/article/details/137764705

智能推荐

JWT(Json Web Token)实现无状态登录_无状态token登录-程序员宅基地

文章浏览阅读685次。1.1.什么是有状态?有状态服务,即服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如tomcat中的session。例如登录:用户登录后,我们把登录者的信息保存在服务端session中,并且给用户一个cookie值,记录对应的session。然后下次请求,用户携带cookie值来,我们就能识别到对应session,从而找到用户的信息。缺点是什么?服务端保存大量数据,增加服务端压力 服务端保存用户状态,无法进行水平扩展 客户端请求依赖服务.._无状态token登录

SDUT OJ逆置正整数-程序员宅基地

文章浏览阅读293次。SDUT OnlineJudge#include<iostream>using namespace std;int main(){int a,b,c,d;cin>>a;b=a%10;c=a/10%10;d=a/100%10;int key[3];key[0]=b;key[1]=c;key[2]=d;for(int i = 0;i<3;i++){ if(key[i]!=0) { cout<<key[i.

年终奖盲区_年终奖盲区表-程序员宅基地

文章浏览阅读2.2k次。年终奖采用的平均每月的收入来评定缴税级数的,速算扣除数也按照月份计算出来,但是最终减去的也是一个月的速算扣除数。为什么这么做呢,这样的收的税更多啊,年终也是一个月的收入,凭什么减去12*速算扣除数了?这个霸道(不要脸)的说法,我们只能合理避免的这些跨级的区域了,那具体是那些区域呢?可以参考下面的表格:年终奖一列标红的一对便是盲区的上下线,发放年终奖的数额一定一定要避免这个区域,不然公司多花了钱..._年终奖盲区表

matlab 提取struct结构体中某个字段所有变量的值_matlab读取struct类型数据中的值-程序员宅基地

文章浏览阅读7.5k次,点赞5次,收藏19次。matlab结构体struct字段变量值提取_matlab读取struct类型数据中的值

Android fragment的用法_android reader fragment-程序员宅基地

文章浏览阅读4.8k次。1,什么情况下使用fragment通常用来作为一个activity的用户界面的一部分例如, 一个新闻应用可以在屏幕左侧使用一个fragment来展示一个文章的列表,然后在屏幕右侧使用另一个fragment来展示一篇文章 – 2个fragment并排显示在相同的一个activity中,并且每一个fragment拥有它自己的一套生命周期回调方法,并且处理它们自己的用户输_android reader fragment

FFT of waveIn audio signals-程序员宅基地

文章浏览阅读2.8k次。FFT of waveIn audio signalsBy Aqiruse An article on using the Fast Fourier Transform on audio signals. IntroductionThe Fast Fourier Transform (FFT) allows users to view the spectrum content of _fft of wavein audio signals

随便推点

Awesome Mac:收集的非常全面好用的Mac应用程序、软件以及工具_awesomemac-程序员宅基地

文章浏览阅读5.9k次。https://jaywcjlove.github.io/awesome-mac/ 这个仓库主要是收集非常好用的Mac应用程序、软件以及工具,主要面向开发者和设计师。有这个想法是因为我最近发了一篇较为火爆的涨粉儿微信公众号文章《工具武装的前端开发工程师》,于是建了这么一个仓库,持续更新作为补充,搜集更多好用的软件工具。请Star、Pull Request或者使劲搓它 issu_awesomemac

java前端技术---jquery基础详解_简介java中jquery技术-程序员宅基地

文章浏览阅读616次。一.jquery简介 jQuery是一个快速的,简洁的javaScript库,使用户能更方便地处理HTML documents、events、实现动画效果,并且方便地为网站提供AJAX交互 jQuery 的功能概括1、html 的元素选取2、html的元素操作3、html dom遍历和修改4、js特效和动画效果5、css操作6、html事件操作7、ajax_简介java中jquery技术

Ant Design Table换滚动条的样式_ant design ::-webkit-scrollbar-corner-程序员宅基地

文章浏览阅读1.6w次,点赞5次,收藏19次。我修改的是表格的固定列滚动而产生的滚动条引用Table的组件的css文件中加入下面的样式:.ant-table-body{ &amp;amp;::-webkit-scrollbar { height: 5px; } &amp;amp;::-webkit-scrollbar-thumb { border-radius: 5px; -webkit-box..._ant design ::-webkit-scrollbar-corner

javaWeb毕设分享 健身俱乐部会员管理系统【源码+论文】-程序员宅基地

文章浏览阅读269次。基于JSP的健身俱乐部会员管理系统项目分享:见文末!

论文开题报告怎么写?_开题报告研究难点-程序员宅基地

文章浏览阅读1.8k次,点赞2次,收藏15次。同学们,是不是又到了一年一度写开题报告的时候呀?是不是还在为不知道论文的开题报告怎么写而苦恼?Take it easy!我带着倾尽我所有开题报告写作经验总结出来的最强保姆级开题报告解说来啦,一定让你脱胎换骨,顺利拿下开题报告这个高塔,你确定还不赶快点赞收藏学起来吗?_开题报告研究难点

原生JS 与 VUE获取父级、子级、兄弟节点的方法 及一些DOM对象的获取_获取子节点的路径 vue-程序员宅基地

文章浏览阅读6k次,点赞4次,收藏17次。原生先获取对象var a = document.getElementById("dom");vue先添加ref <div class="" ref="divBox">获取对象let a = this.$refs.divBox获取父、子、兄弟节点方法var b = a.childNodes; 获取a的全部子节点 var c = a.parentNode; 获取a的父节点var d = a.nextSbiling; 获取a的下一个兄弟节点 var e = a.previ_获取子节点的路径 vue

推荐文章

热门文章

相关标签