Linux进程——学习笔记

Posted 正义的伙伴啊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux进程——学习笔记相关的知识,希望对你有一定的参考价值。

认识操作系统

冯诺依曼体系结构是计算机的基本结构


该结构规定了 cpu(中央处理器) 只和内存进行交互,任何其他设备 例如 输出设备 和 输入设备 都必须把数据加载到内存上,才能被cpu读写

那么为什么要把所有数据都加载到内存中才能被cpu读写?
这是因为不同硬件数据传输的效率不同,cpu读取数据的速度 是 硬盘上传数据的1000万倍,这样两种设备如果直接交互会导致cpu性能大幅度下降,所以需要内存来在中间来当一个缓冲,内存的容量虽然比硬盘小不少,但是传输速度比硬盘快不少,但是和cpu的速度还是差远了,于是 内存 上还有多层的高速缓存,这些高速缓存直接和cpu交互。传输速度越快的设备 ,单位容量的造价越高,所以分级存储是一种最经济的结构,这也是电脑能被大众使用的原因

操作系统

操作系统是进行软硬件资源管理的软件
例如:数据从 外设上加载到内存中,再从内存中被cpu使用、我们写一个c语言程序在屏幕上打印一段文字,这段文字是怎么从内存,到屏幕上(外设)。这些全部是操作系统的功劳


上图显示了操作系统的大致功能:

  • 自上而下:将用户的指令、数据传递给底层的硬件
  • 自下而上:将由硬件输入的数据传递到内存,以供用户使用
    从上还可以发现计算机体系是一个层状结构,任何操作都要经过中间操作系统之手,每一层之间下层通过暴露接口来供上层调用!
    从中可以看出 操作系统实际上是对上层、下层的数据的 管理,实际上是一个管理者的角色

所以为什么要有操作系统:

  1. os方便用户使用,降低了计算机的使用成本
  2. os给用户、开发人员提供了一个良好的运行环境
  3. os对下更加高效的使用硬件资源

os对于这么多数据 ,又该如何管理?
首先操作系统用一个抽象的概念来描述这些数据的大体特征 ,虽然这些数据 功能上可能大不相同。这点有点像C语言中类的结构体,这样操作系统就可以以相同的方式来看待这些数据了。一旦这些 数据 以同一种方式去看待 , 这些 数据 就会变成一个一个相同的结构体个体(结构体包含这些数据的描述信息),接下来要管理这些 个体 就可以数据结构来进行管理。通过对数据结构的增删查改来完成对数据的操作。
接下来学的:进程进程地址空间 都是这种抽象化之后的结果,

进程的概念

进程实际上是操作系统对程序的一种管理
一个电脑上可以跑多个程序,为了保证这些程序有序运行、防止恶意软件修改 计算机软件 ,这时就需要os这张看不见的“大手”,将他们安排的明明白白。

os将所有程序抽象成了一个结构体PCB(Linux下为pcb为task_struct结构体),该结构体内有一个进程可能用到的所有信息。这样不同的程序就可以 用一个进程来描述了!进程之间引入了结构体——双向链表 来对这些独立的结构体进行管理。


上图就完成了一个多程序同时运行的情况,从上图我们发现os实现了如下的转换:
os对程序的管理 -> os对进程的管理 -> 对双链表的管理

task_struct的内容

下面了解一下task_struct 的内容

  • 标识符(pid):不同进程如何区分?要有唯一的标识符来区分不同的进程,task_struct用一串数字(pid,ppid)来区分不同的进程,和他们之间的关系
  • 状态:操作系统通过识别task_struct中状态的标识符来了解进程的状况,比方说:有的程序被终止等待被销毁,这时也需要对应的状态来标识,让操作系统来回收这个进程。
  • 优先级:不同进程谁应该优先被执行?需要优先级来标识
  • 程序计数器:程序即将执行的下一条指令的地址,这个是由寄存器来完成,cpu每次执行指令时,都由一个叫eip的寄存器将下一条指令的地址传给cpu计算,例如后面 进程等待 程序计数器就至关重要
  • 上下文数据:进程执行时处理器的寄存器的数据,与进程的切换有关。例如一个进程在运行中,由于某些原因,需要被暂时停止执行,让出cpu。需要进程保存自己的所有临时数据,以便后面再调用该进程时对数据进行恢复!

进程抢占:

进程放在cpu上之后,不是一直运行直到运行结束,每个进程都有一个运行时间单位:时间片,所以有时候当一个进程的时间片到了或者来了一个优先级更高的进程,当前cpu上运行的进程就要被换下来。这就是进程的抢占。

并行 和 并发

  • 单核cpu:一个cpu上只能对一个进程进行计算,那又是如何完成多进程呢?单核cpu通过进程间的快速切换,来达到在一段时间内,让所有进程代码都得到推进——并发
  • 多核cpu:每个cpu上运行一个进程以达到多进程——并行

总结:
进程 = 程序 + 内核申请的PCB数据结构

查看进程

使用 ps -ajx | grep 特定进程的pid

创建进程

创建一个进程需要用到函数pid_t fork()
fork的特性:

  1. 有两个返回值
  2. fork出来的进程叫做子进程,父子进程代码共享,数据各自私有一份

fork的返回值:

  1. 如果是子进程,返回的是0
  2. 如果是父进程,则返回的是子进程的pid

