进程基本介绍

Posted yumoz

tags:

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

进程概念

观点一:程序的一个执行实例,正在执行的程序等。
内核观点的概念:担当分配系统资源的实体。

进程描述

怎么样描述进程呢?进程信息被放在一个叫做**进程控制块(process control block,PCB)**的数据结构中,可以理解为进程属性的集合。在Linux操作系统下PCB是task_struct(Linux中描述进程的结构体)。

进程查看

使用命令ls /proc 查看Linux系统文件下的进程信息

进程创建

fork函数:

  • fork函数有两个返回值;
  • fork执行后,父进程和子进程代码共享,数据各自开辟空间,私有一份。

思考下面代码执行结果:

int main()
{
  pid_t id = fork();
  printf("hello yumoz\\n");
  sleep(1);
  return 0;
}

代码执行结果:

原因就是fork之后,创建了子进程,子进程和父进程都去执行打印语句,于是打印了两次。为了避免fork之后程序代码执行两次的情况发生,我们先去了解一下fork函数的基本知识。


fork : 创建一个子进程;
头文件:#include<unistd.h>
创建方式:pid_t fork(void);
返回值:如创建子进程成功,父进程返回子进程的PID,返回0给子进程。如果失败,父进程返回-1,没有子进程被创建。


所以,fork创建子进程后需要进行分流(if判断)。分流的目的是让子进程和父进程分别去完成不同的任务。

int main()
{
  pid_t id = fork();
  if(id < 0)
  {
    printf("create error\\n");
    return 1;
  }
  else if(id == 0) //子进程
  {
    printf("I am child process\\n");
  }
  else  //父进程
  {
    printf("I am parent process\\n");
  }
  sleep(1);
  return 0;
}

进程状态

首先我们列出进程的几种状态:

  1. 运行态:该进程正在运行。
  2. 就绪态:进程做好准备,只要有机会就开始执行。
  3. 阻塞、等待状态:进程在某些事件发生前不能执行,如I/O操作完成。
  4. 新建态:进程已经创建,但是没有将此进程加入到可执行队列中。通常是进程控制块已经创建,但是还没有加载到内存中的新进程。
  5. 退出态:操作系统从可执行进程组中释放出的进程,或是因为它自身停止了,或者是因为某种原因被取消。

下面对上面几种状态进程一个总结,首先看一个内核代码(只需要简单看看):

/*
 * 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 *task_state_array[] = {
	"R (running)",		/*  0 */
	"S (sleeping)",		/*  1 */
	"D (disk sleep)",	/*  2 */
	"T (stopped)",		/*  4 */
	"T (tracing stop)",	/*  8 */
	"Z (zombie)",		/* 16 */
	"X (dead)"		/* 32 */
};

说明,看到上述代码,我们可以总结如下状态:

  • R(running) 运行状态:并不意味着程序一定在运行中,它表明进程要么是在运行状态要么是在运行队列中。也就是可被调度。如果一个CPU,可以同时存在多个R状态的进程(因为R表示的是运行或者就绪状态)。
  • S(sleeping) 睡眠状态:意味着进程在等待事件完成(或称之为可中断睡眠、浅度睡眠),用来等待某种事情发生,随时可以被唤醒或杀掉。
  • D(disk sleep) 磁盘休眠状态:或称之为不可中断睡眠状态,在这个状态的进程通常会等待I/O的结束。(或者称之为深度睡眠,表示改进程不会被杀掉,即便是操作系统也不能被杀掉,除非自动唤醒);
  • T(stopped) 停止状态:可以发送SIGSTOP信号给进程来停止进程。此时被暂停的进程可以通过发送SIGCONT信号让进程继续执行。
  • X(dead)死亡状态:这个状态知识一个返回状态,你不会在任务列表中看到此状态
  • Z (zombie) 僵尸状态:当子进程退出,并且父进程没有读到子进程的退出的返回代码就会产生僵尸进程。

僵尸进程

僵尸状态是一个很特殊的状态,从系统层面分析:进程退出,曾经申请的资源并不是立即被释放掉的,而是要暂存一段时间,供OS(或父进程)进行读取,称为僵尸状态。这么深涩难懂吗?并不是,继续往下看。
首先通过一个例子说明僵尸进程:

int main()
{
  pid_t id = fork();
  if(id == 0)
  {
    int count = 5;
    while(count){
      printf("do child process! count:%d \\n",count--);
      sleep(1);
    }
    printf("child quit!\\n");
    exit(1);
  }
  else if(id > 0)
  {
    while(1)
    {
      printf("do father process!\\n");
      sleep(2);
    }
  }
  else
  {
    printf("fork error\\n");
  }
}

通过上述代码可以使用脚本键控进程状态:

while :; do ps aux | head -1 && ps aux | grep myfork | grep -v grep;sleep 1; echo "##根据需要打印##"; done

发现子进程先退出,之后父进程没有读到子进程的退出信号,子进程成了僵尸进程。那么了解到这了,一些僵尸进程的知识还是必须要知道的。下面列出来:

  1. 只要子进程退出,父进程还在运行,但父进程没有读到子进程状态,子进程进入Z状态
  2. 僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取状态代码。

僵尸进程的危害

  1. 进程的退出状态必须被维持下去,因为他需要告诉父进程一些信息。但如若父进程一直读不到,那么子进程就一直处于Z状态。
  2. 维护退出状态需要数据维护,他也属于进程的基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,那么PCB一直都要维护。
  3. 如果一个父进程创建了很多个子进程,子进程都成了僵尸进程,无法回收,就会造成内存资源浪费。因为数据结构对象本身就是在占用内存的资源。
  4. 僵尸进程还会导致内存泄漏等。

孤儿进程

父进程先退出,子进程没退出,则称之为“孤儿进程”。
孤儿进程最终会被1号init进程领养。
先看一段代码,了解孤儿进程:

int main()
{
  pid_t id = fork();
  if(id > 0)
  {
      printf("do child process!pid: %d ppid:%d \\n", getpid(),getppid());
      sleep(3);//父进程先退出    
    exit(0);
  }
  else if(id == 0)
  {
    while(1)
    {
      printf("do father process!...pid:%d , ppid: %d ...\\n",getpid(),getppid());
      sleep(1);
    }
  }
  else
  {
    printf("fork error\\n");
  }

  return 0;
}

杀掉孤儿进程:kill -9 进程编号


进程优先级


UID:执行者身份
PID:进程代号
PPID:进程的父进程代号
PRI:此进程的优先级,值越小越早被执行
NI:进程的nice,表示进程可被执行的优先级的修正系数值,取值范围-20至19

说明:
nice值为负,表示该进程优先级值会变小,即其优先级会变高,越快被执行。反之,同理。
调整进程优先级,在Linux下,就是调整进程nice值。

以上是关于进程基本介绍的主要内容,如果未能解决你的问题,请参考以下文章

java 简单的代码片段,展示如何将javaagent附加到运行JVM进程

代码片段:Shell脚本实现重复执行和多进程

C中进程与线程的基本使用(结合代码讲解)

C中进程与线程的基本使用(结合代码讲解)

Huawei_Netconf_Ncclient

JUC并发编程 -- JUC介绍 & 线程/进程 & 并发/并行 & Java代码查看CPU的核数