Linux下进程链接结构,命令行参数,环境变量

bash 是一种 shell。在 Linux 系统中,当我们在终端输入命令时,通常是在一个 shell 环境下进行的。如果这个 shell 是 bash,那么所有命令行执行的命令都是 bash 的子进程。

1.Linux下进程链接结构

进程链接补充知识:

  • 所有进程都要以链表方式链接
  • 进程可以在调度队列里,也可以在阻塞队列里等等

Linux中链式结构:双链表结构

C语言中双链表:

如果按照C语言设定的链表结构,然后把进程属性与链接字段放在一起,这样就会显得不优雅,过于臃肿,更重要的是可扩展性小。

所以Linux设计模式,只有链接字段,没有属性字段。

结构体中,存放两个指针,前驱指针,与后驱指针,用来表示双链表,然后进程PCB中管理一个该结构体成员,可以通过该结构体成员,使用了node结构体的双向链表进而来链接其他进程,管理起来。

Linux采用了内部数据结构,属性在数据结构外部的方法,从而对进程进行管理链接。

那么意义是什么呢?

如图:

说明了,一个进程,既可以在全局链表中,又可以在任何一个其他数据结构中,只需要加相关数据结构节点字段即可!!!

一个进程如果在运行队列就链接到queuenode中,在等待队列中就链接到waitnode中!!!进程什么状态要做什么就链接到相关字段即可!!!

原理是什么呢?如果要知道进程属性怎么办呢?知道next,prev只能找到下一个进程的link地址,该怎么找到其进程相关属性呢?

在C语言中学过:

如上图,在一个结构体中,如果只知道变量c的地址该如何找到该数据结构的起始地址呢?

我们只要知道c相对于起始地址的偏移量,就可以知道数据结构起始地址,通过c的地址减去偏移量,然后对其结果进行强转一下就可以知道,起始地址。

那么关键是怎么求偏移量呢?我们把其实地址当作0来看,对0进行强转,然后指向c变量,然后对其取地址就可以知道偏移量是多少,然后再计算即可,

在C语言中有一个宏offset就是用来求偏移量的,和上面原理一样,知道偏移量就可以求出数据结构起始地址。

所以,当我们知道进程中link地址时,只要能得出偏移量就可以算出struct task_struct的初始地址,然后就可以直接访问进程数据了。

2.命令行参数

在main函数中,存在两个参数,argc 与 argv[]。argc指的是参数个数,argv指的是参数清单

让我们对上述代码打印看看

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
    printf("argc: %d\n", argc);
    for (int i = 0; i < argc; i++)
    {
        printf("argv[%d]: %s\n", i, argv[i]);
    }
    return 0;
}

结果如下:

在命令行上只输入可执行程序,结果是参数个数为1,参数清单有个刚才输入的字符串。在多输入几次观看发现,argc的参数个数就是在命令行上输入以空格为分隔符的字符串个数,acgv[]中就是这些字符串。

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
    printf("argc: %d\n", argc);
    for (int i = 0; argv[i]; i++)
    {
        printf("argv[%d]: %s\n", i, argv[i]);
    }
    return 0;
}

更改上述代码用argv[i]来进行控制,依然运行结果一样,说明argv[]最后一个为NULL,所以循环才会结束。

为什么要有这两个参数呢?

同一个程序,可以根据选项的不同,来表现出不同的功能!!!比如:指令中选项的实现!

如下代码:

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        printf("Usage: code opt\n");
        return 1;
    }
    if (strcmp(argv[1], "-opt1") == 0)
        printf("功能1\n");
    else if (strcmp(argv[1], "-opt2") == 0)
        printf("功能2\n");
    else if (strcmp(argv[1], "-opt3") == 0)
        printf("功能3\n");
    else
        printf("默认功能\n");
    return 0;
}

指令的本质就是可执行程序,那么指令后面的选项是怎么被可执行程序拿到的呢?

主要原因是:这个可执行程序,他所对应的命令行参数,最终会被解析出来,然后传递给这个程序,所以程序就拿到了对应的选项,目标程序内部再对选项进行判断,根据选项的不同,让程序表现出不同的风格。

以前学习过程中写的程序很单一,不需要选项,而Linux中指令程序要通过选项进行操作。

main函数的参数是谁给传递的呢?

