Linux——进程概念进程创建僵尸进程孤儿进程环境变量程序地址空间详解
Posted 康x呀
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux——进程概念进程创建僵尸进程孤儿进程环境变量程序地址空间详解相关的知识,希望对你有一定的参考价值。
进程概念
进程基本概念
从用户角度:进程就是一个正在运行中的程序。
操作系统角度:操作系统运行一个程序,需要描述这个程序的运行过程,这个描述通过一个结构体task_struct(task_struct是Linux内核中的一种数据结构,被装载在RAM里,里面包含着进程的信息)来描述,统称为PCB,因此对操作系统来说进程就是PCB(process control block)程序控制块。
进程的描述信息有:标识符PID,进程状态,优先级,程序计数器,上下文数据,内存指针,IO状态信息,记账信息。都需要操作系统进行调度。
查看进程
进程的信息可以通过ls /proc系统文件来查看(如果要获取PID为1的进程信息,通过ls /proc/1来查看):
也可以使用ps -ef -aux(较详细)指令来直接显示进程状态:
进程创建
Linux中非常重要的函数——fork(),它从已存在的进程中创建一个新进程。新进程为子进程,而原进程为父进程。
创建进程:pid_t fork(void)
为什么创建子进程?
根据需求,区分父子进程,让各自运行不同代码去解决不同问题,根据返回值不同来区分。
返回值:
父进程:返回值大于0,子进程的pid
子进程:返回值等于0
若失败则返回-1;
可明显看出子进程和父进程的输出结果不一样
总的来说:子进程复制pcb的信息,代码共享,但是子进程并非从头开始,而是从fork()函数之后开始,数据独有
网络上对fork()的理解:
(1)一个进程进行自身的复制,这样每个副本可以独立的完成具体的操作,在多核处理器中可以并行处理数据。这也是网络服务器的其中一个典型用途,多进程处理多连接请求。
(2)一个进程想执行另一个程序。比如一个软件包含了两个程序,主程序想调起另一个程序的话,它就可以先调用fork来创建一个自身的拷贝,然后通过exec函数来替换成将要运行的新程序。
进程状态
进程状态一般有:就绪态,阻塞态,运行态
在Linux下:R运行状态,S睡眠状态,D磁盘休眠状态,T停止状态,X死亡状态
这些当我们使用指令ps -aux和 ps -aux | grep status 就可以看到
僵尸进程
在进程状态中有两个比较特殊的存在:僵尸进程和孤儿进程
僵尸进程是进程退出后,但是资源没有释放,处于僵死状态的进程。
产生原因:
子进程先于父进程退出,操作系统检测到进程的退出,通知父进程,但是父进程这时候正在执行其他操作,没有关注这个通知,这时候操作系统为了保护子进程,不会释放子进程资源,因为子进程的PCB中包含有退出原因。这时候因为既没有运行也没有退出,因此处于僵死状态,成为僵尸进程。
下面用代码示例说明:
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 —— 程序的默认运行路径
在程序中获取环境变量的接口:
char *getenv(const char *env_name) //接口获取
name:环境变量名称
返回值:对应name环境变量的数据,如果找不到返回NULL;
如下结果直接显示环境变量的具体内容:
如果将变量的内容名称改为MYVAL,给它设值100,echo可以得到他的值是100,但是程序运行后却没有。这时用export指令设置环境变量,因此这就是环境变量具有进程间传递性的一个应用。
程序地址空间
进程的地址空间(可以说是程序地址空间),并且每个进程都有一份,整体内存也就这么大,不可能每个进程都一样,呢怎么分配?
答:给每一个进程虚拟一个完整的地址空间
地址是什么?地址是内存的编号,指向内存的一块区域
输出结果:
我们发现,输出出来的变量值和地址是一模一样的,因为子进程按照父进程为模版,父子并没有对变量进行进行任何修改。将代码稍加改动后:
输出结果:
我们发现,父子进程,输出地址是一致的,但是变量内容不一样
得出如下结论:
变量内容不一样,所以父子进程输出的变量绝对不是同一个变量
但地址值是一样的,说明,该地址绝对不是物理地址!
在Linux地址下,这种地址叫做 虚拟地址
我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理(OS必须负责将 虚拟地址 转化成 物理地址 )
程序地址空间,本质上是操作系统为进程通过 mm_struct 描述的虚拟的地址空间,让每个进程都能访问一个完整的虚拟地址,经过映射之后,实现在物理内存上的离散存储,提高内存利用率,并且提高了内存访问控制。
内存管理
问题:如何通过虚拟地址找到物理内存中的存放位置,访问数据
内存管理方式:
分段式:将虚拟地址空间分为多个段(代码段、数据段…),分段式管理中,会给每个进 程都创建一个段表。
虚拟地址组成:短号+段内偏移
分段式内存管理方式:
取出虚拟地址中的段号,在段表中通过段号找到段表项,取出物理段起始地址,加上段内偏移量得到某个变量的实际存储地址。
分页式内存管理:
虚拟地址组成:页号+业内偏移
页表:记录了页号,权限位,缺页中断位,物理块起始地址…
在虚拟地址空间中,将整个空间划分为一个个小的分页。
分段式管理:基于使用性质划分的段,将地址空间进行分段管理,对于内存利用率并没有太大提高,但是对于程序地址管理比较友好。
分页式管理:基于存储划分的分页分块,将地址空间划分成一个个小的页面进行管理,提高了内存利用率,并且进行了权限管理,提高了内存访问控制。
以上是关于Linux——进程概念进程创建僵尸进程孤儿进程环境变量程序地址空间详解的主要内容,如果未能解决你的问题,请参考以下文章