✨个人主页: 熬夜学编程的小林
💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】
目录
1、进程优先级
2.1、什么是优先级
2.2、优先级的描述
2.3、优先级与权限的关系
2.4、为什么要有优先级
2.5、Linux优先级的特点
2.6、其他概念
2、命令行参数
3、环境变量
3.1、基本概念
3.2、PATH环境变量
3.3、其他环境变量
1、进程优先级
2.1、什么是优先级
指定进程获取某种资源(CPU)的先后顺序。
2.2、优先级的描述
进程优先级的描述跟进程状态描述类似,实质是在task_struct(内存控制块)结构体内部有一个描述优先级的属性,通过数字表示先后顺序。Linux 中优先级数字越小,优先级越高。
2.3、优先级与权限的关系
权限决定能不能获取资源。
优先级决定获取资源的顺序,已经能获取资源。
2.4、为什么要有优先级
进程访问的资源(CPU)始终是有限的。系统中进程大部分情况都是比较多的。
操作系统关于调度和优先级的原则:分时操作系统,基于时间片进行调度,保证基本的公平。如果进程因为长时间不被调度,就造成了饥饿问题。
2.5、Linux优先级的特点
在讲解优先级特点之前,我们先通过一个C语言程序查看优先级。
C语言代码
#include<stdio.h>
#include<unistd.h>
int main()
{
while(1)
{
printf("I am a process,pid:%d\n",getpid());
}
return 0;
}
命令行代码
ps -al // 查看所有进程信息
测试结果
我们很容易注意到其中的几个重要信息,有下:
UID : 代表执行者的身份。
PID : 代表这个进程的代号。
PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号。
PRI :代表这个进程可被执行的优先级,其值越小越早被执行。
NI :代表这个进程优先级的修正数据,nice值,新的优先级 = 默认优先级 + nice,达到对于进程优先级动态修改的过程。
调整优先级
nice/renice 调整优先级
此处博主使用top命令调整优先级。
- 输入top命令
- 进入top后按“r”–>输入进程PID–>输入nice值
测试一
我们将nice值改为100,现象如下:
可以看到 NI 只修改为了19,PRI修改为了99。表名nice值并不能任意调整,而是有范围的。 范围是 [-20,19] ,共40个数字。
测试二
我们将nice值改为-10,现象如下:
我们可以看到,当我们第二次修改nice值时,不允许我们修改,因为修改优先级是有风险的,如果强制需要修改nice值,我们只需要切换成root账号即可。
测试三
使用root账号将nice值改为-10,现象如下:
我们可以看到NI修改成了-10,PRI修改成了70,我们刚刚的PRI是99,将nice值修改为-10,为什么现在的PRI为70了呢?原因是 每次调整优先级都是从80开始的。新的优先级 = 默认优先级 + nice,70 = 80 - 10。
测试四
使用root账号将nice值改为-100,现象如下:
可以看到NI被修改为-20,因此能够证明nice的最小值为-20,且新的优先级 = 默认优先级 + nice,60 = 80 - 20。
2.6、其他概念
- 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级。
- 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰。
- 并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行。
- 并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发。
2、命令行参数
写C语言代码中,main函数也是函数,可以带参数?
答案是可以带参数也可以不带参数,下面就使用带参数的main函数演示。
代码演示
#include<stdio.h>
// 命令行参数测试
int main(int argc,char* argv[])
{
int i = 0;
for(i=0;i<argc;i++)
{
printf("argv[%d] = %s\n",i,argv[i]);
}
return 0;
}
测试结果
通过上图现象我们可以看到argc是元素个数,argv是一个边长数组,猜测数组以NULL结尾。
验证数组以NULL结尾
#include<stdio.h>
int main(int argc,char* argv[])
{
int i = 0;
// argv[i]为假则循环结束
for(i=0;argv[i];i++)
{
printf("argv[%d] = %s\n",i,argv[i]);
}
return 0;
}
测试结果
为什么要有命令行参数?
本质:命令行参数本质是交给我们程序的不同选项,用来定制不同的程序功能。命令中会携带很多选项。
命令行中启动的程序是谁干的?
命令行中启动的程序,都会变成进程,且都是bash的子进程,因此是bash(命令行解释器)干的。
代码演示一
#include<stdio.h>
#include<unistd.h>
int g_val = 10000;
int main()
{
printf("I am parent process,g_val:%d,pid:%d,ppid:%d\n",g_val,getpid(),getppid());
sleep(5);
pid_t id = fork();
if(id == 0)
{
while(1)
{
printf("I am child process,g_val:%d,pid:%d,ppid:%d\n",g_val,getpid(),getppid());
sleep(1);
}
}
else
{
printf("I am parent process,g_val:%d,pid:%d,ppid:%d\n",g_val,getpid(),getppid());
sleep(1);
}
return 0;
}
父进程的数据,默认能被子进程看到并访问。
代码演示二
#include<stdio.h>
#include<unistd.h>
#include<string.h>
int g_val = 10000;
int main(int argc,char* argv[])
{
printf("I am parent process,g_val:%d,pid:%d,ppid:%d\n",g_val,getpid(),getppid());
if(argc != 2)
{
printf("Usage: %s -[a,b,c,d]\n", argv[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");
}
return 0;
}
测试结果
命令行中启动的程序,都会变成进程,且都是bash的子进程。启动程序,默认是输入给父进程bash(命令行解释器)的!!!
3、环境变量
3.1、基本概念
- 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数。
- 如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
- 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性。
3.2、PATH环境变量
基本认知
执行命令和运行自己写的程序是没有区别的,且Linux中70% 的命令是C语言写的。
为什么执行ls这些系统提供的命令可以不带路径,而我们自己写的C语言程序需要加./(带路径)呢?
Linux系统重存在一些全局的设置,表明告诉命令行解释器应该去哪个路径下去寻找可执行程序,当执行ls命令(可执行程序在/usr/bin目录中)时,会先去找ls对应的可执行程序,默认去PATH中找。
查看PATH内容
通过echo $PATH 获取PATH(环境变量)内容 ,类似*p。
补充:
- 系统中很多的配置,在我们登录Linux系统的时候,已经被加载到了bash进程中(内存)。
- bash在执行命令的时候,需要先找到路径(默认去PATH中找),因为未来要加载。
如果我们想像系统指令一样执行自己写的可执行程序,怎么做?
方式一(粗暴):直接将我们的可执行程序拷贝到PATH的其中一个路径(例如/usr/bin)中。
注意:拷贝内容到PATH环境中需要配root权限,此处使用sudo提权。
sudo cp myprocess /usr/bin # 此处的/usr/bin也可以是PATH中的其他路径
不建议使用这种方式,因此下面我们将该可执行程序在/usr/bin中的内容删除。
sudo rm /usr/bin/myprocess
方式二(温柔):把可执行程序路径增加到PATH环境变量中。
错误示范:
PATH=/home/jkl/path # 将可执行程序的当前目录赋值给PATH变量,会直接覆盖PATH
环境变量直接被我们新的路径覆盖了怎么办呢?
其实很简单,直接将系统关闭,重进系统就可以了。因为我们上面说了 系统中很多的配置,在我们登录Linux系统的时候,已经被加载到了bash进程中(内存)。
我们重新登录系统之后,发现环境变量回来了,且不能直接执行我们写的可执行程序了。
正确示范:
PATH=$PATH:/home/jkl/path # 将原本的PATH路径以及我们可执行程序当前目录赋值给PATH
演示结果
PATH环境变量是Linux系统中搜索可执行程序的默认路径,也是which指令搜索路径的默认路径。
PATH环境变量的路径是内存级别的,重新登录系统又会变成默认路径,怎样让追加的环境变量路径永久存在呢?
最开始的环境变量不是在内存中,而是在系统对应的配置文件中,在我们登录系统时,会创建一个bash进程,bash进程会读取配置文件,然后把配置文件的环境变量(包括PATH)在自己的bash进程拷贝一份。
这个配置文件在哪里?
vim .bash_profile
vim .bashrc
vim /etc/bashrc
我们的PATH配置文件可能在.bash_profile或者.bashrc中。博主的在.bashrc中。
想要登录时每次都默认执行自己写的可执行程序,把可执行程序路径加到环境变量的配置文件里面即可,如下图:
加上该路径之后,myprocess可以像系统命令(ls)一样,在任意目录中使用了,且无需加./。
Windows也有环境变量,我们在安装jdk或者python时一般需要配置环境变量。
3.3、其他环境变量
env : 查看系统所有的环境变量
history :查看历史命令
HOME : 家目录所在的环境变量
PWD : 当前目录路径变化
SHELL : 当前Shell,它的值通常是/bin/bash。
HISTSIZE : 历史命令个数 上翻下翻 Linux默认会记录最新的1000条命令
自己定义一个环境变量:
export THIS_IS_MY_ENV=hellolinux 导入环境变量(不加export,依旧存在变量,本地变量)
unset THIS_IS_MY_ENV 取消环境变量
export测试
疑问:export 也是一个命令,命令就会创建子进程,子进程就应该不被bash看到,为什么却能将变量导入到环境变量中。?
80% 命令都是bash创建子进程执行的 ,称为普通命令。还有20%命令,如(export / echo) 由bash亲自执行的,称为内建命令。
怎么证明真的有内建命令呢?
通过上面实验,我们可以证明确实有内建命令。
普通测试
- 本地变量只在bash内部有效,无法被子进程继承下去。导成环境变量才能被获取。
- 用本地变量理解内建命令。内建命令可以查到本地变量。
- echo能够打印本地变量(子进程无法获取)也能够证明echo是内建命令。
注意:已经存在的本地变量导成环境变量,只需要使用export 变量名即可。
能否通过C语言程序查看环境变量呢?
答案是可以的,在写程序之前我们需要查一下手册,man environ,environ为查看全局的指针变量。
C语言程序
#include<stdio.h>
#include<unistd.h>
int main()
{
extern char** environ;// 声明外部文件变量
int i=0;
for(i=0;environ[i];i++)// environ[i]为假则循环结束
{
printf("env[%d]->%s\n",i,environ[i]);
}
return 0;
}
测试结果
测试结果与使用命令env的结果是一样的。 说明环境变量默认也是可以被bash子进程拿到的。环境变量们,默认在bash内部。
环境变量有很多,bash内部是如何组织的?
通过上面的C语言代码我们大概可以推断,环境变量的组织与命令行参数类似,是通过一个变长数组组织的,最后一个数据以NULL结尾。
main函数带参的C语言程序
#include<stdio.h>
#include<unistd.h>
int main(int argc,char* argv[],char* env[])
{
int i = 0;
for(i=0;env[i];i++)
{
printf("env[%d]->%s\n",i,env[i]);
}
return 0;
}
测试结果
bash进程启动的时候,默认会给子进程形成argv[]命令行参数表(用户输入的命令行来),env[]环境变量表(从OS的配置文件来),bash通过各种方式交给子进程。
补充:
- 导环境变量的本质:把字符串添加到环境变量表中。
- 环境变量具有系统级的全局属性,因为环境变量会被子进程继承下去。
获取环境变量方法:
- extern char** environ;
- main函数参数
- getenv("path");
man getenv
代码
#include<stdio.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("psth:%s\n",path);
return 0;
}
测试结果
putenv(); // 创建环境变量