在命令行解释器上进行输入指令加选项,他是字符串,首先会被shell拿到,根据空格为分隔符进行打散,形成一张表argv[]和元素个数argc。

在命令行上启动的程序,父进程都是谁? shell!!!启动的进程与shell都是父子关系,因为子进程会继承父进程,对于只读数据,不做修改,那么子进程没必要拷贝,直接可以跟父进程共享,所以,命令行参数被打散形成的表,后面就不会被修改,所以子进程也可以看到。

也就是说,最终在命令行上所作的解析,形式的argv这一张表,和argc这样的东西,因为一旦解析打散,就不会修改,所以对于子进程来将,子进程也可以看见这张表和argc,即可执行程序也可以看见。

所以程序在运行时,未来子进程会把从shell继承下来的argv与argc,以参数的形式传递给我们进程,所以我们的程序里,就读到shell命令行解析后对应的选项!!

也就是说,命令行参数,他的打散和形成的过程是由shell,我们的父进程来执行的,然后我们自己的程序是子进程,他是shell以fork的形式创建出来的,shell形成全局的argc , argv,打散后也不修改了,所以子进程也可以读到,然后子进程把他们传递给main函数就可以了

那么谁调用main函数呢?

那么main函数是自己程序代码的第一个函数,入口函数,那么main也是一个函数,函数被定义出来就是要被调用的,那么谁调用main函数呢?

main函数是我们自己写的程序的入口,但不一定是系统调度我这个程序时直接从main函数开始,

CRTstartup指的是 C 运行时库的启动代码。当一个 C 程序开始执行时,首先执行的不是main函数,而是CRTstartup代码。

就相当于如下:

所以编译器,操作系统,加载器不是互相割裂的,彼此之间是有关系的。

3.环境变量

main函数里可以不带参数,也可以只带两个参数,或者可以带三个参数,第三个参数就是

char *env[]----->环境变量。

shell不光形成命令行参数表,还要形成一个环境变量的表env。

1.看一看环境变量

int main(int argc, char *argv[], char *env[])
{
    for(int i = 0;env[i];i++)
    {
        printf("env[%d]: %s\n",i,env[i]);
    }
    return 0;
}

环境变量的格式就是 key=value。以key,value方式构建的,具有“全局”属性的变量叫做环境变量。

2.见一见环境变量:PATH

见一见环境变量----感受一个环境变量的特点。

指令env:查看环境变量。

PATH环境变量:指定命令的搜索路径。

echo $PATH查看PATH环境变量,

为什么我们执行自己的程序要带  ./ 。不带就会报错,找不到。

执行系统命令时,为什么不需要呢?比如:ls pwd。

实际上系统查指令会去 /usr/bin/ 下查找,我们自己的程序不在该路径下,所以找不到,必须带 ./

为什么系统知道命令在/usr/bin/下?能修改让他认识我的路径吗?

shell登录时,他就有对应的环境变量PATH,告诉shell,应该去哪个路径下去查指令,

查对应环境变量指令:echo $()

PATH环境变量里面的内容,就是当shell运行任何一个命令行命令,他首先要查,shell会自动去以冒号为分隔符的多个子路径下去查,所以路径都查了,如果没有就会报错,找到了就会把该程序加载并执行。

PATH---->路径集合---->系统可执行文件的搜索集合。

自己所写的可执行程序不在这些路径下,所以报错。

如果不想带路径,让我们程序直接运行起来:

  1. 把可执行程序拷贝到/usr/bin/下(cp /usr/bin/  done)
  2. 把该可执行程序的路径添加到PATH中去

通过在命令行上直接输入: PATH = 可执行程序路径。

这样就可以让自己程序直接运行起来,不需要加 ./ 。但是这样其他指令就不能运行,比如:ls

此时,我们查看PATH变量内容:

直接就把直接路径全部给覆盖了,所以,系统命令都不能使用运行。这里只要关闭重新登陆就可以恢复

因为环境变量是内存级的,是被加载到bash进程内,子进程继承环境变量,子进程修改不会改变父进程内容,关闭重新登陆shell,子进程就可以重新继承bash里面环境变量。

PATH环境变量从哪里来

环境变量,开始都在系统配置文件中。

