首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【Linux】进程调度算法、进程切换、环境变量

【Linux】进程调度算法、进程切换、环境变量

作者头像
用户11872857
发布2025-12-17 16:27:04
发布2025-12-17 16:27:04
1730
举报
  • 注意:程序名本身会被算作第 1 个参数

char* argv[]

  • 全称argument vector(参数向量 / 数组)
  • 作用:是一个字符串数组,存储了终端传入的每一个命令行参数
    • argv[0]:默认是程序本身的路径 / 名称
    • argv[1]:第 1 个用户传入的参数;
    • argv[2]:第 2 个用户传入的参数;
    • …… 以此类推,最多有argc个元素。

在 C 语言的char* argv[]数组中(该数组也被称为 “命令行参数表”),无论参数数量是多少,argv的最后一个有效元素的下一个位置(即argv[argc])会被系统自动初始化为 NULL。

在执行命令./test a b c时,这些参数会按顺序存入**char* argv[]**这个字符指针数组,具体对应关系是:

  • argv[0] 存储第一个参数:"./test"(程序本身的路径 / 名称)
  • argv[1] 存储第二个参数:"a"
  • argv[2] 存储第三个参数:"b"
  • argv[3] 存储第四个参数:"c"

(补充说明:终端中参数之间的空格是分隔符,所以./test a b c会被拆分成 4 个独立的字符串,依次填入argv数组的对应位置)

既然 main 函数可以接收 argc 和 argv 参数,这就直接证明 main 函数并非程序运行的 “原生起点”—— 程序启动时,操作系统先调用启动函数(如**Startup()**或**CRTStartup()**,不同编译环境命名略有差异),由这些启动函数完成程序运行前的基础初始化(比如内存分配、运行环境配置),最终再由启动函数主动调用 main 函数,并将命令行参数封装为 argc 和 argv 传递给它。

简言之:操作系统 → Startup/CRTStartup(启动函数) → main函数(接收参数执行),main 函数的参数正是这一调用链的直接体现。


Q:为什么要让 main 函数支持命令行参数(argc/argv)?

核心是为指令、工具、软件等提供命令行选项的支持—— 通过在执行程序时传入不同参数,能让同一个程序实现不同功能。比如ls -lls是程序,-l是命令行参数),传入-l就能让ls以详细列表形式显示文件,不传则是默认格式,灵活适配不同使用场景。

如果想实现多选项,可以用循环遍历所有参数 + 逐个匹配判断,示例代码:

代码语言:javascript
复制
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[])
{
    // 1. 无参数时打印用法提示(argc=1 表示只有程序名)
    if (argc == 1)
    {
        printf("Usage: %s -[a|b|c|d] [多个参数可叠加]\n", argv[0]);
        printf("示例: %s -a -b -c\n", argv[0]);
        return 1; // 退出程序,避免后续逻辑执行
    }

    // 2. 遍历所有传入的参数(i从1开始,跳过argv[0]程序名)
    for (int i = 1; i < argc; i++)
    {
        // 逐个判断参数,匹配则执行对应功能
        if (strcmp(argv[i], "-a") == 0)
        {
            printf("执行功能1(参数-a)\n");
        }
        else if (strcmp(argv[i], "-b") == 0)
        {
            printf("执行功能2(参数-b)\n");
        }
        else if (strcmp(argv[i], "-c") == 0)
        {
            printf("执行功能3(参数-c)\n");
        }
        else if (strcmp(argv[i], "-d") == 0)
        {
            printf("执行功能4(参数-d)\n");
        }
        else
        {
            // 3. 识别到非法参数时提示,并继续处理其他参数
            printf("警告:无效参数 %s,仅支持 -a/-b/-c/-d\n", argv[i]);
        }
    }

    return 0;
}
C:环境变量参数

实际上,我们也可以通过main函数的环境变量参数(如char *envp[]),获取当前程序所在 bash 进程的所有环境变量。

envpargv是同类型的字符指针数组(都属于 “参数向量表”),envp对应的是环境变量表;由于环境变量的数量不固定,所以需要通过envp末尾的NULL指针来判断遍历的结束。

Ⅲ、如何获取和设置环境变量?

