在 Linux 系统中,进程和环境变量是操作系统的核心组成部分。进程优先级决定了资源分配的先后顺序,直接影响系统性能;而环境变量则提供了一种灵活的方式,用于传递系统配置信息以及控制程序的运行环境。本篇文章将深入探讨进程优先级的概念、调节方法及其原理,以及环境变量的定义、特性和实际应用,帮助读者更好地理解 Linux 系统的核心机制,从而在实际工作中更高效地调试和优化系统。
示例代码:
#include <stdio.h>
#include <unistd.h>
int main() {
while (1) {
printf("I am a process, PID:%d, PPID:%d\n", getpid(), getppid());
sleep(1);
}
return 0;
}
运行步骤:
编译运行程序:
gcc process.c -o process
./process
打开另一个终端,查看进程的优先级:
ps -al | head -1 ; ps -al | grep process
输出字段解析:
PRI(Priority):
NI(Nice 值):
0
。关系公式:
PRI(new) = PRI(old) + nice
举例:
默认情况下(nice=0),PRI 值为 80。
如果 nice=-10,则:
PRI = 80 + (-10) = 70 (优先级提高)
查看进程优先级(top
命令):
使用 top 实时监控系统进程:
top
找到目标进程的 PID
。
修改优先级:
top
中,按下 r
键。PID
。命令行方式修改:
启动新进程并设置 nice 值:
nice -n <nice值> ./process
示例:
nice -n -10 ./process
调整运行中的进程:
renice <nice值> -p <PID>
示例:
renice -5 -p 12345
注意:
task_struct
结构体(即进程控制块)。task_struct
中包含进程的优先级、状态等信息。并发情况下, 操作系统会根据每个进程的时间片进行进程间的切换,只要当前正在被 CPU 调度的进程的时间片到了,无论有没有执行结束,都会被操作系统从 CPU 上拿下来,放到 waiting 数组中去排队,等待 CPU 的下一次调度,我们把这种过程叫做进程基于时间片轮转的调度算法。
两个问题:
1.为什么函数返回值,会被外部拿到呢?
函数调用过程中,栈会保存必要的信息,包括返回地址、参数和局部变量等。
返回值通过寄存器或栈上的特定位置传递给调用者:
2.系统如何得知进程当前执行到哪一行代码了?
CPU 中的程序计数器寄存器 ( PC 或者 eip ) 保存着当前正在执行指令的地址。每次指令执行完毕,程序计数器会自动递增(或根据控制流指令,如 jmp
、call
、ret
等更新),以指向下一条需要执行的指令。
补充知识:寄存器
环境变量是操作系统用于存储系统范围内或用户特定信息的键值对,通常用于配置系统和应用程序运行时的行为。通过设置和读取环境变量,操作系统、开发工具和应用程序能够更灵活地响应不同的运行环境。
上图是 Windows 系统下的环境变量,本质上就是一组键值对,接下来将演示 Linux 系统下常见的环境变量。
./
才能执行?默认搜索路径中没有当前目录:当前目录(.
)通常并不包含在 PATH
环境变量中。这是为了避免安全隐患(防止恶意程序通过伪造指令干扰系统)。
加 ./
的作用:加 ./
明确告诉系统,程序位于当前目录下。例如:
./my_program
表示运行当前目录下名为 my_program
的程序。
ls
、cat
)的本质:它们是存放在系统目录中的可执行程序,通常位于 /usr/bin/
或 /bin/
等目录下。
PATH
环境变量:Linux 系统通过 PATH
环境变量来指定一系列目录,当你在终端输入指令时,系统会按照 PATH
中定义的目录顺序,依次查找该指令对应的可执行程序。
ls
时,系统会在 PATH
的所有目录中搜索 ls
对应的程序(如 /usr/bin/ls
),一旦找到,就直接执行。command not found
。PATH
?1. 查看 PATH
:
echo $PATH
输出示例:
表示系统会依次在 /usr/local/bin
、/usr/bin
、/bin
等目录中查找命令。
2.临时添加路径到 PATH
:
PATH=$PATH:/home/xny/linux/lesson13
/home/xny/linux/lesson13
是当前工作目录。$PATH
表示保留原有的搜索路径,不覆盖。此时我们当前的工作目录也被添加了进去,此时在该目录下的可执行程序在执行的时候就可以不加 ./ 了。
如图所示,mycmd可以直接当作命令使用了。
HOME
?HOME
是 Linux 系统中的一个环境变量,它表示当前用户的主目录路径。主目录是每个用户的个人工作空间,用于存储用户的配置文件、文档、下载文件等。不同的用户拥有各自的主目录,彼此隔离。
HOME
路径:通常位于 /home/username
。HOME
路径:默认为 /root
。HOME
目录。cd
(不带参数)或 cd ~
时,都会切换到 HOME
目录。HOME
环境变量的值echo $HOME
示例输出:
我们可以通过 env
指令查看当前 bash 从操作系统中继承下来的所有环境变量。
除了使用 env
指令,我们还可以通过 getenv
这个系统调用接口来获取某个环境变量的值。
USER
是一个系统环境变量,用来存储当前登录用户的用户名。Linux 系统依赖此变量来标识当前用户,并根据其权限级别(如普通用户或 root 用户)提供不同的访问权限。
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("USER: %s\n", getenv("USER"));
return 0;
}
解释:
getenv("USER")
:
getenv
获取环境变量 USER
的值,即当前登录用户的用户名。打印结果:
不同用户运行此程序时,会得到各自的用户名。例如:
USER: root
或
USER: john
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char user[32];
strcpy(user, getenv("USER"));
if(strcmp("root", user) == 0)
{
printf("root用户,不受权限约束\n");
}
else
{
printf("普通用户,受权限约束\n");
}
printf("USER: %s\n", getenv("USER"));
return 0;
}
功能:
USER
: getenv("USER")
获取当前登录用户名。user
字符数组中。root
用户: strcmp
函数对比用户名是否为 root
。root
,则打印 “root用户,不受权限约束”。运行结果示例:
如果 root 用户运行程序:
root用户,不受权限约束
USER: root
如果普通用户(如 john)运行程序:
普通用户,受权限约束
USER: john
USER
变量与文件的拥有者和权限信息进行对比,判断当前用户是否有权访问或修改文件。r
)、写(w
)、执行(x
)。root
用户,通常不受权限限制。命令行参数是指在运行程序时通过命令行传递给程序的参数。它们使程序可以接受外部输入,从而改变程序的行为或处理不同的数据。
在 C/C++ 中,命令行参数通过 main
函数的参数 argc
和 argv
获取:
argc
(argument count):参数的数量。argv
(argument vector):一个字符串数组,每个元素是一个参数的值。int main(int argc, char *argv[])
argc
:表示参数的个数,包括程序本身的名称。 argc
值为 1。argv
:一个指针数组,存储每个参数的字符串。 argv[0]
:始终是程序的名称(包含路径)。argv[1]
到 argv[argc-1]
:表示传递给程序的实际参数。int main(int argc, char* argv[])
{
int i = 0;
for(; i < argc; i++)
{
printf("argv[%d]->%s\n",i, argv[i]);
}
return 0;
}
运行示例:
命令行参数通过以下方式传递给程序:
./mycode -a -b -c
时,bash
会将这整行输入当作一个字符串。bash
使用空格作为分隔符,将字符串拆分成单独的部分。argv
,其大小传递给 argc
。argv
是一个指针数组,其最后一个元素会自动被设置为 NULL
,用来标识参数结束。这种设计的好处是方便程序在处理参数时快速检测结束标志,而不用依赖 argc
。命令行参数的一个重要作用是为程序、指令或工具提供灵活的选项支持,以便实现不同的功能或行为。
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[])
{
if (argc != 2) { // 参数数量校验
printf("./mycode [-a|-b|-c|-d]\n");
return 0;
}
if (strcmp(argv[1], "-a") == 0) { // 判断第一个参数是否为 "-a"
printf("功能1\n");
} else if (strcmp(argv[1], "-b") == 0) {
printf("功能2\n");
} else if (strcmp(argv[1], "-c") == 0) {
printf("功能3\n");
} else if (strcmp(argv[1], "-d") == 0) {
printf("功能4\n");
} else {
printf("功能5\n");
}
return 0;
}
运行示例:
命令行参数有一个重要的作用,它可以为指令、工具、软件等提供命令行选项的支持。
除了上面提到的 argc
和 argv
两个参数,main 函数还有第三个参数 env
,它也是一个指针数组,存放的是该进程的环境变量。
#include <stdio.h>
int main(int argc, char* argv[], char* env[])
{
int i = 0;
for (; env[i]; i++) {
printf("env[%d] -> %s\n", i, env[i]);
}
return 0;
}
argc
和 argv
)
env
)
argv
数组中。argv
和 argc
获取这些参数,并根据参数执行不同的功能。bash
中运行程序时,bash
作为父进程会将其环境变量传递给程序(子进程)。
设置环境变量 在 Bash 中执行以下命令:
export MY_VALUE=12345678
然后再查看当前的环境变量:
下面执行子进程 ./mycmd,可以发现它里面也有 MY_VALUE 这个环境变量,说明子进程 mycmd 继承了父进程 bash 的环境变量。
正是因为子进程可以继承父进程的环境变量,所以我们在 bash 输入的所有指令都要遵守权限,因为输入的所有指令都可以看做是 bash 的子进程,都继承了 bash 的环境变量。通过下面这条指令可以删除环境变量。
unset MY_VALUE
本地变量:就是在命令行中直接定义的变量。
bash
命令行直接定义的,仅在当前 bash
会话中有效。环境变量:
export
将本地变量转化为环境变量。
查看所有变量:使用 set
查看:
set
echo
能打印本地变量我们可以通过 echo
指令打印出本地变量,之前说过 echo
作为指令,本质上也是一个可执行程序,既然是可执行程序,那就会创建进程,但是我们又说了子进程是无法继承父进程的本地变量,那为什么 echo 可以打印出父进程 bash 的本地变量呢?
本地变量的作用域:仅在当前 bash
会话中有效。
echo
是内建命令:直接由 bash
执行,不创建子进程,因此可以访问 bash
的本地变量。
内建命令与常规命令
bash
中的指令可以分为两类:
ls
:使用 fork
创建子进程,然后执行 execve
加载外部程序。bash
自身直接实现,不会创建子进程。echo
、cd
、export
、set
等。echo 就是一个内建命令,执行 echo 命令的时候并不会创建子进程.
同样的,cd
也是一个内建命令,用于切换当前工作目录。其本质是调用系统接口 chdir()
,直接改变当前进程的工作目录。
除了上面提到的两种通过代码获取环境变量的方法(命令行第三个参数、getenv系统接口)外,还可以通过全局变量 environ
获取
libc
提供的全局变量 environ
,指向环境变量表。extern
显式声明后使用:#include <stdio.h>
int main(int argc, char *argv[])
{
extern char **environ;
int i = 0;
for(; environ[i]; i++)
{
printf("%s\n", environ[i]);
}
return 0;
}
通过对进程优先级和环境变量的深入学习,我们可以发现,它们在 Linux 系统中扮演着不可或缺的角色。进程优先级确保了多任务系统中资源的公平竞争和高效利用,而环境变量为系统和用户程序的交互提供了灵活性和动态配置的能力。掌握它们不仅能帮助我们优化系统性能,还能提升在日常开发和运维中的效率。希望通过本篇文章,能够让您对 Linux 系统的运行机制有更深入的认识,助力您的学习与实践。