Linux——进程概念、进程创建、僵尸进程、孤儿进程、环境变量、程序地址空间详解

Linux——进程概念、进程创建、僵尸进程、孤儿进程、环境变量、程序地址空间详解

  • 进程概念
    • 进程基本概念
    • 查看进程
  • 进程创建
  • 进程状态
    • 僵尸进程
    • 孤儿进程
    • 守护进程
  • 环境变量
  • 程序地址空间
      • 内存管理

进程概念

进程基本概念

从用户角度:进程就是一个正在运行中的程序。

操作系统角度:操作系统运行一个程序,需要描述这个程序的运行过程,这个描述通过一个结构体task_struct{}(task_struct{}是Linux内核中的一种数据结构,被装载在RAM里,里面包含着进程的信息)来描述,统称为PCB,因此对操作系统来说进程就是PCB(process control block)程序控制块

进程的描述信息有:标识符PID,进程状态,优先级,程序计数器,上下文数据,内存指针,IO状态信息,记账信息。都需要操作系统进行调度。

查看进程

进程的信息可以通过ls /proc系统文件来查看(如果要获取PID为1的进程信息,通过ls /proc/1来查看):
Linux——进程概念、进程创建、僵尸进程、孤儿进程、环境变量、程序地址空间详解_第1张图片
也可以使用ps -ef -aux(较详细)指令来直接显示进程状态:
Linux——进程概念、进程创建、僵尸进程、孤儿进程、环境变量、程序地址空间详解_第2张图片

进程创建

Linux中非常重要的函数——fork(),它从已存在的进程中创建一个新进程。新进程为子进程,而原进程为父进程。
创建进程:pid_t fork(void)

为什么创建子进程?
根据需求,区分父子进程,让各自运行不同代码去解决不同问题,根据返回值不同来区分。
返回值:
父进程:返回值大于0,子进程的pid
子进程:返回值等于0
若失败则返回-1;
Linux——进程概念、进程创建、僵尸进程、孤儿进程、环境变量、程序地址空间详解_第3张图片
可明显看出子进程和父进程的输出结果不一样
在这里插入图片描述
总的来说:子进程复制pcb的信息,代码共享,但是子进程并非从头开始,而是从fork()函数之后开始,数据独有

网络上对fork()的理解:
(1)一个进程进行自身的复制,这样每个副本可以独立的完成具体的操作,在多核处理器中可以并行处理数据。这也是网络服务器的其中一个典型用途,多进程处理多连接请求。
(2)一个进程想执行另一个程序。比如一个软件包含了两个程序,主程序想调起另一个程序的话,它就可以先调用fork来创建一个自身的拷贝,然后通过exec函数来替换成将要运行的新程序。

进程状态

进程状态一般有:就绪态,阻塞态,运行态

在Linux下:R运行状态,S睡眠状态,D磁盘休眠状态,T停止状态,X死亡状态

这些当我们使用指令ps -aux和 ps -aux | grep status 就可以看到

僵尸进程

在进程状态中有两个比较特殊的存在:僵尸进程和孤儿进程
僵尸进程是进程退出后,但是资源没有释放,处于僵死状态的进程。

产生原因

子进程先于父进程退出,操作系统检测到进程的退出,通知父进程,但是父进程这时候正在执行其他操作,没有关注这个通知,这时候操作系统为了保护子进程,不会释放子进程资源,因为子进程的PCB中包含有退出原因。这时候因为既没有运行也没有退出,因此处于僵死状态,成为僵尸进程。

下面用代码示例说明:
Linux——进程概念、进程创建、僵尸进程、孤儿进程、环境变量、程序地址空间详解_第4张图片
z这个标志就是僵尸进程的标志,僵尸进程的危害很大,kill这个命令普通进程可杀死,但是杀不死僵尸进程。
在这里插入图片描述
僵尸进程的危害?

1.进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态。

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

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

4.内存泄漏

那么怎么避免僵尸进程的产生?
一般处理就是关闭父进程,这样僵尸子进程也随之消失了。所以我们最好设置进程等待,等待子进程完成了工作,并且通知了父进程之后,然后再退出。

孤儿进程

父进程先于子进程退出,父进程退出后,子进程成为后台进程,并且父进程为1号进程(孤儿进程被1号init进程领养,当然要有init进程回收喽)。
特性:运行在后台,父进程成为1号进程。孤儿进程退出后不会成为僵尸进程—— 退出后父进程对其资源进行读取了

