【Linux】进程的概念 - Go语言中文社区

【Linux】进程的概念


一、进程的概念

程序:代码+数据

进程:代码+数据+堆栈+PCB

程序:为了完成特定功能的一系列指令的有序集合

进程:一个具有一定独立功能的程序在数据集合上的一次动态执行过程

  • 每个进程都有自己的状态

  • 每个进程都有自己的虚拟地址空间

  • 进程是操作系统分配资源的基本单位

组成:进程包含了一个程序执行过程中的所有状态信息

  • 程序的代码

  • 程序处理的数据

  • 程序计数器中的

  • 寄存器动态变化

  • 系统资源

进程与程序的联系:

  • 程序是进程的基础

  • 程序每次执行构成不同的进程

  • 进程是程序功能的体现

  • 多次执行,一个程序可以应对多个进程,通过调用关系,一个进程可以包含多个程序(多对多)

进程与程序的区别

  • 进程是动态的,程序是静态的,程序是有序代码的集合,进程是程序的执行,进程有核心态/用户态

  • 进程是暂时的,程序是永久的,进程是一个状态变化的过程,程序可长久保存

  • 进程与程序的组成不同,进程的组成包括程序、数据、和进程控制块

进程控制块(PCB---进程属性的集合)

  • 进程信息被放在一个叫进程控制块的数据结构中,Linux中的PCB是:task_struct,它里面包含着进程的信息,并且会被装载到RAM内存中

task_struct内容

  • 标识符:描述本进程的唯一标识符,用来区别其他进程

  • 状态:任务状态,退出代码,退出信号等

  • 优先级:相对于其他进程的优先级

  • 程序计数器:程序中即将被执行的下一条指令的地址

  • 内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块指针

  • 上下文数据:进程执行时处理器的寄存器中的数据

  • I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用文件列表

  • 记账信息:可能包括处理时间总和,使用的时钟数总和、时间限制、记帐号等

  • 其他信息

调度:优先级、动态变化

         从就绪中的程序中选择合适的放在CPU中执行

 

 

二、进程相关操作命令

cat /proc/sys/kernel/pid_max:系统ID号最大值

ps:报告系统当前进程

ps -ef

df:查看磁盘情况

df -ef

ps -ef | grep a.out |grep -v grep反向过滤

 

 

三、创建一个进程的一般工作

  • 1.分配一个PID(从小到大找第一个没有被使用的ID)

           【0—cat /proc/sys/kernel/pid_max】

              0号进程是内核进程,它创建1号进程。

             0号进程还将进程从物理内存搬到磁盘,和从磁盘搬到物理内存

  • 2.分配PCB,拷贝父进程的PCB的绝大部分数据

  • 3.给子进程分配资源

  • 4.复制父进程地址空间

  • 5.将子进程制成就绪状态放入就绪队列

  • 6.pid_t fork(viod)     子进程0,父进程大于1

  • 子进程是父进程的翻版,子进程具有父进程的栈、数据段、堆和执行文本段的拷贝。

  • 父进程和子进程有相同的程序文本段,但各自拥有不同的堆栈、数据段、堆段拷贝

  • 执行fork( )后每个进程均可修改自己的栈数据、堆段中变量,而不影响另一进程

pid_fork (void); 

作用:创建一个进程

返回值:子进程返回0;父进程返回大于0(实际上就是子进程的PID);当前无法创建时返回-1

  • 1.创建后,父子进程交替进行(0为子进程,父进程大于1)

  • 2.若父进程死亡,子进程变为孤儿进程,由1号进程领养

  • 3.若子进程死亡,成为僵尸进程

 

  • fork()创建的子进程是对父进程的栈、数据段、堆和执行文本的拷贝,但是这样做会浪费内存空间。避免这种浪费有两种方法:

  • 1、内核将每一个进程的代码段标记为只读,从而使进程无法修改自身代码,这样子进程和父进程可以共享同一代码段,指向相同的物理内存页帧

  • 2、对于父进程数据段、堆段和栈段的各页,内核采用写时复制技术,起初内核将进程的代码段标记为只读,使父进程和子进程指向相同的物理内存,调用fork()后内核会捕获所有子进程和父进程对代码段的修改企图,然后将要修改的代码段拷贝分配给造内核捕获的进程,之后父进程和子进程就可以修改各自的代码段拷贝,不再相互影响。

