【Linux】-- 进程概念
创始人
2025-06-01 13:16:44
0

基本概念

进程(Process):是操作系统进行资源分配的最小单位。一个进程是一个程序的一次执行过程。

进程和程序的区别

程序首先以文件的形式保存在磁盘当中 (双击之后)加载到了内存中 由cpu加载开始运行

这个进程被加载到内存之后除了原本的代码之外还多了一堆的数据

多出来的数据就是操作系统对于进程的描述 而我们将这一堆数据称为PCB(Process Control Block) 进程控制块

进程等于程序加上PCB

PCB

当我们使用ps指令的时候 我们可以看到系统中存在着大量的进程

每个进程都对应着一个PCB

每个PCB对应着一个进程的数据 它们之间使用双链表组织起来 我们可以通过PCB来找到并管理每个进程

这样子我们就把操作系统对于进程的管理转化为了对于双链表的增删查改

Linux下的PCB是test_struct结构体

task_struct是什么

进程控制块(PCB)的作用是描述进程 而Linux系统是使用c语言写出来的

我们在c语言中一般是使用一个结构体去描述一个对象 这个在linux描述进程的结构体我们就把它叫做task_struct

task_struct是PCB

task_struct一般会被储存在内存中

task_struct里面有什么

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

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

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

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

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

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

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

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

  • 其他信息

查看进程

我们可以通过两种方式来查看进程

  • 通过系统目录查看

  • 通过ps指令查看

通过系统目录查看进程

在根目录下有一个proc目录

这个目录中含有大量的进程信息

这些文件中有些是用数字表示的 而这些数字实际上就是程序的pid

通过ps指令查看进程

但是我们一般查看进程的时候使用的是这样子的语句

ps axj | head -1 && ps axj | grep xxx

这段命令分为两部分&& 连接的两个命令 如果前面执行成功了就会执行后面的语句

ps axj | head -1

这段命令的意思打出 ps axj的第一行

ps axj | grep xxx

这段命令的意思是打出所有包含xxx的进程

获取程序的pid和ppid

  • pid : pid是程序的标识符

  • ppid: ppid是当前进程的父进程的标识符

getpid() 在 unistd.h 头文件里面
int main()    
{    while(1)    {    printf("I am a process, pid: %d, ppid: %d\n", getpid(), getppid());    sleep(1);    }    return 0;
}

通过fork函数创建进程

fork是一个系统调用级别的函数 其功能就是创建一个子进程

我们可以通过如下的代码来验证它

int main()    
{    int ret = fork();                                                                                 while(1)    {    printf("I am a process, pid: %d, ppid: %d\n", getpid(), getppid());    sleep(1);    }    return 0;
}

如何理解fork创建进程

  1. 从创建层面 不论是使用指令 跑代码还是使用fork创建进程 在操作系统眼中都没有区别

  1. 从继承层面 fork出来的子进程它本身没有代码和数据 所以说它是拷贝的父进程的代码和数据(写时拷贝)

代码和数据是全部拷贝过来吗?

对于代码来说 是的 但是一般创建进程之前的代码是用不到的 因为已经运行到创建进程(上下文)
对于数据来说父子进程的数据也就是PCB大部分是共享的 但是我们也要考虑修改的情况
因为进程具有独立性 这里就要用到一个写时拷贝的技术

关于父子进程

可以通过pid的返回值来区分父子进程 从而达到同时进行两个任务的目的

fork函数的返回值:

  1. 如果子进程创建失败返回-1

  1. 如果子进程创建成功 在父进程中返回子进程的pid 在子进程中返回0

int main()      
{      int ret = fork();              if(ret > 0)                                                                                                                                                                                                  {        printf("you can see me!!!\n");        }        else        {        printf("oops! you can see me!!!\n");        }        sleep(1);        printf("hello world\n");        sleep(1); 
}
#include
#includeusing namespace std;int main()
{pid_t id = fork();   // int                                                                                                                                                                                  if(id == 0)        {                  //child        while(true)    {              cout << "I am child, pid: " << getpid() << ", ppid:" << getppid() <<  endl;      sleep(1);      }              }                  else if(id > 0)      {                  //father       while(true)      {      cout << "I am parent, pid: " << getpid() << ", ppid:" << getppid() <<  endl;      sleep(2);      }      }      else       {      //TODU      }      //cout << "hello proc: " << getpid() << " hello parent: " << getppid() << "ret: " << id << endl;sleep(1);return 0;
}

这就是两个进程同时运行的结果

在做业务的时候我们可以将里面的代码换成业务逻辑就可以了

进程的状态

在进程从创建到消亡的这段时间会存在不同的状态 这些状态值储存在PCB当中 操作系统会根据PCB中的状态值来决定这个进程是否运行 结束

常见的进程状态有以下几点

在我们的Linux源码中对于状态有着如下的定义

