当前位置:   article > 正文

详解Linux进程概念

linux进程

目录

1.基本常识及概念

1.1冯诺依曼体系结构

1.2操作系统

2.进程

2.1什么是进程

2.2描述进程--PCB

2.3 task_struct中的内容

3.进程的相关操作

3.1查看进程

3.2通过系统调用获取进程标识符(PID)

3.3通过函数调用创建进程  --- fork函数初始

3.4 命令行上启动的进程

4. 进程状态

4.1进程状态

4.2 进程状态的查看

4.3僵尸进程

4.4孤儿进程

5.进程优先级

5.1基本概念

5.2查看系统进程

5.2.1 PRI and NI

5.2.2 PRI vs NI

5.3 修改进程的优先级

5.4 进程的其他概念

6.环境变量

6.1 基本概念 

6.2 常见环境变量

6.3查看环境变量的方法

6.4 测试PATH

6.4.1 创建 hello.c文件

6.5 环境变量的相关变量

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

6.7 环境变量通常具有全局性

7. 进程地址空间


1.基本常识及概念

1.1冯诺依曼体系结构

我们常见的计算机,如笔记本;不常见的计算机,如服务器;大部分都遵守冯诺依曼体系结构。如下图所示:

  •  输入设备:磁盘,网卡,键盘,话筒,摄像头;
  • 输出设备:磁盘,网卡,显示器,显卡,音响;
  • 存储器:主要指内存,磁盘不属于存储器;
  • 控制器&运算器:CPU;

1.2操作系统

任何计算机系统都包含了一个基本的程序集合,称为操作系统(OS);笼统的理解:OS包含了

  • 内核(进程管理,内存管理,文件管理,驱动管理)
  • 其他程序(库函数,shell程序)

2.进程

2.1什么是进程

进程=程序+操作系统维护进程的相关的数据结构。 

在学校的教材中,进程被理解成是一个正在执行的程序,这种说法是错误的,程序只是进程中的一部分,他还包含了操作系统维护的进程的数据结构。

2.2描述进程--PCB

操作系统管理进程,把进程的信息放到进程控制快(PCB)的数据结构中,也就是说,PCB中存放的是进程属性的集合。

在Linux操作系统中,描述进程的结构体叫做task_struct。task_struct是Linux内核的一种数据结构。task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。

2.3 task_struct中的内容

  • 标识符:描述本进程的唯一标识符,用来区别其他的进程
  • 状态:任务状态,退出代码,退出信号灯。
  • 优先级:相对于其他的进程的优先级
  • 程序计数器:程序中即将被执行的下一条指令的程序
  • 内存指针:包括程序代码和进程相关数据的指针,还有其他的进程共享 的内存块的指针
  • 上下文数据:进程执行时处理器的寄存器中的数据
  • I/O状态信息:包括显示I/O请求,分配给进程的I/O设备和进程使用的文件列表
  • 记账信息:可能包括处理器的时间总和,使用的时钟总和,时间限制
  • 其他信息

可以在内核源代码里找到它,所有运行在系统里面的进程都以task_struct链表的形式存在内核中。

3.进程的相关操作

3.1查看进程

进程的相关信息可以通过在命令行输入 ls  /proc 获取,比如要查看进程为的PID为 1 的进程的信息就输入 ls /proc/1,但是一般都是没有权限的,可以加上 sudo

3.2通过系统调用获取进程标识符(PID)

PID:当前进程的ID;

PPID:当前进程的父进程的ID

可以看出,通过函数getpid() 和 getppid() 可以得到子进程和父进程的标识符。 他们的头文件是sys/types 和 unistd;但是在虚拟机中,只需要一个stdio.h就可以调用这两个代码。

3.3通过函数调用创建进程  --- fork函数初始

通过调用 fork 函数,可以发现,原本只有一行输出,确在显示界面输出了两行,而这的根本原因就是 fork 创建了一个子进程。

通过 if 分流,代码和结果如下所示:  通过 if 的分流,可以清楚的说明:fork()  函数返回的是0 给子进程,返回给父进程的的值就是子进程的 PID。如果返回的值是小于 0 就是创建子进程失败了。