G:如何获取环境变量?

前面已经讲了 3 种获取环境变量的方式:

  1. 终端中通过echo命令(如echo $PATH),直接打印单个环境变量的值;
  2. 终端中通过env命令,查看所有环境变量的列表;
  3. C 程序中通过main函数的环境变量参数(如char *envp[]),在代码中获取环境变量。

接下来我再讲解一种通过C语言接口获取环境变量的方法。

S:如何设置环境变量?
1、临时添加环境变量(仅当前终端会话生效)

基本语法

代码语言:javascript
复制
export 环境变量名=值

示例:

说明

  • export的作用是将变量标记为 “环境变量”,使子进程可以继承该变量;
  • 关闭终端后,该环境变量会失效。
2、永久添加环境变量(所有终端会话生效)

需将设置指令写入 Shell 配置文件(以 Bash 为例):

编辑配置文件

代码语言:javascript
复制
echo 'export 环境变量名=值' >> ~/.bashrc

示例:

代码语言:javascript
复制
echo 'export MYENV="hello world"' >> ~/.bashrc

当前终端进程想用新配置 → 必须执行 source ~/.bashrc(相当于 “给当前 Shell‘刷新内存’”);

不想执行 source → 直接关闭当前终端,重新打开新终端即可(新终端启动时会自动读取修改后的~/.bashrc)。

说明

  • 不同 Shell 对应的配置文件不同(如 Zsh 对应~/.zshrc);
  • 配置文件修改后,新启动的终端会自动加载该环境变量。

3、临时取消(仅当前终端生效,关闭终端即失效)

核心命令:unset

代码语言:javascript
复制
# 取消单个环境变量(比如你设置的MYENV)
unset MYENV

# 验证是否取消:输出为空则成功
echo $MYENV

补充:清空 PATH(慎用!)

如果想临时清空某个环境变量的值(而非彻底取消),可以直接赋值为空:

代码语言:javascript
复制
# 仅清空值,变量仍存在(不推荐,容易踩坑)
MYENV=""
# 验证:输出为空,但变量还在
env | grep MYENV  # 仍能看到MYENV=
4、永久取消(所有终端生效,需删除配置文件中的定义)

针对你之前写到~/.bashrc里的export MYENV="hello world",操作步骤:

编辑配置文件,删除环境变量定义:

代码语言:javascript
复制
# 用vim打开~/.bashrc
vim ~/.bashrc

# 操作步骤:
# 1. 按G跳到文件最后一行
# 2. 找到你添加的export MYENV="hello world"这一行
# 3. 按dd删除该行(或在行首加#注释掉)
# 4. 按ESC → 输入:wq 保存退出

让修改生效:

代码语言:javascript
复制
# 刷新当前终端的配置(立即永久取消,不想用该指令重新打开终端也会生效)
source ~/.bashrc

# 验证:无论当前终端/新终端,echo $MYENV 都为空
echo $MYENV  # 输出空

Ⅳ、环境变量的继承性 + 全局属性 +本地变量

1、继承性

父进程(Shell)通过export MYENV=123456设置环境变量后,子进程(./test程序)能通过getenv继承并读取该值(运行./test输出 “获取到的 MYENV 值:123456”);

2、全局属性

环境变量的 “全局” 是父→子进程链条内的单向共享:子进程会继承父进程的环境变量,但子进程修改的是独立副本,不影响父进程。

子进程会继承父进程的环境变量:


子进程对环境变量的修改不影响父进程:

Q:为啥子进程修改环境变量不影响父进程?

这是因为进程的环境变量是 “复制继承” 而非 “共享”,底层逻辑是:当父进程创建子进程时,会把自己的环境变量完整复制一份给子进程 —— 子进程拿到的是 “副本”,不是和父进程共享同一份数据。所以:

  • 子进程修改的是 “自己的副本”,不会动到父进程的原始数据;
  • 就像你复制了一份文件,修改副本不会影响原文件,是一个道理。
3、本地变量

本地变量是仅在当前 Bash 进程内生效的变量(不加export的变量),不会被 Bash 的子进程继承。

证明本地变量不会被继承:

4、内建命令和常规命令