为什么fork有两个返回值:
我们一般认为函数执行到返回值时,主体函数已经执行完了,返回值只是给调用函数返回一个执行结果,所以fork函数主体执行完成时,就已经有两个进程了,两个进程各自执行返回语句,就有了两个返回值

理解fork:

  1. 从用户的角度去看:执行完fork之后,会有两个独立的进程来执行后续的代码,这实际上就是 父子进程之间代码共享,数据各自私有一份。之所以数据要私有一份,是因为进程的独立性
  2. 从os的角度去看:子进程以父进程为模板,其中代码是共享的,数据是写时拷贝

用if语句对父子进程进行分流

首先接受两个函数:
pid_t getpid():返回该进程的pid
pid_t getppid():返回该进程的ppid(父进程的pid)

 #include<stdio.h> 
  2 #include<unistd.h>                                                                             
  3 int main()
  4          
  5   int a=fork();
  6   if(a==0)     
  7          
  8     while(1)
  9            
 10       printf("i am son:   pid: %d  ppid:%d   \\n",getpid(),getppid()); //返回子进程的pid、ppid
 11       sleep(1);                                                      
 12               
 13     
 14   else
 15     while(1)
 16            
 17       printf("i am father:   pid: %d  ppid:%d   \\n",getpid(),getppid()); //返回父进程的pid、ppid
 18       sleep(1);                                                         
 19                                                                        
 20               
 21   return 0;
 22           


在该程序运行的时候,用ps -axj去查看进程状态,发现两个进程都在同时运行

进程的状态

在查看进程状态时,有一列是STAT

这一列显示的是进程的状态,进程的状态在os对进程的管理中也是一个非常重要的信息

  • R运行状态(running):代表进程在要么在运行,要么在运行队列中等待调用
  • S运行状态(sleeping):浅度休眠意味着进程在等待事件的完成,可以对外部事件作出反应,上面的例子中父子进程就是S状态,他们在等待printf将打印的内容刷新到屏幕上
  • D运行状态(disk sleep):深度睡眠状态,无法对外部事件作出反应,即便是操作系统也无法将其杀掉,只能等D自己执行结束。例如:进程A在等待内存想磁盘写入大量数据时,就进入了D状态
  • T运行状态(stopped):进程被停止、挂起
  • X运行状态(dead):这是进程被回收之后的状态,你不会在任务列表里看到这个状态
  • Z运行状态(zonbie):Z状态是一种持续的状态,进程运行结束、退出的时候,会把数据和代码释放,但是PCB是不会释放的,退出的过程中进程会把在执行过程中的执行情况或执行结果写入PCB中,这些进程的返回信息会被os或者他的父进程读取,读取成功之后该进程的PCB才会被释放。在父进程未读取PCB内容的这段时间内,该进程一直处于Z状态,这种进程叫做“僵尸进程” ,且无法被杀掉。总之:僵尸进程是为了保存子进程的运行结果直到被父进程读取(回收)

僵尸进程的危害:僵尸进程由于无法被杀掉,所以os要不断的去维护该进程,所以会一直占用空间,导致内存泄漏。

孤儿进程

孤儿进程就是父进程在子进程运行完之前就已经退出,子进程就无法被父进程回收,此时孤儿进程会被1号进程领养并进行回收

进程的优先级

为什么需要进程的优先级?
进程的优先级决定了进程的执行的顺序,本质上还是cpu的资源有限,优先级决定了cpu的资源该如何分配

优先级和权限的区别
权限:决定你是否能得到资源
优先级:你一定能得到资源只是先后顺序的问题

如何查看进程的优先级?
ps -al
以上信息中:
UID:操作系统用来标识用户的号码
PID:操作系统用来标识进程的号码
PPID:操作系统用来标识 子进程的父进程的号码
PRI:当前进程的优先级
NI:代表这个进程的NICE值,我们通过修改这个值来修改进程的优先级

PRI和NI

  • PRI
    PRI代表的是该进程的优先级,PRI的值越小,优先级越高
  • NI
    NI就是NICE值,一个进程对应一个NICE值,我们修改PRI是通过修改NICE值来完成,关系式为:PRI(new)=PRI(old) + NICE 也就是:修改后的PRI的值等于修改前的PRI值+NICE
    注意: PRI(old)值始终是80,也就是说PRI始终以80为基准来修改的,NICE的取值范围是[-20,20)
    为什么PRI的基准值要设定成始终是80?
    有一个基准自方便调整,而且会始终把PRI的范围控制在[60,100),不会出现较为特别大 或者 特别小的优先级,比方说:有一个恶意程序可以通过多次修改NICE值将PRI修改到一个特别大的值

如何修改优先级

  1. 首先使用ps -al查看进程的pid,记住这个pid值

  2. 输入top,就会进入一个类似于Windows中任务管理器面板的界面,输入r ,并输入修改进程的pid

  3. 输入NICE值

总结
Linux的优先级由PRI和NI共同决定,os为了保证进程之间的公平竞争,所以把 每次PRI的修改的基准值都设为了80 其次把NI的取值范围限制死。

以上是关于Linux进程——学习笔记的主要内容,如果未能解决你的问题,请参考以下文章

Linux学习笔记(12)-进程间通信|管道

Linux内核分析——第六周学习笔记20135308

学习笔记

Linux学习笔记4

关于linux操作系统中进程相关问题的学习笔记

Linux学习笔记--进程控制