【Linux】14.Linux进程概念(3)
文章目录
- 1. 其他概念
- 2. 环境变量
- 2.1 基本概念
- 2.2 常见环境变量
- 2.3 查看环境变量方法
- 2.4 测试PATH
- 2.5 测试HOME
- 2.6 和环境变量相关的命令
- 2.7 环境变量的组织方式
- 2.8 通过代码如何获取环境变量
- 2.9 通过系统调用获取或设置环境变量
- 2.10 环境变量通常是具有全局属性的
- 2.11 常规命令和内建命令
- 2.12 实验
- 2.13 本地变量和环境变量的主要区别
1. 其他概念
竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级
独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发
函数的返回值是怎么被外部拿到的呢?
通过寄存器
系统如何得知我们的进程当前执行到哪行代码的呢?
程序计数器
pc/eip
:记录当前进程正在执行指令的下一行指令的地址。寄存器很多,扮演什么角色?
提高效率,进程的高频数据会放入寄存器中。
CPU
内的寄存器里面保存的是什么数据?进程相关的数据,里面保存的是进程的临时数据,是当前进程的上下文数据。
进程在从 CPU离开的时候,要将自己的上下文的数据保存好,甚至带走。保存到目的是为了未来恢复进程。
进程被切换的时候:
保存上下文数据
恢复上下文数据
2. 环境变量
2.1 基本概念
环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数如:我们在编写
C/C++
代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性
环境变量是系统提供的一组
name=value
形式的变量,不同的环境变量有不同的用户,通常具有全局属性。
2.2 常见环境变量
PATH
: 指定命令的搜索路径
HOME
: 指定用户的主工作目录(即用户登陆到Linux
系统中时,默认的目录)
SHELL
: 当前Shell
,它的值通常是/bin/bash。
2.3 查看环境变量方法
echo $NAME //NAME
:你的环境变量名称
2.4 测试PATH
- 首先创建并编译
hello.c
:
#include <stdio.h>
int main()
{
printf("hello world!\n");
return 0;
}
gcc hello.c -o hello
- 执行对比:
./hello # 使用相对路径执行
hello # 直接执行会提示找不到命令
- 系统命令可以直接执行的原因:
- 系统会在
PATH
环境变量指定的目录中查找可执行文件- 常用命令通常位于
/bin
、/usr/bin
等已经包含在PATH
中的目录- 而当前目录
.
默认不在PATH
中,所以需要指明路径
- 将程序路径添加到
PATH
:
export PATH=$PATH:$(pwd) # pwd获取当前目录的完整路径
- 添加
PATH
后:
hello # 现在可以直接执行了,系统会在PATH中查找
2.5 测试HOME
用
root
和普通用户,分别执行echo $HOME
,对比差异
.
执行cd ~
;pwd
,对应~
和HOME
的关系
- 用不同用户执行
echo $HOME
:
# 普通用户执行
echo $HOME
/home/用户名
# 切换到root用户执行
su root
echo $HOME
/root
- 执行
cd ~
和pwd
:
cd ~ # 切换到主目录
pwd
# 普通用户会显示: /home/用户名
# root用户会显示: /root
~
和HOME
的关系:
~
是HOME
环境变量的简写符号~
会自动展开为当前用户的家目录路径HOME
环境变量存储了用户的家目录路径- 在命令行中,
~
和$HOME
是等价的,例如:cd ~ # 等价于 cd $HOME ls ~/.bashrc # 等价于 ls $HOME/.bashrc
主要区别:
- 普通用户的家目录在
/home/用户名
root
用户的家目录在/root
- 每个用户都有自己独立的
HOME
路径
2.6 和环境变量相关的命令
echo
: 显示某个环境变量值
export
: 设置一个新的环境变量
env
: 显示所有环境变量
unset
: 清除环境变量
set
: 显示本地定义的shell
变量和环境变量
2.7 环境变量的组织方式
每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以’\0’结尾的环境字符串
2.8 通过代码如何获取环境变量
命令行第三个参数
#include <stdio.h>
int main(int argc, char *argv[], char *env[])
{
int i = 0;
for(; env[i]; i++){
printf("%s\n", env[i]);
}
return 0;
}
- 通过main函数第三个参数获取:
int main(int argc, char *argv[], char *env[])
env
是main
函数的第三个参数- 它是一个指向字符串数组的指针
- 数组以
NULL
结尾- 每个字符串的格式为
"NAME=VALUE"
通过第三方变量environ
获取
#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;
}
libc
中定义的全局变量environ
指向环境变量表,environ
没有包含在任何头文件中,所以在使用时 要用extern
声明。
- 通过
environ
变量获取:extern char **environ;
environ
是一个全局变量- 它指向同样的环境变量表
- 需要用
extern
声明是因为:
environ
变量定义在C
运行时库(libc
)中- 它不在任何标准头文件中声明
extern
告诉编译器这个变量在其他地方定义- 链接器会在运行时库中找到这个符号
2.9 通过系统调用获取或设置环境变量
putenv
, 后面讲解
getenv
, 本次讲解
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("%s\n", getenv("PATH"));
return 0;
}
常用getenv
和putenv
函数来访问特定的环境变量。
代码解析:
getenv()
是标准库stdlib.h
中的函数- 参数是要查询的环境变量名称(字符串)
- 返回值是该环境变量的值(字符串),若变量不存在则返回
NULL
- 此例中获取
PATH
环境变量的值并打印
2.10 环境变量通常是具有全局属性的
环境变量通常具有全局属性,可以被子进程继承下去
#include <stdio.h>
#include <stdlib.h>
int main()
{
char * env = getenv("MYENV");
if(env){
printf("%s\n", env);
}
return 0;
}
直接查看,发现没有结果,说明该环境变量根本不存在
导出环境变量
export MYENV="hello world"
再次运行程序,发现结果有了!说明:环境变量是可以被子进程继承下去的!想想为什么?
- 进程创建过程:
父进程(shell)
|
|-- fork() 创建子进程
| |-- 复制父进程的环境变量表
| |-- 复制父进程的地址空间
|
|-- exec() 加载新程序
|-- 保留环境变量表
|-- 替换代码和数据
- 具体原因:
-
fork()
时:- 子进程获得父进程环境变量表的完整副本
- 包括通过
export
设置的MYENV
变量
-
exec()
时:- 虽然子进程的代码和数据被新程序替换
- 但环境变量表被保留下来
- 这是操作系统专门的设计,为了让新程序能够获取环境信息
- 系统调用层面:
fork() -> clone() // 内核复制进程环境表
execve() // 加载新程序,保留环境变量
这种设计的目的:
- 允许父进程向子进程传递信息
- 保持系统配置的一致性
- 提供进程间简单的通信机制
我们所运行的进程,都是子进程,bash
本身在启动的时候,会从操作系统的配置文件中读取环境变量信息,子进程会继承父进程交给我的环境变量。
所以当我们在shell
中export
一个变量后,通过fork-exec
创建的所有子进程都能继承到这个变量。
2.11 常规命令和内建命令
两批命令:
- 常规命令 – 通过创建子进程完成的
- 内建命令 – bash不创建子进程,而是由自己亲自执行,类似于bash调用了自己写的,或者系统提供的函数。
这两类命令的区别:
- 常规命令(外部命令)
- 存在于文件系统中的可执行文件
- 执行时会创建子进程
- 可以通过
which
命令找到其路径 - 执行效率相对较低
- 例如:
ls
,cp
,mv
,vim
等
# 查看命令位置
which ls # 输出: /bin/ls
which cp # 输出: /bin/cp
# 观察进程创建
ps -f # 执行前
ls /tmp # 执行外部命令
ps -f # 可以看到新的进程被创建并结束
- 内建命令(内部命令)
- 内置在 shell 程序中
- 不需要创建子进程
- 使用
type
命令可以确认是否为内建命令 - 执行效率更高
- 例如:
cd
,pwd
,export
,echo
等
# 查看命令类型
type cd # 输出: cd is a shell builtin
type echo # 输出: echo is a shell builtin
# 常用内建命令示例
cd /tmp # 切换目录
pwd # 显示当前目录
export PATH # 设置环境变量
source file # 执行脚本
- 两者的具体区别:
# 环境变量的影响
# 外部命令:子进程继承父进程的环境变量
name="john"
export name
ls # 子进程可以看到 name 变量
# 内建命令:直接在当前 shell 中执行
cd /tmp # 可以直接访问和修改当前 shell 的变量
- 常见的内建命令:
# 作业控制
bg # 后台运行
fg # 前台运行
jobs # 显示作业状态
# shell 变量操作
export # 设置环境变量
set # 设置 shell 变量
unset # 删除变量
# 目录操作
cd # 改变目录
pwd # 显示当前目录
dirs # 显示目录栈
# 其他常用内建命令
alias # 设置命令别名
history # 显示命令历史
source # 执行脚本
exit # 退出 shell
echo # 显示文本
test # 条件测试
[ # 条件测试的另一种形式
- 查看命令类型的方法:
# 使用 type 命令
type -a cd # 显示 cd 命令的所有可能位置
type -a echo # 有些命令既有内建版本也有外部版本
type -a ls # 显示是外部命令
# 使用 which 命令(只对外部命令有效)
which ls # 显示外部命令的路径
which cd # 内建命令则找不到路径
- 性能对比示例:
# 测试内建命令执行时间
time for i in {1..1000}; do echo "test" > /dev/null; done
# 测试外部命令执行时间
time for i in {1..1000}; do /bin/echo "test" > /dev/null; done
# 内建命令通常会快很多
重要说明:
-
内建命令的优势:
- 执行效率高(无需创建子进程)
- 可以直接修改 shell 环境
- 启动速度快
-
外部命令的特点:
- 功能通常更专一和强大
- 需要额外的进程创建和环境设置
- 可以被替换或更新
-
实际应用建议:
- 对于频繁使用的简单操作,优先使用内建命令
- 需要复杂功能时,使用专门的外部命令
- 在脚本中,注意命令类型对性能的影响
2.12 实验
如果只进行 MYENV=“helloworld” ,不调用export导出,在用我们的程序查看,会有什么结果?为什么?
如果只执行
MYENV="helloworld"
而不使用 export,程序将查看不到这个变量。
- Shell中变量的两种类型:
-
本地变量(局部变量)
- 仅在当前shell中有效
- 通过
MYENV="helloworld"
设置 - 不会被子进程继承
-
环境变量
- 可以被子进程继承
- 通过
export MYENV="helloworld"
设置 - 或
declare -x MYENV="helloworld"
设置
- 执行过程对比:
# 设置本地变量
MYENV="helloworld"
./a.out # 程序输出为空
# 设置环境变量
export MYENV="helloworld"
./a.out # 程序输出 helloworld
- 原理说明:
Shell进程
|-- MYENV="helloworld" (仅在shell进程中可见)
|-- fork()创建子进程
|-- 只继承环境变量,不继承本地变量
|-- exec()执行新程序
|-- getenv()找不到MYENV变量
所以不使用export时,MYENV只是shell的本地变量,不会被放入环境变量表中,因此子进程无法通过getenv()获取到这个变量。
2.13 本地变量和环境变量的主要区别
本地变量:
- 仅在当前会话/脚本内有效
- 作用域局限于定义它的 shell 或脚本
- 子进程无法继承
- 通常用
变量名=值
的形式定义
环境变量:
- 对当前shell及其所有子进程都有效
- 可被子进程继承
- 使用
export
命令设置- 常用于配置系统环境
示例:
# 本地变量
name="john"
echo $name
# 环境变量
export PATH="/usr/local/bin:$PATH"
export JAVA_HOME="/usr/lib/jvm/java-8-openjdk"