0
点赞
收藏
分享

微信扫一扫

ArcGIS API for JavaScript 4.x 教程(三)显示自定义底图样式

言诗把酒 2023-08-09 阅读 35
linux

目录

🌞专栏导读

🌛什么是进程

⭐什么是PCB? 

🌛查看进程 

🌛如何通过系统调用查看进程PID

🌛fork

🌞认识进程状态

🌛查看进程状态 

🌛R状态

 ⭐例如:

🌛S状态 

🌛D状态 

🌛T状态

🌛t状态:

🌛X状态 

🌛Z状态

⭐僵尸进程

⭐僵尸进程的危害 

🌛孤儿进程


🌞专栏导读

🌛什么是进程

有小伙伴就会问了,什么是进程呢?

进程=内核关于进程的相关数据结构+当前代码的内容和数据

⭐什么是PCB? 

从操作系统理解进程概念-------先描述,后组织
为了使参与并发执行的程序能独立的运行,必须为之配置一个专门的数据结构-----task_struct,称为进程控制块(PCB进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。

系统利用PCB来描述进程的基本情况和运行状态,进而控制和管理进程(也就是组织)进程。 相应地,由程序段、相关数据段和PCB三部分构成了进程映像(进程实体)。所谓创建进程,实质上是创建进程映像中的PCB;而撤销进程,实质上是撤销进程的PCB。值得注意的是,进程映像是静态的,进程则是动态的。
进程的唯一标志—PCB
综上所述,进程的定义大体分为以下几点

  • 进程是程序的一次执行
  • 进程是一个程序及其数据在处理机上顺序执行时所发生的活动
  • 进程是具有独立功能的程序在一个数据集合上运行过程,它是系统进行资源分配和调度的一个独立单位

动态性:进程是程序的一次执行,它有着创建、活动、暂停、终止等过程,具有一定的生命周期,是动态地产生、变化和消亡的。动态性是进程最基本的特征。
并发性:指多个进程实体,同存于内存中,能在一段时间内同时运行,并发性是进程的重要特征,同时也是操作系统的重要特征。引入进程的目的就是为了使程序能与其他进程 的程序并发执行,以提高资源利用率。
独立性:指进程实体是一个能独立运行、独立获得资源和独立接受调度的基本单位。凡未建立PCB的程序都不能作为一个独立的单位参与运行。
异步性:由于进程的相互制约,使进程具有执行的间断性,即进程按各自独立的、 不可预知的速度向前推进。异步性会导致执行结果的不可再现性,为此,在操作系统中必须 配置相应的进程同步机制。
结构性:每个进程都配置一个PCB对其进行描述。从结构上看,进程实体是由程序段、数据段和进程控制段三部分组成的。
进程的状态和转换

task_ struct内容分类

🌛查看进程 

#include<stdio.h>

int main()
{
  while(1)
  {
    printf("我是一个进程\n");
    sleep(1);
  }
  return 0;
}
  • 这是一个简单的进程(myprocess)
ps axj | head -1 && ps axj | grep myprocess | grep -v grep
  • 这是查看进程的指令!

🌛如何通过系统调用查看进程PID

运行截图: 

 注意,返回值类型未pid_t其实就是size_t。通过一下代码进行测试:

#include<stdio.h>
#include<sys/types.h>

int main()
{
  while(1)
  {
    printf("我是一个进程,我的PID是:%d,我的父进程是:%d\n",getpid(),getppid());
    sleep(1);
  }
  return 0;
}

运行截图: 

🌛fork

首先我们通过man手册认识一下fork

$ man fork

运行截图: 

 简单说明就是fork用来创建子进程,在父进程中,fork的返回值为子进程的PID;在子进程中返回值为0。

#include<stdio.h>
#include<sys/types.h>

int main()
{
  printf("fork调用之前的内容.....\n");
  fork();
  printf("fork调用之后的内容.....\n");

  return 0;
}

运行截图: 

fork的功能很强大,我们一般需要与if配合使用进行分流。还记得上面提到的fork的返回值吗?子进程返回值为0,父进程返回值为子进程PID,可以此作为分流的依据。例如:

#include<stdio.h>
#include<sys/types.h>

int main()
{
  pid_t ret = fork();

  if(ret==0)
  {
    // 子进程
    while(1)
    {
       printf("我是子进程,我的PID是:%d,我的父进程是:%d\n",getpid(),getppid());
       sleep(1);
    }
  }
  else 
  {
    // 父进程
    while(1)
    {
       printf("我是父进程,我的PID是:%d,我的父进程是:%d\n",getpid(),getppid());
       sleep(1);
    }
  }

  return 0;
}

运行截图:

此刻父子进程都在运行。那么问题来了——

  • 请问为什么此时ifelse竟然能够同时执行?也就是fork为什么会有两个返回值

 进程间是互相独立的

例如qq与微信同时运行,两个并无关联,互不影响。

🌞认识进程状态

进程在其生命周期内,由于系统中各进程之间的相互制约关系及系统的运行环境的变化,使得进程的状态也在不断地发生变化(一个进程会经历若干种不同状态)。通常进程有以下五种状态,前三种是进程的基本状态。

进程转换图

Linux中进程状态一般有:

 

🌛查看进程状态 

指令:

ps axj | head -n1 && ps axj | grep myprocess | grep -v grep

🌛R状态

当你在电脑上同时运行很多程序,例如你敲代码的时候,还听着某个软件播放的歌曲,或者在浏览器之间来回切换。请问此时这些所有的应用都在CPU运行吗?

答案是,并不是这样的。

在CPU进行工作的时候,会存在一个进程运行的队列。队列维护的内容是一个个task_struct结构体的指针。在该队列中维护的进程都处于R状态,且等着被CPU所调度。

 ⭐例如:

那么有什么办法等看到R状态呢?我们将上面的代码略作修改:

#include<stdio.h>
#include<unistd.h>

int main()
{
  while(1)
  {
    //printf("hello myprocess\n");
  }
  return 0;
}

 运行截图:

 如上图所示,当我们不再访问外设,而是只不停地做重复的运算,此时CPU会一直被调度,就能看到R状态了。

🌛S状态 

S状态称为休眠状态。休眠状态本质是一种阻塞。

例如,当一个进程运行到一半,需要从磁盘上获取很大的一块数据,那么就要花费较久的时间。此时OS的处理方式是,让该进程继续等待它要的数据,但是要求你不能在等待资源的时候还占用着CPU,于是该进程就被OS安排到某个地方进行等待,这时该进程就处于S状态。


运行截图

如上图所示,当进程等待用户从键盘上输入的数据时,它就处于睡眠状态。 

🌛D状态 

D状态也是一种休眠状态,但是它又有个名字叫做磁盘休眠状态或者不可中断休眠。那么如何看待S状态与D状态的区别呢?

🌛T状态

T状态称为停止状态,就是让某个进程暂停一下。

1 #include<stdio.h>
  2 #include<unistd.h>
  3 
  4 int main()
  5 {
  6   int count = 0;
  7    while(1)
  8    {
  9      //printf("hello myprocess\n"); 
 10      printf("我再运行吗??%d\n",count++);
 11      sleep(1);                                           
 12    }
 13    return 0;
 14 }

当程序开始运行后,此时向进程发送暂停的信号:

$ kill -18 (进程PID)

运行截图: 

此外,我们还可以发送继续的信号让该进程继续执行:

$ kill -18 (进程PID)

 运行截图:

 注意:

进程继续在运行了。但是我们发现有一个地方好像和之前不一样了,S后面是不是一直有一个+号来着?我们也不知道+是干嘛的,只知道他现在好像消失了。

“+” 代表在前台运行,没有”+“表示在后台运行;


之前我们在终止一个程序时,习惯使用Ctrl + c ,但是现在好像对于后台在运行的进程失效了,此时我们需要掌握一条新的指令来”杀掉“进程:

$ kill -9 (进程PID)

🌛t状态:

 例如在调试时,我们设置了几个断点。当进程在该断点处停下来时,该进程就处于暂停状态。

 

 

可以看见我们在第十行打了断点,此时观看进程状态:

 我们在调试中运行一下:

观看运行状态:

🌛X状态 

  • X状态为死亡状态是一个瞬时状态不易观察,暂且认为它不重要;

🌛Z状态

  • Z状态被称为僵尸状态。顾名思义,一个进程死了(退出了)但没有”收尸“,就成了”僵尸“。具体一点,当一个进程退出时如果它的父进程没有读取到该进程退出时返回的退出状态码,该进程就会变成僵尸进程。

⭐僵尸进程

#include<stdio.h> 
#include<unistd.h>

int main() 
{
  pid_t id = fork();

  if(id == 0)
  {
    while(1)
    {
      printf("我是子进程,我在运行,pid:%d,ppid:%d\n",getpid(),getppid());
      sleep(1);
    }
  }
  else if(id > 0) 
  {
    while(1)
    {
      printf("我是父进程,我在运行,pid:%d,ppid:%d\n",getpid(),getppid());
      sleep(1);
    }
  }
  return 0;
}

当我们运行程序后,能看到程序正常的在运行; 

 此时当我们执行指令将子进程”杀“掉,子进程就会变成僵尸进程;

$ kill -9 (子进程PID)

其中我们能看到一个英文单词——defunct就是僵尸的意思。 

⭐僵尸进程的危害 

  • 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(即PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护
  • 一个父进程创建了很多子进程,就是不回收,就会造成内存资源的浪费。因为数据结构对象本身就要占用内存。

🌛孤儿进程

运行该程序,我们使用kill命令”杀“掉父进程,此时再来查看进程信息:

如上图所示,子进程发生了两个变化。一是子进程的PPID,二是子进程变为在后台运行了。 

当子进程的父进程挂掉之后,子进程会被1号进程领养。该进程也被称为孤儿进程。

  • 那么为什么要进行领养呢?

原因是孤儿进程会被init进程(1号进程)的进程收养,当然在子进程结束时也会由init进程完成对它的状态收集工作,因此一般来说,孤儿进程并不会有什么危害.

 

举报

相关推荐

0 条评论