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");elseprintf("默认功能\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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android studio中关于printf和print和println的区别

print:为一般输出&#xff0c;同样不能保留精度格式转化&#xff0c;也不能换行输出&#xff0c;输出需要加上换行符printf:常用于格式转换&#xff0c;但需要注意不是换行输出&#xff0c;只用于精度转换&#xff0c;跟C语言的printf一样的&#xff0c;输出需要加上换行符prin…

GISBox VS ArcGIS:分别适用于大型和小型项目的两款GIS软件

在现代地理信息系统&#xff08;GIS&#xff09;领域&#xff0c;有许多大家耳熟能详的GIS软件。它们各自具有独特的优势&#xff0c;适用于不同的行业需求和使用场景。在众多企业和开发者面前&#xff0c;如何选择合适的 GIS 软件成为了一个值得深入思考的问题。今天&#xff…

精深之道:在专业领域迅速铸就影响力

在知识爆炸的时代专业化已成为各行各业竞争的关键词。要想在专业领域内快速实现影响力&#xff0c;不仅需要深厚的专业知识积累&#xff0c;还需要独到的见解、创新的思维以及有效的传播策略。本文旨在探讨如何在专业领域内迅速建立并扩大个人或组织的影响力&#xff0c;成为行…

微服务(二)

目录 1.网关路由 1.1.认识网关 1.2.快速入门 1.2.1.引入依赖 1.2.2.启动类 1.2.3.配置路由 1.3.路由过滤 2.网关登录校验 2.1.鉴权思路分析 2.2.网关过滤器 2.3.自定义过滤器 2.3.1.自定义GatewayFilter 2.3.2.自定义GlobalFilter 2.4.登录校验 2.4.1.JWT工具 …

ESP32学习笔记_FreeRTOS(1)——Task的创建和使用

摘要(From AI): 本文是基于 FreeRTOS 和 ESP_IDF 的学习笔记&#xff0c;详细讲解了任务管理、优先级设置、任务堆栈监控、看门狗定时器&#xff08;IWDT 和 TWDT&#xff09;等关键功能。内容涵盖任务创建与删除、任务挂起与恢复、时间片轮转调度机制&#xff0c;以及任务看门…

95.【C语言】数据结构之双向链表的头插,头删,查找,中间插入,中间删除和销毁函数

目录 1.双向链表的头插 方法一 方法二 2.双向链表的头删 3.双向链表的销毁 4.双向链表的某个节点的数据查找 5.双向链表的中间插入 5.双向链表的中间删除 6.对比顺序表和链表 承接94.【C语言】数据结构之双向链表的初始化,尾插,打印和尾删文章 1.双向链表的头插 方法…

【Docker容器化技术】docker安装与配置、常用命令、容器数据卷、应用部署实战、Dockerfile、服务编排docker-compose、私有仓库

文章目录 一、Docker的安装与配置1、docker概述2、安装docker3、docker架构4、配置镜像加速器 二、Docker命令1、服务相关命令2、镜像相关命令3、容器相关命令 三、Docker容器数据卷1、数据卷概念及作用2、配置数据卷3、配置数据卷容器 四、Docker应用部署实战1、部署MySQL2、部…

海外云手机在出海业务中的优势有哪些?

随着互联网技术的快速发展&#xff0c;海外云手机已在出海电商、海外媒体推广和游戏行业都拥有广泛的应用。对于国内的出海电商企业来说&#xff0c;短视频引流和社交平台推广是带来有效流量的重要手段。借助云手机&#xff0c;企业能够更高效地在新兴社交平台上推广产品和品牌…

abap 可配置通用报表字段级日志监控

文章目录 1.功能需求描述1.1 功能1.2 效果展示2.数据库表解释2.1 表介绍3.数据库表及字段3.1.应用日志数据库抬头表:ZLOG_TAB_H3.2.应用日志数据库明细表:ZLOG_TAB_P3.3.应用日志维护字段配置表:ZLOG_TAB_F4.日志封装类5.代码6.调用方式代码7.调用案例程序demo1.功能需求描述 …

OceanBase 应用实践:如何处理数据空洞,降低存储空间