守护进程

守护进程(精灵进程):是一种特殊的孤儿进程,因为先成为孤儿进程才能运行在后台 —— 特殊(脱离了与终端的关联+会话的关联)的孤儿进程

环境变量

环境变量:保存程序运行环境的变量

相关指令:

      env:查看所有的环境变量
      set:查看环境中所有变量
      echo:打印某个指定变量的数据
      
特性:具有进程之间的传递性

export:设置环境变量
unset:删除变量

常见的环境变量:HOME SHELL USER PATH

重要的环境变量:PATH —— 程序的默认运行路径
Linux——进程概念、进程创建、僵尸进程、孤儿进程、环境变量、程序地址空间详解_第5张图片
在程序中获取环境变量的接口:

char *getenv(const char *env_name)	//接口获取
name:环境变量名称
返回值:对应name环境变量的数据,如果找不到返回NULL;

Linux——进程概念、进程创建、僵尸进程、孤儿进程、环境变量、程序地址空间详解_第6张图片
如下结果直接显示环境变量的具体内容:
在这里插入图片描述
如果将变量的内容名称改为MYVAL,给它设值100,echo可以得到他的值是100,但是程序运行后却没有。这时用export指令设置环境变量,因此这就是环境变量具有进程间传递性的一个应用。
Linux——进程概念、进程创建、僵尸进程、孤儿进程、环境变量、程序地址空间详解_第7张图片

程序地址空间

Linux——进程概念、进程创建、僵尸进程、孤儿进程、环境变量、程序地址空间详解_第8张图片
进程的地址空间(可以说是程序地址空间),并且每个进程都有一份,整体内存也就这么大,不可能每个进程都一样,呢怎么分配?
答:给每一个进程虚拟一个完整的地址空间

地址是什么?地址是内存的编号,指向内存的一块区域
Linux——进程概念、进程创建、僵尸进程、孤儿进程、环境变量、程序地址空间详解_第9张图片
输出结果:
Linux——进程概念、进程创建、僵尸进程、孤儿进程、环境变量、程序地址空间详解_第10张图片
我们发现,输出出来的变量值和地址是一模一样的,因为子进程按照父进程为模版,父子并没有对变量进行进行任何修改。将代码稍加改动后:
Linux——进程概念、进程创建、僵尸进程、孤儿进程、环境变量、程序地址空间详解_第11张图片
输出结果:
Linux——进程概念、进程创建、僵尸进程、孤儿进程、环境变量、程序地址空间详解_第12张图片
我们发现,父子进程,输出地址是一致的,但是变量内容不一样

得出如下结论:

变量内容不一样,所以父子进程输出的变量绝对不是同一个变量
但地址值是一样的,说明,该地址绝对不是物理地址
在Linux地址下,这种地址叫做 虚拟地址

我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理(OS必须负责将 虚拟地址 转化成 物理地址 )

程序地址空间,本质上是操作系统为进程通过 mm_struct 描述的虚拟的地址空间,让每个进程都能访问一个完整的虚拟地址,经过映射之后,实现在物理内存上的离散存储,提高内存利用率,并且提高了内存访问控制。

Linux——进程概念、进程创建、僵尸进程、孤儿进程、环境变量、程序地址空间详解_第13张图片

内存管理

问题:如何通过虚拟地址找到物理内存中的存放位置,访问数据

内存管理方式:
分段式:将虚拟地址空间分为多个段(代码段、数据段…),分段式管理中,会给每个进 程都创建一个段表。

虚拟地址组成:短号+段内偏移

分段式内存管理方式:
Linux——进程概念、进程创建、僵尸进程、孤儿进程、环境变量、程序地址空间详解_第14张图片
取出虚拟地址中的段号,在段表中通过段号找到段表项,取出物理段起始地址,加上段内偏移量得到某个变量的实际存储地址。

分页式内存管理:
虚拟地址组成:页号+业内偏移
页表:记录了页号,权限位,缺页中断位,物理块起始地址…

在虚拟地址空间中,将整个空间划分为一个个小的分页。

分段式管理:基于使用性质划分的段,将地址空间进行分段管理,对于内存利用率并没有太大提高,但是对于程序地址管理比较友好。
分页式管理:基于存储划分的分页分块,将地址空间划分成一个个小的页面进行管理,提高了内存利用率,并且进行了权限管理,提高了内存访问控制。

你可能感兴趣的:(Linux,linux)