Linux基础——进程的概念和控制(操作系统级讲解)

前言

我们经常会听到一个概念——进程。但是进程并不是一个孤立的概念,需要对操作系统有比较深入的了解。所以这篇博客将在读者的脑中先对操作系统构建一个大概的印象,再对进程做了解。

冯诺依曼结构

冯·诺依曼结构也称普林斯顿结构,是一种将程序指令存储器和数据存储器合并在一起的存储器结构。程序指令存储地址和数据存储地址指向同一个存储器的不同物理位置,因此程序指令和数据的宽度相同,如英特尔公司的8086中央处理器的程序指令和数据都是16位宽

简单来说,数据和指令同时被储存在同一个储存器中。

这里的储存器就是内存,运算器和控制器共同组成了中央处理器,其他都是外设。包括了输入设备:磁盘、键盘、鼠标……,还有输出设备:磁盘、显示屏、音响……

由于内存的掉电易失,刚开机时内存里是空白的,指令和数据只能从磁盘中读取。

CPU(中央处理器),通过读取内存中的指令进行各种各样的数据处理。 体系结构决定了数据和指令只能先被拷贝到内存才能被CPU处理。

那么数据在外设、内存和CPU之间的流动并非不受控制的,那么由谁来控制?

由操作系统来控制。

操作系统

任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。笼统的理解,操作系统包括: 内核(进程管理,内存管理,文件管理,驱动管理) 其他程序(例如函数库,shell程序等等)。

设计OS的目的

与硬件交互,管理所有的软硬件资源

为用户程序(应用程序)提供一个良好的执行环境

由于直接使用操作系统的接口比较繁琐,而且需要对系统有比较深入的了解,所以使用指令(touch创建文件、rm移除文件等)、编程(cout/cin控制输入输出)来封装系统接口,使系统的操作简洁明了。 

操作系统是怎么管理进行进程管理的呢?很简单,先把进程描述起来,再把 进程组织起来!

进程的管理也满足先描述,后组织的原则。

进程

进程的基本概念

课本概念:程序的一个执行实例,正在执行的程序等

内核观点:担当分配系统资源(CPU时间,内存)的实体。

描述进程-PCB

进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。 课本上称之为PCB(process control block),Linux操作系统下的PCB是: task_struct

PCB就是一个自定义的结构体类型。

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

task_ struct内容分类

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

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

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

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

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

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

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

记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。 其他信息

 进程 = struct tast_struct结构体对象 + 磁盘中的程序代码

见一见进程

 如图所示是正在运行的程序就是一个进程,但是我们怎么看到进程的属性呢?

使用 ps  axj :

 杀死一个进程

使用 kill -9 [进程ID]:

 系统接口getpid

getpid是用来获取当前进程的ID的系统接口。

 

 内存级的目录文件proc

 而且在进程运行时删除磁盘上的代码,进程仍然照常运行。只是目录中的exe开始报警:

命令行中启动的进程,父进程都是bash。

创建子进程

使用fork创建子进程:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
    pid_t id = fork();
    if(id < 0)
    {
        perror("创建子进程失败:");
    }
    else if(id > 0)
    {
        printf("这是一个父进程 pid: %d\n",getpid());
    }
    else
    {
        printf("这是子进程 pid: %d\n",getpid());
    }

    return 0;
}

 

进程状态

为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在 Linux内核里,进程有时候也叫做任务)。

下面的状态在kernel源代码里定义:

/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};

 

 一个cpu有一个运行队列,队列中都是进程控制块。

让进程处于运行状态,本质上是让某一个进程的进程控制块(PCB)被插入到运行队列等待占用CPU的资源。

并不是只有运行队列,由于进程会有别的状态,自然也会在别的队列下等待,比如说等待外设资源时处于阻塞状态,这个进程就不在运行队列,而处在等待外设的队列。

进程的状态不同,本质是PCB为了占用不同资源处在不同队列中。

以上我们知道了进程的运行状态是进程在运行队列中等待占用CPU资源,阻塞状态是进程在阻塞队列下等待外设资源,接下来我们讲一下挂起状态

挂起状态

挂起就是将进程的代码和数据暂时存放到磁盘中。

阻塞不一定挂起,挂起一定阻塞。

挂起也有可能与其他状态组合,在其他队列中时代码和数据被存放到磁盘中。 

进程的不同状态

R

R (runing)运行状态

#include<stdio.h>  
   
int main()  
{  
    int a = 0;  
    while(1)  
    {  
        a = 1 + 1;
    } 
    return 0;                                                                                                              
}  