所以,创建子进程可以通过 if 分流进行分流,从而实现子进程和父进程实现不同的功能。要不然子进程和父进程只会执行一样的代码,因为父进程和子进程的代码是共享的,所以不进行分流就会导致子进程和父进程执行一样的代码。既让这样,

那么为什么子进程和父进程的返回值为什么不同?

父进程中,fork函数返回的是子进程的PID,而子进程中,fork返回的是0;虽然子进程和父进程共享数据和代码,但是这是在默认的情况下;一旦子进程或者父进程要写入数据,就会发什么“写时拷贝”,   写是拷贝涉及到程序地址空间,将在下文中讲述。

3.4 命令行上启动的进程

代码:
  1. #include<stdio.h>
  2. #include<sys/types.h>
  3. #include<unistd.h>
  4. int main()
  5. {
  6. printf("pid= %d, ppid= %d\n",getpid(),getppid());
  7. return 0;
  8. }

重复运行多次:

说明该进程的父进程一直没有变,但是子进程每一次运行都在变化。

说明这个进程的父进程是bash;

总结:所有在命令行上启动的进程的父进程都是bash

4. 进程状态

4.1进程状态

  • R运行状态:并不意味着进程一定在运行中,它表明进程要么是在运行中或者在运行队列里。
  • S睡眠状态:意味着进程在等待事件完成,比如你的网易云音乐在没联网的情况下回卡着,因为它在等待网络的数据包,此时这个进程就在睡眠状态。
  • D磁盘休眠状态:不可中断睡眠,在这个状态的进程通常会等待IO的结束
  • T停止状态:通过发送SIGTOP信号来停止进程,可以发送SIGCONT信号让进程继续运行。
  • X死亡状态:这个状态只是一个返回状态,你不会造任务列表中看到这个状态。

S进程和R进程的转换:

我们把 task_struct(进程控制块)从 R (run_struct)状态 到 S等待队列中,叫做挂起等待(阻塞),从 等待队列到运行队列,被CPU调度,叫做唤醒进程。

4.2 进程状态的查看

ps aux / ps axj

4.3僵尸进程

  • 僵死状态是一个比较特殊的状态,当进程退出并且父进程(使用wait()系统调用)没有读取到子进程退出的返回代码时就会产生僵死状态;
  • 僵死状态会终止状态保持在进程表中,并且会一直等待父进程读取退出状态代码;
  • 所以,只要子进程退出,父进程还在,但父进程没有读取子进程状态,子进程进入Z状态。

僵尸进程的危害

  • 维护退出状态本身就是要数据维护,也属于进程的基本信息,所以保存 task_struct(PCB中),换句话说,Z状态一直不退出,PCB就要一直维护。
  • 如果一个父进程创建了很多的子进程,就是不回收,就会造成内存资源的泄漏,因为进程的数据结构本身就是占内存的。

4.4孤儿进程

  • 父进程如果提前退出,子进程就称为“孤儿进程”。
  • 孤儿进程被1号init进程领养,当然要有init进程回收。

5.进程优先级

5.1基本概念

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

5.2查看系统进程

指令: ps -l

5.2.1 PRI and NI

  • PRI就是进程的优先级,值越小其优先级就越高。
  • NI --->nice值,其表示进程可被执行的优先级的修正数值,也就是通过这个数字更改优先级。
  • PRI(new)=PRI(old)+nice;每一次PRI(old)的值都是80.也就是说,当nice为5是,PRI(new)就是85,如果改变nice=8;PRI(new)=87;
  • nice的取值范围是 -20至19,一共40个级别。

5.2.2 PRI vs NI

nice值不是进程的优先级,他们不是同一个概念,但是nice值会影响到进程的变化。可以理解为nice值是进程优先级的修正数据。

5.3 修改进程的优先级

指令: top

 具体操作的流程:

  1. 输入top指令
  2. 然后按 “r”
  3. 输入PID,然后回车
  4. 输入要优先级的修正数据--->nice值

5.4 进程的其他概念

竞争性:系统中的进程数目众多,而CPU资源少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便有了优先级。