之前我们认为 “所有命令都会以 Bash 进程的子进程形式执行”,这个说法其实不准确。实际使用中,Shell 命令可以分为两类:常规命令内建命令

1. 常规命令 常规命令的执行,是通过创建子进程来完成的—— 这些命令是独立的程序,Bash 会启动一个新的子进程,让子进程去运行这个程序,程序跑完子进程就结束。 2. 内建命令 内建命令的执行,Bash 不会创建子进程,而是由 Bash 自己直接执行 —— 相当于 Bash 调用了自己内置的功能(类似 Bash 自己写好的函数),不用额外开新进程。

执行./test(常规命令)时,Bash 会创建子进程,但父进程的本地变量不会传给子进程,所以./test读不到MYENV

echo是内建命令,执行时 Bash 不会开子进程,直接在自身进程内运行,因此能访问到父进程的本地变量,所以echo $MYENV能输出123


【模拟cd内建指令】

在 Shell 中,cd是内建指令,它的底层是通过chdir系统调用函数实现的。

chdir**函数**

  • 函数原型int chdir(const char *path);
  • 功能:用于修改当前进程的工作目录。
  • 参数**path**:字符串形式的目标路径(支持绝对路径 / 相对路径)。
  • 返回值
    • 成功:返回0
    • 失败:返回-1,同时会设置errno标识错误原因
代码语言:javascript
复制
#include <unistd.h>
#include <stdio.h>

int main(int argc, char *argv[], char *envp[])
{
    sleep(20); // 延迟,方便观察进程状态
    printf("change begin\n");
    if (argc == 2)  // 接收命令行参数作为目标路径
    {
        chdir(argv[1]); // 调用chdir修改当前进程的工作目录
    }
    sleep(20);
    return 0;
}

第一次(change begin前):指向的是原目录/home/lih/code-under-linux/Test-course

第二次(change begin后):指向的是目标目录/home/lih—— 说明chdir确实成功修改了子进程的工作目录!

ls -ld /proc/[进程ID]/cwd 作用:查看cwd(进程工作目录的软链接)自身信息

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=4u7xbz032vy

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-12-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、进程调度
    • Ⅰ、进程调度概念
    • Ⅱ、位图(bitmap)中位的定位与状态判断
    • Ⅲ、进程调度流程
  • 二、进程切换
    • Ⅰ、进程切换概念
    • Ⅱ、进程上下文是什么?
    • Ⅲ、cpu中的寄存器是什么?
      • 【问题】:为什么函数返回值会被外部拿到?
      • 【问题】:系统如何知道进程当前执行到哪行代码?
    • Ⅳ、如何进行进程切换?
      • 【小故事】:学生当兵
      • 【进程切换核心步骤】
  • 三、环境变量
    • Ⅰ、环境变量概念
      • 问题 1:为什么系统指令不用加./就能直接执行,自己写的指令却需要?
      • 问题 2:如何查看环境变量?
      • 问题 3:如何让自己写的指令不用加./就能执行?
      • 问题 4:不小心覆盖了系统指令的默认搜索路径,怎么恢复?
      • 问题 5:为什么登录系统后会直接进入家目录?
      • 问题 6:echo $SHELL输出/bin/bash是什么意思?
      • 问题 7:$SHELL的核心作用是什么?
      • env命令
      • 【核心环境变量表格】
      • 环境变量如何被组织?
    • Ⅱ、命令行参数
      • Q:为什么要让 main 函数支持命令行参数(argc/argv)?
      • C:环境变量参数
    • Ⅲ、如何获取和设置环境变量?
      • G:如何获取环境变量?
      • S:如何设置环境变量?
      • 1、临时添加环境变量(仅当前终端会话生效)
      • 2、永久添加环境变量(所有终端会话生效)
      • 3、临时取消(仅当前终端生效,关闭终端即失效)
      • 4、永久取消(所有终端生效,需删除配置文件中的定义)
    • Ⅳ、环境变量的继承性 + 全局属性 +本地变量
      • 1、继承性
      • 2、全局属性
      • Q:为啥子进程修改环境变量不影响父进程?
      • 3、本地变量
      • 4、内建命令和常规命令
      • 【模拟cd内建指令】
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档