fork和vfork

  • fork是创建一个子进程,并把父进程的内存数据copy到子进程中。

  • vfork是创建一个子进程,并把子进程 和父进程的内存数据share一起共用

  • vfork不进行写时拷贝,性能相较于fork更好,但是vfork的实现在每个系统上面都存在错误,只有在vfork调用完后加上exec或者exit才会正确。

getpid()可以获取进程的PID;getppid()可以获取进程的父进程的PID

创建一个孤儿进程

创建一个僵尸进程

僵尸进程是不能用kill被杀死的,从系统中移除其的唯一方法就是杀掉他们的父进程(或等待其父进程终止),此时init将接管和等待这些僵尸进程,将它们清除掉。

  • 当创建的僵尸进程过多的时候会导致以下问题:

  • 造成内存资源的浪费

  • 内存泄漏

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

四、进程的优先级

  • ps -l(查看当前系统进程的优先级)

  • UID 执行者身份

  • PID 进程ID

  • PPID 父进程ID

  • PRI 进程可被执行的优先级,其值越小越早被执行

  • NI 进程的nice值

子进程会继承父进程的优先级,因为PCB中存储着进程的优先级,创建子进程的时候会将父进程的PCB拷贝过来。

进程的nice值    

  • 概念

    • 进程的nice值 表示进程可被执行的优先级的修正数值
  •  取值范围

    • nice取值范围:-20-19,总共40个级别

  • nice值的修改

    • PRI(new)=PRI(old)+nice 

      • 如:renice -5 -p 2600(将PID为2600的进程nice值修改为-5)

      • 如:nice -n -5 ./test  (开始执行指定nice值的进程)

    • 在进程启动之前可以通过nice来调整进程的优先级

    • 当nice的值为负值的时候,该程序的优先级值将会变小,其优先级会变高,会越快被执行。

  • 用top命令修改已存在进程的nice

    • top可以查看系统所有进程的相关信息,相当于Windows的任务管理器

      • “r”->输入进程PID->输入naice值     (修改已存在进程的nice值)
      • “k”->输入进程PID->"enter"  (杀死已存在的进程)
      • “<”向上翻页   ">"向下翻页
      • “q”(退出top)

进程的并行和并发

  • 并行:多个进程在多个CPU下分别同时运行

  • 并发:多个进程在一个CPU下采用进程切换的方式,在一段时间内,让多个进程都得以推进,一个CPU只能在某个时间内运行一个进程,不能多进程同时运行

  • 多进程程序只可能是并行的不可能是并发的

五、环境变量

环境变量在命令行参数和PCB之间

本地变量和环境变量:

  • 本地变量只能在当前进程使用,不能在子进程中使用

  • 父进程定义的环境变量能在当前进程和子进程中使用

  • 子进程定义的环境变量只能在子进程使用,不能在父进程中使用

环境变量相关指令:

  • export name=val  (定义环境变量,=左右不能加空格)

  • unset avj(删除环境变量)

  • env 获取全部环境变量 

  • echo $环境变量名  打印出相对应的环境变量值

  • unset:可以删除本地变量

设置环境变量:

  • 当前终端设置的环境变量只能在当前终端使用,离开就没有了,且只能在当前终端当前用户下使用

  • 如果想所有地方都能使用环境变量需要配置在  ~/.bash_profile

  • 如果希望在配置文件中设置的变量生效,要重启(在终端启动之前会执行~/  .bash_profile和~/ .bashrc这两个文件里的内容)

在程序中获取环境变量:

  • 获取所有的环境变量,main的第三个参数

  • 获取某一个环境变量,val=getenv(“name”)

  • 在程序中设置环境变量 ,putenv(“name=value”)等号左右不能有空格

查看进程树 pstree

查看进程树中bash及其前两行后两行  pstree |grep -A2 -B2 “bash”

六、程序地址空间

 

子进程是父进程的拷贝,当子进程修改时,先将值拷贝到另一块物理内存中,将页表中的只读改为可读可写,再修改值

因此,修改后父子进程虚拟地址相同,物理地址不同

 

错误处理

linux绝大部分函数出错返回值都是-1

int errno;//linux函数出错后将错误编号放在errno全局变量中

if(fork()==-1)
    errno(错误码编号)

#include<errno.h>
    char* strerror(errno);通过错误编号,返回错误信息

#include<string.h>
    printf("%s",strerror(errno));打印错误信息

perror(cmd="fork")
{
    printf("%s : %sn",cmd,strerror(errno));
}

 

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/lxf_style/article/details/81235001
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2019-10-27 20:40:24
  • 阅读 ( 1273 )
  • 分类:Linux

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