问题描述 某保险行业客户的核心系统&#xff0c;从Oracle 迁移到OceanBase之后&#xff0c;发现数据存储空间出现膨胀问题&#xff0c;数据空间 datasize9857715.48M&#xff0c;实际存储占用空间17790702.00M。根据 required_mb - data_mb 值判断&#xff0c;数据空洞较为严重…

软件测试:测试用例详解

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、通用测试用例八要素   1、用例编号&#xff1b;    2、测试项目&#xff1b;   3、测试标题&#xff1b; 4、重要级别&#xff1b;    5、预置…

C++——左值和右值的本质区别

左值和右值好干嘛&#xff1f; 深入理解左值和右值可以帮助我们对代码进行优化 一、什么是左值和右值 左值&#xff1a;有某种存储支持的变量 右值&#xff1a;临时值&#xff08;字面量、函数的结果&#xff09; Ⅰ右值是字面量 int yy 22;22本身就是一个临时的&#xf…

【iOS】知乎日报第三周总结

【iOS】知乎日报第三周总结 文章目录 【iOS】知乎日报第三周总结前言评论区文字评论区的一个展开效果评论区数据的一个请求修改了主页获取数据的逻辑主页无限轮播图图片主色调的一个获取将一些拓展部分的内容写在分类里小结 前言 本周笔者因为金工实习整个项目进展比较慢&#…

OpenAI的Triton能替代Nvidia的CUDA吗

先说我的观点&#xff0c;我觉得可以&#xff0c;但是应该不是现在。 然后得补个概念&#xff0c;啥是Triton OpenAI的Triton 是一种专为高效编写深度学习运算而设计的编程语言和编译器。它旨在简化用户编写针对现代GPU&#xff08;尤其是NVIDIA GPU&#xff09;的自定义运算…

【黑马Redis原理篇】Redis数据结构

视频来源&#xff1a;原理篇[2,15] 文章目录 1.动态字符串SDS1.1 内部结构&#xff1a; 2.IntSet3.Dict3.1 dict的内部结构3.2 dict的扩容 4.ziplist压缩列表5.QuickList6.SkipList跳表7.RedisObject对象8.Redis的五种数据结构8.1 String8.2 List8.3 Set8.4 Zset 有序集合8.5 …

SpringBoot 创建多模块项目 项目分模块 项目简化 打包发布

介绍 在 Spring Boot 中&#xff0c;创建多模块项目可以帮助我们将项目拆分成多个相对独立、可重用的模块&#xff0c;从而使代码结构更清晰&#xff0c;便于管理和维护。通常&#xff0c;这样的做法可以提高开发效率&#xff0c;并且更易于进行版本控制和分布式部署。 项目结…

MySQL 数据库之表操作

1. 创建表 CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) [character set 字符集 collate 校验规则 engine 存储引擎];field 表示列名datatype 表示列的类型character set 字符集&#xff0c;如果没有指定字符集&#xff0c;则以所在数据库…

【R78/G15 开发板测评】串口打印 DHT11 温湿度传感器、DS18B20 温度传感器数据,LabVIEW 上位机绘制演化曲线

【R78/G15 开发板测评】串口打印 DHT11 温湿度传感器、DS18B20 温度传感器数据&#xff0c;LabVIEW 上位机绘制演化曲线 主要介绍了 R78/G15 开发板基于 Arduino IDE 环境串口打印温湿度传感器 DHT11 和温度传感器 DS18B20 传感器的数据&#xff0c;并通过LabVIEW上位机绘制演…

Chromium Mojo(IPC)进程通信演示 c++(2)

122版本自带的mojom通信例子associated-interface 仅供学习参考&#xff1a; codelabs\mojo_examples\02-associated-interface-freezing 一、目录结构如图&#xff1a; 二、interface.mojom接口 1、codelabs\mojo_examples\mojom\interface.mojom // Copyright 2023 The C…

「Mac畅玩鸿蒙与硬件32」UI互动应用篇9 - 番茄钟倒计时应用

本篇将带你实现一个番茄钟倒计时应用&#xff0c;用户可以设置专注时间和休息时间的时长&#xff0c;点击“开始专注”或“开始休息”按钮启动计时&#xff0c;应用会在倒计时结束时进行提醒。番茄钟应用对于管理时间、提升工作效率非常有帮助&#xff0c;并且还会加入猫咪图片…