我们登陆shell时,启动一个shell进程,shell读取用户和系统相关的环境变量的配置文件,形成shell自己的环境变量表,然后被子进程继承拿到环境变量PATH。

要想永久的更改PATH,先要找到配置文件,在家目录就会存在两个配置文件 .bash_profire和 .bashrc 。

只要把自己可执行程序路径放入配置文件中,在配置文件中进行修改路径,这样就可以直接像指令一样使用自己的程序!!!

指令source:在shell环境中,用于在当前环境中加载和执行脚本或配置文件。

补充知识:

进程内部都会有记录是谁启动的进程的UID,那么,你在启动进程的时候,系统怎么知道你是谁?并且把你的UID写到PCB中?

环境变量中有,所以在登陆的时候,把你自己是谁写到环境变量里,bash早就知道我这个bash是给哪一个用户提供服务的,然后启动创建进程,用户名就知道了。

3.见一见多的环境更变量

1.HOME----->当前用户对应的家目录

那么是默认就在家目录里,再设置的环境变量,还是默认先读环境变量,再把当前用户设置再家目录下?

当我们在登陆的时候,系统首先给我们用户创建对应的bash来给我们做准备,bash读取环境变量相关的配置文件,读取配置文件的本质就是要配置他自己的环境变量,bash也是一个进程,也要有cwd

bash的cwd设置为我们读到的HOME环境变量。

怎么设置?bash调用类似chdir的函数,将bash对应的工作目录更改成cwd对应的路径。

所有命令行执行命令,都是bash的子进程,这些子进程的task_struct属性从哪里来呢?

有很多属性都是拷贝自父进程的,代码共享,数据各自私有一份。其中也包括cwd,

就这是为什么在命令行上,当我自己处在某一个路径,把路径切换了,当我去执行命令时,我的路径总会是在我对应的指定的bash的路径,因为我的cwd继承了bash的cwd。

为什么默认处在家目录?

因为最开始bash读取环境变量,把环境变量设置好,bash把自己cwd设置搭配HOME环境变量中。

2.SHELL--->本质记录这个用户登陆时启动了哪一个shell

3.PWD---->保存当前进程所在的工作路径

函数getenv:获取环境变量,获取成功返回该环境变量起始位置地址,反之返回NULL。

执行下面代码:

#include<stdio.h>
#include<stdlib.h>

int main()
{
    printf("%s\n",getenv("PWD"));
    return 0;
}

指令pwd就是通过环境变量PWD来获取的!!!

为什么要保存呢?

进程能获得自己所在的路径,新建一个文件“log.txt”,怎么新建呢?----> getenv("PWD")/filename

方便定位当前工作路径,对文件进行操作新建。

4.USER--->表示当前使用系统的用户是谁

#include<stdio.h>
#include<stdlib.h>

int main()
{
    printf("USER: %s\n",getenv("USER"));
    return 0;
}

可以通过获取USER,意味着可以让我的子程序,识别用户身份,如果不是目标用户就不能使用该程序,是该目标用户才能使用该程序。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int main()
{
    //可以让我的程序只能我运行,其他人用不了
    const char *who = getenv("USER");
    if(strcmp(who, "ubuntu") == 0)
    {
        printf("执行程序的正常命令\n");
        return 0;
    }
    else
    {
        printf("无权访问\n");
        return 1;
    }
    return 0;
}

5.OLDPWD--->切换路径后,保存上一次所在的路径

指令cd - :就可以根据OLDPWD回到上一个路径

4.理解环境变量

环境变量功能上:是什么作用就是什么作用。

环境变量是系统提供的具有“全局”属性的变量。

本地变量VS环境变量

直接在命令行上输入变量会被放入到本地变量中去,在bash进程中也会存在一张表,用于存放本地变量。

怎么同时查看本地变量与环境变量?

指令set:查看所有变量,里面有一部分是bash本身的变量

怎么把本地变量更改为环境变量呢?

指令export 名:把本地变量更改为环境变量

指令unset 名:删除一个环境变量

把本地变量变成环境变量就是把本地变量中表的元素放入环境变量中。 也可以直接导入环境变量

ecport a=100

 下面代码生成的可执行程序为bash的子进程:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main()
{
    //先设置本地变量ISRUNNING,然后export设置为环境变量
    char *isrunning = getenv("ISRUNNING");
    if (isrunning == NULL)
    {
        while (1)
        {
            printf("当前进程首次启动!\n");
            sleep(1);
        }
    }
    else
    {
        printf("当前进程已经运行了,不需要再启动了!\n");
    }
    return 0;
}