可以看出,这时只占用cpu资源,进程状态时R。

 S

S (sleeping) 浅度睡眠

对程序做一点改变:

 这时,由于进程等待显示器资源的时间长于等待cpu的资源,大多数时间进程的状态都是S,是一种阻塞状态。

 等待外设或者sleep,都是S状态。

 T

T (stop) 暂停状态

 先来看看kill的操作:

T状态是暂停:

使用 kill -19  进程号,就可以使得进程变为T状态,这是一种暂停状态。

 使用 kill -18  进程号,就可以使得进程变回原来的状态。

 但是这是S后面的加号没有了,并且这时ctrl + C已经不能使程序结束了,这说明进程已经变为了后端进程,只能直接用kill -9杀死。

 D

D (disk sleep) 深度睡眠

在高IO的情况下,资源紧张,操作系统为了合理利用资源会将一些进程变为深度睡眠状态,该状态的进程无法被杀死,只能通过断电或者进程自己醒来解决。

t

t (tracing  stop)  被追踪的暂停状态

 

 我们可以看到,在使用gdb打断点后,进程运行到断点之后停止,这个状态就是被追踪的暂停状态。

Z

Z (zombie) 僵尸状态

当进程结束时,并不能马上释放进程控制块,因为这时候父进程和操作系统不知道,需要父进程接收之后,子进程的状态被父进程和操作系统所知,才能释放。

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        printf("这是子进程: pid : %d  ppid : %d\n", g    etpid(),getppid());
        sleep(5);
        exit(1);
    }
    else
    {
        while(1)
        {

            printf("这是父进程: pid : %d  ppid : %d\n    ", getpid(),getppid());
            sleep(2);                                 
        }
    }
    return 0;
}

 

 我们明显看到,子进程结束后,由于父进程没有及时接收处理,子进程变成僵尸状态,进程的僵尸状态也会造成内存的泄露。

X

X (dead)  死亡状态

僵尸进程和孤儿进程

僵尸进程

像上述一样,由于子进程结束,并且父进程并没有及时接收子进程,导致子进程的数据没有被及时释放。所以说,僵尸进程是一个问题,僵尸进程造成内存泄漏等。

孤儿进程

孤儿进程是由于父进程先一步结束,这时子进程被称为“孤儿进程”。

孤儿进程被1号init进程领养,由init进程回收。

先写一个程序:

 #include<stdio.h>
 #include<unistd.h>
 #include<sys/types.h>
 #include<stdlib.h>
 int main()
 {
     pid_t id = fork();
     if(id == 0)
     {
         while(1)
         {

             printf("这是子进程: pid : %d  ppid : %d\n", getpid(),getp    pid());
             sleep(1);                                                 
         }
     }
     else
     {
 
             printf("这是父进程: pid : %d  ppid : %d\n", getpid(),getp    pid());
             sleep(3);
             exit(1);
     }
 }

在三秒钟后,父进程死亡,留下子进程被系统进程收养。

 从前台进程变为后台进程

进程优先级

权限决定了能不能做,优先级是已经可以做这件事,只是决定了这件事什么时候做。

由于资源太少,需要确定优先级,谁先谁后,在Linux中优先级用整型编号表示。

 其中,priority表示权限。

在上图中是80.

最终优先级 = 老的优先级(80) +  nice(-20 ~ 19)

 上图是我改进程的优先级的过程,由于修改优先级需要权限,第一步使用: sudo top

然后键入 r  ,想要修改的进程的pid,  最后输入nice值。

进程的其他概念

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

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

并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行

并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为 并发

进程切换

 特别值得注意的是,在进程被剥离时。cpu中寄存器中属于这个进程的数据保存到pcb中,当进程恢复运行时,再把数据拷贝到cpu中的寄存器上。

环境变量

环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数 如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但 是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性

我们自己编译生成的可执行程序和系统中的指令是一样的,但是为什么我们运行自己的可执行程序时在前面要带上路径,而指令不需要?因为指令的路径系统会自动去找,指令安装的过程其实就是一个拷贝到特定路径下的过程。

 把我们的可执行程序放到指令路径下,会污染指令池,不建议那么做。

还有一种方法,将当前路径设置为系统默认路径,使用指令:

export PATH=$PATH:想要添加的路径

 

 在图中的.bash_profile以及.bashrc,加上/etc/bashrc,使得系统执行时环境变量被自动定义和导入。

我们所改变的是在内存中的环境变量,在系统重启之后一切恢复原样。

env指令可以查看所有环境变量。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JDSZGLLL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值