/*
* 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 (run)

一个进程处于运行状态(running) 并不意味着进程一定处于运行当中,运行状态表明一个进程要么在运行中 要么在运行队列里 可以同时存在多个R状态的进程

浅度睡眠状态 S (sleep)

我们让这个进程休眠30秒

此时我们可以使用 kill -9 + pid 结束进程

深度睡眠状态 D (disk sleep)

一个进程处于深度睡眠状态(disk sleep)表示该进程不会被杀掉 即便是操作系统也不行,只有该进程自动唤醒才可以恢复 该状态有时候也叫不可中断睡眠状态(uninterruptible sleep) 处于这个状态的进程通常会等待IO的结束

例如 某一进程要求对磁盘进行写入操作 那么在磁盘进行写入期间 该进程就处于深度睡眠状态 是不会被杀掉的 因为该进程需要等待磁盘的回复(是否写入成功)以做出相应的应答(磁盘休眠状态)

暂停状态-T (stop)

在Linux当中 可以通过发送SIGSTOP信号使进程进入暂停状态(stopped)

发送SIGCONT信号 该进程就继续运行

僵尸状态 Z(zombie)

当一个进程将要退出的时候 在系统层面 该进程曾经申请的资源并不是立即被释放 而是要暂时存储一段时间 以供操作系统或是其父进程进行读取 如果退出信息一直未被读取 则相关数据是不会被释放掉的 一个进程若是正在等待其退出信息被读取 那么我们称该进程处于僵尸状态(zombie)

僵尸状态的存在是必要的

因为如果该进程的申请的资源在其运行结束之后立即释放那么我们便不知道该进程完成的如何

要知道它完成的如何我们必须要它的调用者来接受它的返回值 在了解完毕之后才可以让这个进程终止

我们在写c语言代码的时候通常在最后加上一个返回值

int main()
{// ..return 0;
}

这个return的值实际上是返回给操作系统的

我们可以使用 echo $? 命令来查询上次命令的返回值

死亡状态 X (dead)

我们一般不能查询到进程的死亡状态 因为在进程死亡的那一瞬间该进程的所有资源将会被释放 该进程也不存在了

僵尸进程

处于僵尸状态的进程就叫做僵尸进程

我们可以制造一个僵尸进程 具体操作就是让父进程一直运行 然后子进程退出 这样子的话子进程就一直无法将返回值传递给父进程 那么子进程就会一直处于僵尸状态

代码如下

int main()
{pid_t id = fork();   // int     if(id == 0)    {    //child     cout << "I am child, pid: " << getpid() << ", ppid:" << getppid() <<  endl;                                                                                           }                          else if(id > 0)            {                          //parent               while(true)            {                      cout << "I am parent, pid: " << getpid() << ", ppid:" << getppid() <<  endl;    sleep(2);            }                      }                          else                       {                          //TODU                 } return 0;
}

可以看到子进程的状态就变成了Z 僵尸状态

僵尸进程的危害

  1. 僵尸进程的退出状态必须一直维持下去 因为它要告诉其父进程相应的退出信息 可是父进程一直不读取 那么子进程也就一直处于僵尸状态

  1. 僵尸进程的退出信息被保存在task_struct(PCB)中 僵尸状态一直不退出 那么PCB就一直需要进行维护

  1. 若是一个父进程创建了很多子进程 但都不进行回收 那么就会造成资源浪费 因为数据结构对象本身就要占用内存

  1. 僵尸进程申请的资源无法进行回收 那么僵尸进程越多 实际可用的资源就越少 也就是说 僵尸进程会导致内存泄漏

孤儿进程

在Linux中的进程大多是父子关系 万一 一个进程的子进程还没有结束但是他的父进程结束了 那么就称这个进程为孤儿进程

如果没有父进程来管理这个子进程那么他就会一直占用资源 所以说为了防止这种情况 我们设计了让1号init进程来领养这个进程

代码如下

int main()
{  pid_t id = fork();      if(id == 0)                  {                            //child            while(true)        {                  cout << "I am child, pid: " << getpid() << ", ppid:" << getppid() <<  endl;    sleep(3);        }                  }                      else if(id > 0)        {                      //parent                          cout << "I am parent, pid: " << getpid() << ", ppid:" << getppid() <<  endl;                   }                      else                   {                                                                                                 //TODU             } return 0;
}

子进程的父进程变为了 1 (init进程)

相关内容

热门资讯

千亿中加基金总经理离职!在任一... 作者 | 刘银平编辑 | 付影来源 | 独角金融近日,中加基金一则人事变动公告引起市场关注。李莹因个...
国投白银LOF三连板,连发14... 红星资本局12月24日消息,A股全天走强,三大指数集体上扬。不过,今天市场最大亮点是LOF(上市型开...
空缺8个月后补位,邢台银行老将... 本文来源:时代周报 作者:刘子琪 来源:图虫邢台银行迎来新行长。12月23日,河北金融监管局发布批...
张瑜升任华创证券首席经济学家,... 华创证券有限责任公司(下称“华创证券”)迎新任首席经济学家。12月24日,澎湃新闻记者从华创证券研究...
从城市到山野:一场席卷五亿人的... 在“全民健身计划”与“三亿人上冰雪”的国家战略推动下,户外已渐从遥不可及的远方,转变为触手可及的生活...
兢强科技借回购名义为实控人分红... 本文来源:时代商业研究院 作者:陆烁宜来源丨时代商业研究院作者丨陆烁宜编辑丨郑琳二次闯关IPO,年入...
朴朴超市开城漳州 交流朴朴超市,实名添加微信lihua759321进群昨日,朴朴超市与漳州市芗城区政府举行了朴朴超市漳...
“木头姐”憋出内伤!比黄金疯狂... 来源丨北美商业见闻黄金和白银正日益成为年度最佳交易品种,有望双双创下自1979年以来最大的年度涨幅。...
俞敏洪已敲定东方甄选接班人:孙... 孙东旭离职一个半月后,东方甄选即将迎来新的掌舵者。12月24日,据第一财经,俞敏洪已敲定东方甄选接班...
光大信托拟售12亿不良债权,近... 来源:密探财经(ID:Spy Finance)“临近招募结束,还有约半个月!”这是阿里资产平台披露的...