如果设置为本地变量,子进程获取不到,bash不会传递给子进程,

导成环境变量如下:

说明环境变量可以被子进程继承,子进程中没有本地变量,本地变量转换为环境变量是在bash中完成的。

环境变量可以被子进程继承下去,环境变量可以被所有bash之后的进程全部看见,所以环境变量具有“全局”属性。

为什么要有环境变量?

  • 系统的配置,尤其是具有指导性的配置信息,环境变量他是系统配置起效的一种表现
  • 进程具有独立性!环境变量可以用来进程间传递数据(只读数据)

还有一种查看环境变量方法:environ

为什么是双指针,因为他指向环境变量表的起始位置,因为环境变量表里面也是地址,所有是双指针。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main()
{
    for(int i = 0; __environ[i];i++)
    {
        printf("%s\n",__environ[i]);
    }
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/913172.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FPGA实现串口升级及MultiBoot(八)四样错误实例演示

本文目录索引 一个指令和三种方式二种位流和四样错误Golden位流工程Watchdog的原理1、打开自己使用的Vivado版本的TCL SHELL2、进入multiboot_address_table.tcl 文件所在目录3、运行 multiboot_address_table.tcl 文件4、按照需求输入参数启动地址确定MultiBoot位流工程验证ex…

信息安全工程师(84)UNIX/Linux操作系统安全分析与防护

前言 UNIX/Linux操作系统&#xff0c;尤其是Linux&#xff0c;以其开放性、稳定性和安全性在服务器、桌面、嵌入式设备和超级计算机中占据重要地位。然而&#xff0c;没有任何操作系统可以百分之百地保证安全&#xff0c;UNIX/Linux也不例外。 一、UNIX/Linux操作系统安全分析 …

day08(单片机)时钟系统+定时器+PWM

目录 时钟系统定时器PWM 时钟系统 时钟基本概念 时钟源 晶体振荡器&#xff08;Crystal Oscillator&#xff09; RC振荡器&#xff08;Resistor-Capacitor Oscillator&#xff09; ​​​​​​​STM32U5时钟源 HSI(High Speed Internal) HSE(High Speed External) LSI(Low Spe…

【JavaEE初阶 — 多线程】内存可见性问题 volatile

1. 内存可见性问题 内存可见性的概念 什么是内存可见性问题呢&#xff1f; 当一个线程对共享变量进行了修改&#xff0c;那么另外的线程都是立即可以看到修改后的最新值。在Java中&#xff0c;可以借助 synchronized、volatile 以及各种Lock 实现可见性。如果我们将变量声…

通用特效Shader

一、通用特效Shader介绍 1.1 什么是通用特效材质 Unity支持SRP Batcher后&#xff0c;使用UberShader的优势非常明显。所谓&#xff0c;UberShader&#xff0c;即一个超级Shader&#xff0c;覆盖一类功能&#xff0c;而不是多个分散的小Shader&#xff0c;比如一个通用特效Sh…

spark-本地模式的配置和简单使用

python环境的安装 在虚拟机中&#xff0c;只能安装一个python的版本&#xff0c;若想要安装别的版本&#xff0c;则需要卸载之前的版本——解决方式&#xff0c;安装Anaconda 通过百度网盘分享的文件&#xff1a;Anaconda3-2021.05-Linux-x86_64.sh 链接&#xff1a;https://…

分享三个python爬虫案例

一、爬取豆瓣电影排行榜Top250存储到Excel文件 近年来&#xff0c;Python在数据爬取和处理方面的应用越来越广泛。本文将介绍一个基于Python的爬虫程序&#xff0c;用于抓取豆瓣电影Top250的相关信息&#xff0c;并将其保存为Excel文件。 获取网页数据的函数&#xff0c;包括以…

PyQt5 详细安装与配置教程及使用

文章目录 Part1&#xff1a;安装 PyQt5Part2&#xff1a;配置 PyQt5 的依赖工具 QtDesigner 和 PyUICPart3&#xff1a;使用QtDesigner设计界面Part4&#xff1a;使用PyUIC将设计好的界面转换为.py文件Part5&#xff1a;通过代码显示ui界面 Part1&#xff1a;安装 PyQt5 需要安…

ssm079基于SSM框架云趣科技客户管理系统+jsp(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 题目&#xff1a;客户管理系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本客户管理系统就是在这…

C语言 | Leetcode C语言题解之第556题下一个更大元素III

题目&#xff1a; 题解&#xff1a; int nextGreaterElement(int n){int x n, cnt 1;for (; x > 10 && x / 10 % 10 > x % 10; x / 10) {cnt;}x / 10;if (x 0) {return -1;}int targetDigit x % 10;int x2 n, cnt2 0;for (; x2 % 10 < targetDigit; x2…

华为大变革?仓颉编程语言会代替ArkTS吗?

在华为鸿蒙生态系统中&#xff0c;编程语言的选择一直是开发者关注的焦点。近期&#xff0c;华为推出了自研的通用编程语言——仓颉编程语言&#xff0c;这引发了关于仓颉是否会取代ArkTS的讨论。本文将从多个角度分析这两种语言的特点、应用场景及未来趋势&#xff0c;探讨仓颉…

Linux:基本开发工具

一&#xff1a;编辑器vim 1.1vim的基本概念 vim其实有多重模式&#xff0c;这里我们主要了解vim的三种模式&#xff0c;分别是命令模式&#xff08;command mode&#xff09;,插入模式(Insert mode)和底行模式(lst line mode) 正常/普通/命令模式(Normal mode) …

第14张 GROUP BY 分组

一、分组功能介绍 使用group by关键字通过某个字段进行分组&#xff0c;对分完组的数据分别 “SELECT 聚合函数”查询结果。 1.1 语法 SELECT column, group_function(column) FROM table [WHERE condition] [GROUP BY group_by_expression] [ORDER BY column]; 明确&#…

TVM计算图分割--BYOC框架

文章目录 BYOC架构算子标注单算子标注复合算子标注Cost-based PartitionCodegenCodegen for C代码生成流程概览代码生成工程实现实现CodegenC实现CSourceCodegenCodegen for JSON实现JsonCodegenRuntimeJSONRuntime参考随着后端设备数量激增,为达到较高的效果在这些设备上,对…

计算机毕业设计Python+卷积神经网络股票预测系统 股票推荐系统 股票可视化 股票数据分析 量化交易系统 股票爬虫 股票K线图 大数据毕业设计 AI

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

qt QShortcut详解

1、概述 QShortcut是Qt框架中的一个类&#xff0c;它提供了一种创建键盘快捷键的方式。通过QShortcut&#xff0c;开发者可以将特定的键盘组合&#xff08;如CtrlC、AltF4等&#xff09;与应用程序中的动作&#xff08;如复制、关闭窗口等&#xff09;关联起来。当用户在应用程…

C++OJ_二叉树的层序遍历

✨✨ 欢迎大家来到小伞的大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C_OJ 小伞的主页&#xff1a;xiaosan_blog 二叉树的层序遍历 102. 二叉树的层序遍历 - 力扣&#xff08;LeetCode&#xff0…

The Rank-then-Encipher Approach

原始观点 Format-Preserving Encryption 4 The Rank-then-Encipher Approach 引用1 Hybrid diffusion-based visual image encryption for secure cloud storage 2.2 Sum-preserving encryption Bellare introduced the concept of format-preserving encryption (FPE)…

DolphinDB 与南方科技大学联合授课啦!

11月1日&#xff0c;南方科技大学商学院和 DolphinDB 联合举办了高校课程讲座。讲座由南方科技大学商学院高级研究学者冯鹏举主持&#xff0c;DolphinDB 创始人兼 CEO 周小华博士、某百亿私募数据平台架构师潜蛟老师进行精彩演讲。 Part 1 : 大数据时代下数据库架构革新与生态…

IDM扩展添加到Edge浏览器

IDM扩展添加到Edge浏览器 一般情况下&#xff0c;当安装IDM软件后&#xff0c;该软件将会自动将IDM Integration Module浏览器扩展安装到Edge浏览器上&#xff0c;但在某些情况下&#xff0c;需要我们手动安装&#xff0c;以下为手动安装步骤 手动安装IDM扩展到Edge浏览器 打…