进程基础

什么是进程?

进程的概念:程序的一个执行实例,正在执行的程序。简单来说,比如你打开了一个APP这就是一个进程,在Linux系统下,在命令行一个ls的命令也是一个进程。从内核的角度来说,进程是担当分配系统资源(CPU时间,内存)的实体。

怎么描述进程

进程的所有信息都放在一个叫做进程控制块的数据结构中,称它为PCB。Linux操作系统下的PCB称为task_struct。
每一个进程都有一个task_struct,这个结构体用来描述一个进程,里面存放着进程的各种信息。进程的PCB用一个双向链表连接,当有进程创建,就在链表上添加一个task_struct,同样当一个进程销毁,就删除一个task_struct。那task_struct中的的具体内容又都包含哪些呢?

task_struct

  1. 标识符:用来描述进程,每一个进程都有一个唯一的标识符,进程pid;
  2. 状态:任务状态,退出代码,退出信号等。
  3. 优先级:这么多进程,系统怎么知道先执行哪一个呢?所以当然要有优先级了,就是当前进程相对与其它进程的优先级。
  4. 程序计数器:程序中即将被执行的下一条指令的地址。
  5. 内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。
  6. 上下文数据:进程执行时处理器的寄存器中的数据。
  7. I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的 。
  8. 记账信息:包括处理器时间的总和,使用得时钟数总和,时间限制,记账号等。

进程调度算法

  1. 时间片轮转调度算法(RR):给每个进程固定的执行时间,根据进程到达的先后顺序让进程在单位时间片内执行,执行完成后便调度下一个进程执行,时间片轮转调度不考虑进程等待时间和执行时间,属于抢占式调度。优点是兼顾长短作业;缺点是平均等待时间较长,上下文切换较费时。适用于分时系统。
  2. 先来先服务调度算法(FCFS):根据进程到达的先后顺序执行进程,不考虑等待时间和执行时间,会产生饥饿现象。属于非抢占式调度,优点是公平,实现简单;缺点是不利于短作业。
  3. 优先级调度算法(HPF):在进程等待队列中选择优先级最高的来执行。
  4. 多级反馈队列调度算法:将时间片轮转与优先级调度相结合,把进程按优先级分成不同的队列,先按优先级调度,优先级相同的,按时间片轮转。优点是兼顾长短作业,有较好的响应时间,可行性强,适用于各种作业环境。
  5. 高响应比优先调度算法:根据“响应比=(进程执行时间+进程等待时间)/ 进程执行时间”这个公式得到的响应比来进行调度。高响应比优先算法在等待时间相同的情况下,作业执行的时间越短,响应比越高,满足段任务优先,同时响应比会随着等待时间增加而变大,优先级会提高,能够避免饥饿现象。优点是兼顾长短作业,缺点是计算响应比开销大,适用于批处理系统。

进程状态

如字面意思,就是进程的某种状态,我们要想了解进程就要知道进程的不同状态。
进程有五种状态:R,S,D,T,t,X,Z,下面具体介绍
1. R运行状态(Running):运行状态不一定就是在运行中,也有可能是在运行队列里。
2. S睡眠状态(Sleeping):进程正在等待事件的完成。也叫做可中断睡眠(interruptible sleep)。也就是说睡面可以在某些特定的情况下终止。
3. D磁盘休眠状态(Disk sleep):有时候也叫做不可中断睡眠状态(uninterruptible sleep),在这个状态下的进程通常都会等待I/O的结束
4. T停止状态(stopped):可以通过发送SIGSTOP信号给进程来停止T进程。这个被暂停的进程可以通过发送SIGCONT信号让进程继续执行。
5. X死亡状态(dead):这是 内核运行里的do_exit()函数返回的状态。这个状态只是一个返回状态,你不在任务列表里面看到这个状态。

僵尸进程(Zombies)

僵死状态:这是一个人比较特殊的状态。当进程退出并且父进程(使用wait()系统调用)没有读取到子进程退出的返回代码时就会产生僵(死)尸进程。僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。所以,你想如果有很多很多僵尸进程的话,那也就意味着这些进程都会一直占用着内存。必定会导致内存泄漏了。所以说只要子进程退出,父进程还在运行,但是父进程没有读取子进程的状态,那么子进程将会进入Z状态。
下面简单实现一下僵尸进程

   #include<stdio.h>
   #include<unistd.h>
   #include<stdlib.h>
   #include<errno.h>
   int main()
  {
      pid_t pid = getpid();
      pid_t id = fork();
       if(id<0)
      {
          perror("fork");
          return -1;
      }
      else if(id>0)
      {//father
          printf("father pid is:%d,return pid is:%d\n",getpid(),id);
          sleep(10);
      }
      else if(id==0)
      {                                                                                                                             
          printf("child pid is:%d\n",getpid());
          exit(3);//子进程退出
      }
  }

运行结果如下图
这里写图片描述
我们知道进程是由PCB维护的,那如果Z状态一直不退出,PCB一直都要维护。那如果父进程创建了很多个子进程,就是不回收,就会造成资源的浪费。因为数据结构要占用内存。那么该如何避免呢?
解决僵尸进程

孤儿进程

父进程提前退出,子进程就被称为“孤儿进程”。孤儿进程被1号进程领养。父亲不管儿子了,儿子当然就编程孤儿了啊。
代码实现如下:

 #include<stdio.h>
 #include<unistd.h>
 #include<stdlib.h>
 #include<errno.h>
 int main()
 {                                                                                                                                 
     pid_t ret = fork();
     if(ret<0)
     {
        perror("fork");
        return -1;
     }
     if(ret==0)
     {
       printf("child pid is %d\n",getpid());
       sleep(10);
     }
     if(ret>0)
     {
         printf("father is %d\n",getpid());
         sleep(5);
         exit(0);
     }
  }

运行结果如下
这里写图片描述

posted @ 2017-11-28 22:10  chan0311  阅读(94)  评论(0编辑  收藏  举报