2024年运维最全Linux--进程概念_gid是进程

.PHONY:clean
clean:
rm -f myporc


查看进程脚本



> 
> ps ajx | head -1 && ps ajx | grep "myporc" 
> 
> 
> 


![](https://img-blog.csdnimg.cn/9e55ca3cbee2403891c40e8d1dfc56c8.png)



> 
> 
> * Process ID(PID)  
>  Linux中标识进程的一个数字,它的值是不确定的,是由系统分配的(但是有一个例外,启动阶段,kernel运行的第一个进程是init,它的PID是1,是所有进程的最原始的父进程),每个进程都有唯一PID,当进程退出运行之后,PID就会回收,可能之后创建的进程会分配这个PID
> * Parent Process ID(PPID)  
>  字面意思,父进程的PID
> * Process Group ID(PGID)  
>  PGID就是进程所属的Group的Leader的PID,如果PGID=PID,那么该进程是Group Leader
> * Session ID(SID)  
>  和PGID非常相似,SID就是进程所属的Session Leader的PID,如果SID==PID,那么该进程是session leader
> * *TPGID*:*控制终端进程组ID*(由控制终端修改,用于指示当前前台进程组)
> * STAT: 进程状态
> * *UID:用户标识码*
> * *TIME:命令常用于测量一个命令的运行时间*
> 
> 
> 


 进程在调度运行的时候,进程具有动态属性


#### 见见系统调用



> 
> **man getpid**
> 
> 
> 


![](https://img-blog.csdnimg.cn/ca91fc0c00004bef97764fd917f30b8d.png)**测试代码:**



#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main()
{
while(1)
{
printf(“我是一个进程!,我的ID是:%d\n”,getpid());
sleep(1);
}
return 0;
}


![](https://img-blog.csdnimg.cn/afc3833ec492472b94807efa7d667872.png)


每次重新运行程序pid都会改变


因为每次都需要重新加载到内存,就意味着操作系统都会重新创建task\_struct,重新分配pid


![](https://img-blog.csdnimg.cn/90d6f9c6a69c404b9398a2fa664b3529.png)



> 
>  ls /proc
> 
> 
> 


![](https://img-blog.csdnimg.cn/bde6f382b7014b008c31d31f4596bfcf.png)



> 
>  “/proc/[pid]”目录,pid为进程的数字ID,是个数值,每个运行着的进                                                程都有这么一个目录。
> 
> 
> 


![](https://img-blog.csdnimg.cn/f2c8002b394b4951bfb5b388c94c6a5f.png)


![](https://img-blog.csdnimg.cn/8389d086b09041139ed60632db1e3c6e.png)**查看父进程**



#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main()
{
while(1)
{
printf(“我是一个进程!,我的ID是:%d\n,父进程ID是:%d\n”,getpid(),getppid());
sleep(1);
}
return 0;
}


![](https://img-blog.csdnimg.cn/9c202b2f3bd4469b90959037c314922a.png)


![](https://img-blog.csdnimg.cn/29764afa66d54650b53490a22fed2b1d.png)


 命令行上启动的进程,一般它的父进程没有特殊情况的话,都是bash


#### 通过系统调用创建进程-fork初识



> 
> 运行 man fork 认识fork
> 
> 
> fork有两个返回值
> 
> 
> 父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝) 
> 
> 
> 


![](https://img-blog.csdnimg.cn/f2b16c8776974e28ba75b39a138f33cf.png)


**创建进程**



#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
int ret = fork();
if(ret < 0){
perror(“fork”);
return 1;
}
else if(ret == 0){ //child
printf(“I am child : %d!, ret: %d\n”, getpid(), ret);
}else{ //father
printf(“I am father : %d!, ret: %d\n”, getpid(), ret);
}
sleep(1);
return 0;
}


![](https://img-blog.csdnimg.cn/8ca17f87d3e0455fbde702fd6293ef84.png)


### 进程状态


#### **宏观概念**


![](https://img-blog.csdnimg.cn/db0cb44358234d85a34d2e8f96924800.png) **总结:**



> 
> 1.一个CPU一个运行队列
> 
> 
> 2.让进程入队列的本质是:将该进程的struct stsk\_struct结构体对象放入运行队列中
> 
> 
> 3.进程PCB在runqueue,就是R--运行状态,而不是这个进程正在运行才叫运行状态
> 
> 
> 4.进程不只会等待(占用)CPU资源,也可能随时随地要外设资源
> 
> 
> 5.所谓的进程不同的状态,本质是进程在不同的队列中,等待某种资源
> 
> 
> 


####  挂起状态


![](https://img-blog.csdnimg.cn/b7a6f9a92f324fd1a2c1fca34302d006.png)


**阻塞和挂起的区别**



> 
> 挂起了一定阻塞,阻塞了不一定挂起。阻塞时该进程的代码和数据都在内存中,当内存不足时,就会产生挂起状态该内存的数据和代码就会被操作系统放进磁盘中
> 
> 
> 


#### Linux操作系统的的状态



> 
> static const char \* const task\_state\_array[] = {
> 
> 
> "R (running)", /\* 0 \*/
> 
> 
> "S (sleeping)", /\* 1 \*/
> 
> 
> "D (disk sleep)", /\* 2 \*/
> 
> 
> "T (stopped)", /\* 4 \*/
> 
> 
> "t (tracing stop)", /\* 8 \*/
> 
> 
> "X (dead)", /\* 16 \*/
> 
> 
> "Z (zombie)", /\* 32 \*/
> 
> 
> };
> 
> 
> 


**R运行状态(running):** 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。


**S睡眠状态(sleeping):** 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。


**D磁盘休眠状态(Disk sleep):**有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的 进程通常会等待IO的结束。


**T停止状态(stopped):** 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。


**X死亡状态(dead):**这个状态只是一个返回状态,你不会在任务列表里看到这个状态。



#### 进程状态查看



> 
> ps aux / ps axj 命令 
> 
> 
> 


**R状态--运行状态**



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

int main()
{
int a=0;
while(1)
{
a=1+2;
};

return 0;    

}


![](https://img-blog.csdnimg.cn/ef3e1b4236e04f2db4122a8b258a2526.png)


**S状态--阻塞状态**



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

int main()
{
int a=0;
while(1)
{
a=1+2;
printf("当前a的值是:%d\n ",a);
};

return 0;    

}


![](https://img-blog.csdnimg.cn/9c99fa791a4b460996d3d5b3b6056dce.png)


**为什么是S状态?**


因为printf会访问显示器,显示器是外设的输入输出速度比较慢,CPU就会等待显示器就绪。99%都是在等I/O就绪,1%才是执行打印代码。所以当我们去查的时候,几乎大概率都会是S状态


**t状态 --停止状态**


进程处于此状态表示该进程正在被追踪,比如 gdb 调试进程: 


![](https://img-blog.csdnimg.cn/2623fbf72b9d4a73bffcc38635d33893.png)


**T状态--停止状态**


**测试代码**



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

int main()
{
int a=0;
while(1)
{
a=1+2;
};

return 0;    

}



> 
> **kill -l**
> 
> 
> 


![](https://img-blog.csdnimg.cn/335cb9a8bb5941969007c369b746115d.png)



> 
>  **kill -19 [pid]--停止进程** 
> 
> 
> 


![](https://img-blog.csdnimg.cn/0df2a76bf3a842c78c9111f5be5c67d3.png)


**后台运行**



> 
>   **kill -18 [pid]--运行进程**
> 
> 
> 


![](https://img-blog.csdnimg.cn/671aabac5273462d88dec6b95e2993fe.png)



> 
> **后台运行--‘+’的消失**
> 
> 
> **前台运行--有‘+’**
> 
> 
> 


![](https://img-blog.csdnimg.cn/6a973ed3807c4857afa2aefba5b9c24b.png) 用Ctrl+c不能杀死![](https://img-blog.csdnimg.cn/95de6c7ed8aa4ab1a55797c5175e1c53.png)



> 
>  就用kill -9 [pid]
> 
> 
> 


![](https://img-blog.csdnimg.cn/e0c744018b5549308057d3d0c51ac287.png)


**总结:**


先将该进程暂停后,再运行。进程状态前面的 + 号消失了,该进程变成了后台程序。但是对于后台进程来说,我们只能通过 kill 命令来杀死它。


**D状态--深度睡眠**



> 
> 深度睡眠TASK\_UNINTERRUPTIBLE:不可被信号唤醒;
> 
> 
> 浅度睡眠TASK\_INTERRUPTIBLE:唤醒方式,等到需要的资源,响应信号;
> 
> 
> 


深度睡眠场景:



> 
> 有些场景是不能响应信号的,比如读磁盘过程是不能打断的,NFS也是;
> 
> 
> 执行程序过程中,可能需要从磁盘读入可执行代码,假如在读磁盘过程中,又有代码需要从磁盘读取,就会造成嵌套睡眠。逻辑做的太复杂,所以读磁盘过程不允许打断,即只等待IO资源可用,不响应任何信号;
> 
> 
> 应用程序无法屏蔽也无法重载SIGKILL信号,深度睡眠可以不响应SIGKILL kill-9信号;
> 
> 
> 


**注意:处于深度睡眠状态的进程既不能被用户杀掉,也不能被操作系统杀掉,只能通过断电,或者等待进程自己醒来**。深度睡眠一般只会在高IO的情况发生下,且如果操作系统中存在多个深度睡眠状态的程序,那么说明该操作系统也即将崩溃了。


**X--死亡状态** 


死亡状态代表着一个进程结束运行,该进程对应的PCB以及代码和数据全部被操作系统回收。


**Z--僵尸状态**



僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程


僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。


所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态


![](https://img-blog.csdnimg.cn/bc48b44e63df4ab5b0f565c5377c8ece.png)


### 两个特殊的进程


####  Z(zombie)-僵尸进程


僵尸进程是处于僵尸状态的进程


**脚本代码**



> 
> while :; do ps axj | head -1 && ps axj |grep myprocess |grep -v grep; sleep 1; done
> 
> 
> 


**测试代码**



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

int main()
{

    pid_t id = fork();    
    if(id == 0)    
    {    
        //child    
        while(1){    
            printf("I am child process, pid: %d, ppid: %d\n", getpid(), getppid());
            sleep(5);    
            exit(1);    
        }    
    }    
    else    
    {    
        //parent    
        while(1)    
        {    
            printf("I am parent proceass, pid: %d, ppid: %d\n", getpid(), getppid());                sleep(1);
        }                
    }        
return 0;

}



> 
> 子进程:pid= 26655 
> 
> 
> 父进程:ppid=26654
> 
> 
> 


![](https://img-blog.csdnimg.cn/4dac946acc454f73b4915d4f87827fee.png)


当运行一段时间后的显现,26654进程因为打印变成S状态,由于26655退出了该进程变成僵尸进程。


![](https://img-blog.csdnimg.cn/281f937a3b0e468e9648f1fa5ae1f63d.png)


 在上面测试代码中



> 
>  if(id == 0)      
>          {      
>              //child      
>              while(1){      
>                  printf("I am child process, pid: %d, ppid: %d\n", getpid(), getppid());  
>                **sleep(5);**  
>                  **exit(1);**  
>              }      
>          }    
> 
> 
> 


子进程通过5秒后退出,结合观察得到再子进程(26655)退出后,父进程(26654)变成了僵尸状态。



> 
> **26654 26655 26654  5238 pts/0    26654 Z+    1005   0:00 [myprocess] <defunct>**
> 
> 
> **<defunct>  的意思失效**
> 
> 
> **意味着该进程是失效的,死掉的**
> 
> 
> 


当我们再观察进程时,我们发现该进程不在了,其原因是关掉了父进程,该失效的子进程被系统回收了。


![](https://img-blog.csdnimg.cn/64fae864bd144212b697e14825c441bb.png)


#### 僵尸进程危害


进程的退出状态必须被维持下去,父进程需要一直知道子进程的状态,随时进行处理。可父进程如果一直不读取,那子进程就一直处于Z状态。


维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task\_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护。


那一个父进程创建了很多子进程,就是不回收,就会造成内存资源的浪费,因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间!--内存泄漏


#### 孤儿进程


**测试代码**



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

int main()
{

    pid_t id = fork();    
    if(id == 0)    
    {    
        //child    
        while(1){    
            printf("I am child process, pid: %d, ppid: %d\n", getpid(), getppid());
            sleep(1);                                                                  
        }    
    }    
    else    
    {    
        //parent    
        while(1)    
        {    
            printf("I am parent proceass, pid: %d, ppid: %d\n", getpid(), getppid());                sleep(1);
        }                
    }        
return 0;

}


最开始代码跑起来时,父进程pid=8644,bash=5238;子进程的pid=8645,子进程的父进程8644;他们的运行状态都是S状态--因为在打印就会访问I/O;


![](https://img-blog.csdnimg.cn/48d584c604dc4911aa1690d3c3c975e9.png)


当我们销毁子进程时,kill -9 8645;我们发现子打印进程不在了,但是子进程的状态还在,变成了Z状态,这个时候就只能等父进程退出,让操作系统进行回收。


![](https://img-blog.csdnimg.cn/f26b62aecb3d4885b0e1f9faf7ccf1a0.png)


 当我们杀掉父进程时,子进程被操作系统领养了,这个过程就叫做孤儿进程![](https://img-blog.csdnimg.cn/6c0c3de9d3734a67a07ad2f0adc5cdf9.png)


 当整个进程变成孤儿了,我们发现我们用Ctrl + c 是不能退出的;我们细心就会发现最开始子进程是S+当变成孤儿进程了,它的状态就是S了。说明了该程序变成了后台程序。


![](https://img-blog.csdnimg.cn/20b3cb35ce1c40b7862ef24fbbbb3d7f.png)


那么这个时候我们就只有用kill -9 16866,杀掉子进程了


![](https://img-blog.csdnimg.cn/d2d93338a4f14e2c88f3fbb555df6eb8.png)


### 进程优先级


#### 基本概念


cpu资源分配的先后顺序,就是指进程的优先权(priority)。


优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。 还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。


#### 查看系统进程


在linux或者unix系统中,用ps –l命令则会类似输出以下几个内容: 


![](https://img-blog.csdnimg.cn/77cb9d758d8e4af6b65b3ccb7218bdb6.png)


我们很容易注意到其中的几个重要信息,有下:



> 
> UID : 代表执行者的身份
> 
> 
> PID : 代表这个进程的代号
> 
> 
> PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
> 
> 
> PRI :代表这个进程可被执行的优先级,其值越小越早被执行
> 
> 
> NI :代表这个进程的nice值
> 
> 
> 


**PRI and NI**



> 
> PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高
> 
> 
> 那NI呢?就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值
> 
> 
> PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice
> 
> 
> 这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行 所以,调整进程优先级,在Linux下,就是调整进程nice值
> 
> 
> **nice其取值范围是-20至19,一共40个级别**
> 
> 
> 


#### 用top命令更改已存在进程的nice:


top



> 
> 1.非root用户:sudo top 进入top
> 
> 
> 2.在top中按r
> 
> 
> 3.输入进程pid
> 
> 
> 4.输入nice值
> 
> 
> 


这里是输入nice值为100,我们发现最大区间是99 -19


![](https://img-blog.csdnimg.cn/f7567ca3898c42d598cf8571f6f245ad.png)


这里是输入nice值为-100,所以我们发现最小大区间是60-20


![](https://img-blog.csdnimg.cn/743eafc6485748748924d62d7381c7ef.png)


经过上面两个例子,我们就发现其实我们只能改变**范围是-20至19,一共40个级别**




---


我们再观察一个场景,当我们把nice值改9的时候,我们发现值变成89,那么就得出结论:**每次改nice值都在默认nice值的基础上进行改动,而不是修改之后。**


![](https://img-blog.csdnimg.cn/8655d24fb6a44146b691ff8ddd848fb0.png)


#### 其他概念


竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高 效完成任务,更合理竞争相关资源,便具有了优先级 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰 并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行 并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为 并发


### 进程切换


**测试代码**



#include <stdio.h>

int main()
{
int a=10;
int b=10;
int c=b+a;

printf("%d\n",c);      
a=20;      
b=30;      
c=b+a;      

printf(“%d\n”,c);
return 0;
}


我们从该代码中不难看出,a,b,c都是临时变量,在以前c语言也知道,他们的数据都是存放在寄存器里的,这里我们发现寄存器里的数据是可以发生改变的。这里我们进入vs2013查看反汇编,数据都是加载到寄存器中的。


![](https://img-blog.csdnimg.cn/89bf683805394caba950d2dde5f3c4c3.png)


在这里有一个概念需要知道:**CPU虽然只有一套寄存器,但是寄存器的数据是属于当前进程的**。这里的寄存器其实更偏向于寄存器内的数据,而不是寄存器硬件。


![](https://img-blog.csdnimg.cn/19cd859fc7f345f6a9782d0d8b3ec2b8.png)


在这个过程中运行的时候,占有CPU进程不是一直要占有到进程结束!因为CPU虽然只有一套寄存器而且还要对其他进程进行处理,**进程在运行的时候,都会有自己的时间片**


![](https://img-blog.csdnimg.cn/be3f63228b82465b886b199e441210db.png)


操作系统会对每个进程进行设置一个时间片,让CPU去读取进程数据进行处理,就会有进入/退出--进程的切换。在这个切换的过程中寄存器数据需要被保护和恢复。


**进程在切换的时候,要进行进程的上下文保护,当进程在恢复运行的时候,要进行上下文的恢复。**


在任何时刻,CPU里面的寄存器里面的数据,看起来是在大家都能看到的寄存器上,但是,寄存器内的数据,只属于当前运行的进程!


寄存器被所有进程共享,寄存器内的数据,是每个进程各自私有的---上下文数据


### 环境变量


#### 基本概念


环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数 如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性


#### 常见环境变量


**PATH : 指定命令的搜索路径** 


![](https://img-blog.csdnimg.cn/68a70140145446ee8db5f101ed747b1f.png)


我们直接输入test我们发现,编译不起


![](https://img-blog.csdnimg.cn/d2c9f9148a734d8288d48fcf1df5f83f.png)


我们将我们写的可执行程序放入usr/sbin路径中,需要注意的这个文件目录只有root用户可以进去


![](https://img-blog.csdnimg.cn/eb2858d150e74463a7fd9d690d6ae591.png)这种是不支持的,会污染指令池;



> 
> sudo rm /usr/bin/test.o ---删除    
> 
> 
> 


![](https://img-blog.csdnimg.cn/0d1851f33bea4a799b474cdc946c0d06.png)


 一般情况下,我们是选择用



> 
> exprot PATH=$PATH:
> 
> 
> 


![](https://img-blog.csdnimg.cn/2b073496bfa043d2bf865d1cdb5c312e.png) 我们发现使用exprot PATH=$PATH:      test.o 的路径还是在11-19中![](https://img-blog.csdnimg.cn/229addb4db124f80b4f4749c17078db8.png)


**总结:**



> 
> 1.PATH就是系统默认的搜索路径
> 
> 
> 2.系统的指令能被找到就是因为环境变量PATH本来就默认带了系统对于的路径搜索
> 
> 
> 3.which底层实现就使用环境变量PATH来进行路径搜索
> 
> 
> 


系统会默认将.bash\_profile执行一次,将环境变量导到shell中,也就是说环境变量的配置也就是.bash\_profile 再启动的时候加载到bash中


![](https://img-blog.csdnimg.cn/2734a3f462e44ca8a83c37342344b788.png)


 vim  .bash\_profile--进入.bash\_profile![](https://img-blog.csdnimg.cn/34f9fc5f1f784303b56331533b49eb75.png)




---



**HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)**


![](https://img-blog.csdnimg.cn/8d7242cfc84f4626918024a97bcdb835.png)


用root和普通用户,分别执行 echo $HOME ,对比差异 . 执行 cd ~; pwd ,对应 ~ 和 HOME 的关系 


**SHELL : 当前Shell,它的值通常是/bin/bash。**


![](https://img-blog.csdnimg.cn/809cde870fc744e3b230447a4627ecac.png)


#### 和环境变量相关的命令



> 
> 1. echo: 显示某个环境变量值
> 
> 
> 2. export: 设置一个新的环境变量
> 
> 
> 3. env: 显示所有环境变量
> 
> 
> 4. unset: 清除环境变量
> 
> 
> 5. set: 显示本地定义的shell变量和环境变
> 
> 
> 


**USER**


测试代码



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

#define USER “USER”

int main()
{
char *who =getenv(USER);
if(strcmp(who,“root”)==0)
{

printf("hello world\n");    
printf("hello world\n");    
printf("hello world\n");    
printf("hello world\n");    
}    
else{    
    printf("权限不足");    
}    

return 0;    

}


![](https://img-blog.csdnimg.cn/dc1a94dff60f44f0adf9e6030843e85b.png)


这里说明**不同的环境变量运用的场景不同**


**echo- export**


测试代码



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

#define USER “USER”
#define MY_ENV “myval”

int main()
{

char *myenv=getenv(MY_ENV);    
if(NULL==myenv)    
{    
    printf("%s,not found\n",MY_ENV);    
    return 1;                                                                                                                                                                          
}    
printf("%s=%s\n", MY_ENV, myenv);    


return 0;    

}


我们自己定义一个变量,但是我们用echo 查看发现在当前文件能查看,但是用env去找不到。因为是这里相当于是定义的局部变量 


![](https://img-blog.csdnimg.cn/8a4032cc36194ca4bf48637212af7775.png)


**本地变量只会在当前进程(bash)内有效**


我们用export将本地变量导成环境变量


![](https://img-blog.csdnimg.cn/ba9ae24200904103bc9c45d6989c6c6d.png)


然后我们在运行test.o,我们发现myval可以被test.c引用


![](https://img-blog.csdnimg.cn/429e3e7cee2444e089c2faa37c00c929.png)


这里我们就得出结论:**环境变量具有全局性,是会被子进程下去**。 


bash是一个系统进程,myval也是一个进程(fork),那么为了满足不同的场景,bash会帮助我们找指令路径,身份认证;


**set**


用set显示本地定义的shell变量和环境变


![](https://img-blog.csdnimg.cn/ef2d7f462f7b4967a67c2581f2489209.png)


**unset**


![](https://img-blog.csdnimg.cn/2786f419c2fb4ea59342b262d6721a80.png)


**env**


![](https://img-blog.csdnimg.cn/4ed8c06ab1724d499a6eb6e01be989b5.png)


其他相关指令


![](https://img-blog.csdnimg.cn/a2448f0650df4b14b4b75f369d6e6e2e.png)



#### 通过代码如何获取环境变量


**命令行前两个参数**



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

#define USER “USER”
#define MY_ENV “myval”
#define MYPWD “PWD”

int main(int argc, char *argv[])
{

    if(argc != 2)
    {
        printf("Usage: \n\t%s [-a/-b/-c/-ab/-bc/-ac/-abc]\n", argv[0]);
        return 1;
    }
    if(strcmp("-a", argv[1]) == 0)
    {
        printf("功能a\n");
    }
    if(strcmp("-b", argv[1]) == 0)
    {
        printf("功能b\n");
    }
    if(strcmp("-c", argv[1]) == 0)
    {
        printf("功能c\n");
    }
    if(strcmp("-ab", argv[1]) == 0)
    {
        printf("功能ab\n");
    }                                                                                                                                                                                  
    if(strcmp("-bc", argv[1]) == 0)
    {
        printf("功能bc\n");
    }    

return 0;
}


![](https://img-blog.csdnimg.cn/e2e2970d3dbb481c8c8759d1910bd64f.png)


 就好比同一个程序,有多个选项控制


![](https://img-blog.csdnimg.cn/f4fcd5e3a1a84917afa6ccd10815506b.png)


**命令行第三个参数**


libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时 要用extern声明。



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

#define USER “USER”
#define MY_ENV “myval”
#define MYPWD “PWD”

int main(int argc, char *argv[],char *env[] )
{

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

}


![](https://img-blog.csdnimg.cn/dc27bc616ee94822b11cd737cfc36c8b.png)


**getenv**



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

int main()
{
printf(“%s\n”, getenv(“PATH”));
return 0;
}


![](https://img-blog.csdnimg.cn/297510a2a67a46769c66569337d9e1b4.png)


在这个三个获取环境变量的方式:getenv,char\*env,extren char \*\*environ中一般推荐使用getenv;因为一般情况我们不需要把全部变量都获取,都是按照自己的需求来。


总结:


在操作系统中用来指定操作系统运行环境的一些参数,我们在启动系统的时候bash就会自动更新该数据,相应的环境变量都有相应的应用场景,一些简单的指令其实就是函数调用环境变量。


### 程序地址空间


#### 用段代码感受父子进程地址


进程之间是相互独立,都有自己的pdb


测试代码



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

int global_value = 100;

int main()
{
pid_t id = fork();
if(id < 0)
{
printf(“fork error\n”);
return 1;
}
else if(id == 0)
{
int cnt = 0;
while(1)
{
printf(“我是子进程, pid: %d, ppid: %d | global_value: %d, &global_value: %p\n”, getpid(), getppid(), global_value, &global_value);
sleep(1);
cnt++;
if(cnt == 10)
{
global_value = 300;
printf(“子进程已经更改了全局的变量啦…\n”);
}
}
}
else
{
while(1)
{
printf(“我是父进程, pid: %d, ppid: %d | global_value: %d, &global_value: %p\n”, getpid(), getppid(), global_value, &global_value);
sleep(2);
}
}
}
else
{
while(1)
{
printf(“我是父进程, pid: %d, ppid: %d | global_value: %d, &global_value: %p\n”, getpid(), getppid(), global_value, &global_value);
sleep(2);
}
}
sleep(1);
}


运行这段代码后, 我们发现运行一段时间后,子进程和父进程的地址是一样的,但是他们global\_value值却发生了改变,这个是为什么呢? 


![](https://img-blog.csdnimg.cn/38526809c0c848a3a1cb9501628f2d75.png)


这里我需要想一下,这里地址没变,那么这个地址是什么?如果是物理地址父子进程的必须是一样,不然硬件是无法执行。那么这里就很显然就不是物理地址,就是我们提到的**程序地址空间**


为了解决父子进程地址一样,值不一样的现象,我们必须将程序地址空间搞清楚




**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/topics/618542503)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
  • 9
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值