独立性:多进程运行,需要独享各种资源,多进程运行期间互不干扰。

6.环境变量

6.1 基本概念 

环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数。

举个例子:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有关环境变量帮助编译器进行查找。

环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性。

6.2 常见环境变量

  • PATH:指定命令的搜索路径
  • HOME:指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
  • SHELL:当前shell,它的值通常是/bin/bash

6.3查看环境变量的方法

echo $NAME  //NAME:你的环境变量的名字

6.4 测试PATH

6.4.1 创建 hello.c文件

  1. #include<stdio.h>
  2. int main()
  3. {
  4. printf("helllo world\n");
  5. return 0;
  6. }

对比 ./hello 执行和 hello执行 :为什么有些指令可以直接执行,不需要带路径,而我们的二进制程序需要带路径才能执行?将我们的程序所在路径加入环境变量PATH,

export PATH=$PATH:hello程序所在路径。

6.5 环境变量的相关变量

1.echo:显示某个环境变量值

2.export:设置一个新的环境变量

3.env:显示所有环境变量

4.unset:清除环境变量

5.set:显示本地定义的shell变量和环境变量

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

  1. 命令行的第三个参数
    1. #include<stdio.h>
    2. int main(int argc,char* argv[],char* env)
    3. {
    4. for(int i=0;env[i];i++)
    5. printf("%s\n",env[i]);
    6. return 0;
    7. }
  2. 通过三方变量environ:environ不存在任何头文件中,所以要加
    1. #include<stdio.h>
    2. int main()
    3. {
    4. extern char** environ;
    5. int i=0;
    6. for(;environ[i];i++)
    7. printf("%s\n",environ[i]);
    8. return 0;
    9. }
  3. 通过函数调用获取

    1. #include<stdio.h>
    2. #include<stdlib.h>
    3. int main()
    4. {
    5. printf("%s\n",getenv("PATH"));
    6. return 0;
    7. }

6.7 环境变量通常具有全局性

环境变量通常具有全局性,可以被子进程继承下去:

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. int main()
  4. {
  5. char* env=getenv("MYENV");
  6. printf("%s\n",env);
  7. return 0;
  8. }

 将这个文件编译后执行,没有打印出任何内容,说明这个环境变量根本不存在。

导入环境变量: export MYENV=200(把本地的环境变量导入父进程),重新编译 执行后发现,输出了200.

7. 进程地址空间

上图是空间布局图。
空间布局图

先来一段代码:

  1. #include<stdio.h>
  2. #include<stdio.h>
  3. int main()
  4. {
  5. int cnt=5;
  6. int num=100;
  7. pid_t=fork();
  8. if(it==0)
  9. {
  10. while(cnt--)
  11. {
  12. printf("child is running:num=%d\n",num);
  13. sleep(1);
  14. if(cnt==3)
  15. num=200;
  16. }
  17. }
  18. else if(it>0)
  19. {
  20. while(1)
  21. {
  22. printf("parent is running: num=%d\n",num);
  23. sleep(1);
  24. }
  25. }
  26. else
  27. printf("fork fail\n");
  28. }

运行结果:

发现:父子进程,输出的地址是一致的,但是变量内容不一样。

分析:变量内容不一样,所以父子进程输出的变量绝对不是同一个变量。但是地址却是一样的,说明,该地址绝对不是物理地址。在Linux地址下,这种地址叫做虚拟地址,我们在C/C++语言所看到的地址,全部都是虚拟地址!物理地址用户是看不到的,由OS同一管理,OS必须负责将虚拟地址转化成物理地址

 

上图中可以说明,虽然子进程和父进程的数据和代码相同的,但是只是虚拟地址是相同的,而它们的物理地址是不一样的,而当子进程中的数据进程修改时,会发生拷贝(写时拷贝:内核只为新生成的子进程创建虚拟空间结构,它们来复制于父进程的虚拟究竟结构,但是不为这些段分配物理内存,它们共享父进程的物理空间,当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间),所以当子进程中的数据修改时,父进程中的数据不会被修改,因为子进程修改的数据和父进程的数据不是同一个。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/635173
推荐阅读
相关标签
  

闽ICP备14008679号