Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【Linux】Linux进程控制——进程创建、进程终止及进程等待详解

【Linux】Linux进程控制——进程创建、进程终止及进程等待详解

作者头像
P_M_P
发布于 2024-08-15 07:14:27
发布于 2024-08-15 07:14:27
86700
代码可运行
举报
文章被收录于专栏:P_M_P学习笔记P_M_P学习笔记
运行总次数:0
代码可运行

⭐进程创建

📖fork函数初识

Linuxfork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。

  • #include<unistd.h>
  • pid_t fork(void);
  • 返回值:自进程中返回0,父进程返回子进程id,出错返回-1

进程调用fork,当控制转移到内核中的fork代码后,内核做:

  • 分配新的内存块和内核数据结构给子进程
  • 将父进程部分数据结构内容拷贝至子进程
  • 添加子进程到系统进程列表当中
  • fork返回,开始调度器调度

当一个进程调用 fork 之后,就有两个二进制代码相同的进程。而且它们都运行到相同的地方。但每个进程都将可以开始它们自己的旅程,看如下程序:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main( void )
{
   pid_t pid;
   printf("Before: pid is %d\n", getpid());
   if ( (pid=fork()) == -1 )perror("fork()"),exit(1);
   printf("After:pid is %d, fork return %d\n", getpid(), pid);    sleep(1);
   return 0;
}   

运行结果:
[root@localhost linux]# ./a.out
Before: pid is 43676
After:pid is 43676, fork return 43677 After:pid is 43677, fork return 0

这里看到了三行输出,一行 before ,两行 after 。进程 43676 先打印 before 消息,然后它有打印 after 。另一个 after消息有43677 打印的。注意到进程 43677 没有打印 before ,为什么呢?如下图所示

所以, fork之前父进程独立执行,fork之后,父子两个执行流分别执行。注意,fork之后,谁先执行完全由调度器决定。

fork()的返回值

  • 子进程返回0,
  • 父进程返回的是子进程的pid。

📖写时拷贝

通常,父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本。具体见下图:

  • 节省内存:在数据仅被读取时,无需进行数据的复制,节省了内存资源。
  • 提高读操作效率:由于读操作可以直接访问共享内存,因此提高了读操作的效率。
  • 适用于读多写少的场景:在数据被频繁读取而写操作相对较少的场景下,写时拷贝技术能够显著提升系统性能。

📖fork常规用法

  • 一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子进程来处理请求。
  • 一个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec函数。

📖fork调用失败的原因

  • 系统中有太多的进程
  • 实际用户的进程数超过了限制

⭐进程终止

📖进程退出场景

  • 代码运行完毕,结果正确
  • 代码运行完毕,结果不正确
  • 代码异常终止
  • 代码运行完毕,结果正不正确看退出码;代码异常终止时,看退出信号;
  • 所以衡量一个进程的退出,我们只需要关注退出码(告诉父进程)和退出信号(os发出信号)。

📖进程常见退出方法

正常终止(可以通过 echo $? 查看进程退出码)

  • 从main返回
  • 调用exit
  • _exit

异常退出

  • ctrl + c,信号终止
_exit()函数

#include <unistd.h> void _exit(int status); 参数: status 定义了进程的终止状态,父进程通过 wait 来获取该值

  • 说明:虽然status是int,但是仅有低8位可以被父进程所用。所以_exit(-1)时,在终端执行echo $?发现返回值是255。
exit()函数

#include <unistd.h> void exit(int status);

exit() 最后也会调用_ exit(), 但在调用_ exit() 之前,还做了其他工作:

  1. 执行用户通过 atexit或on_exit定义的清理函数。
  2. 关闭所有打开的流,所有的缓存数据均被写入
  3. 调用_exit()
  • exit()会在退出进程的时候,冲刷缓冲区(不是内核缓冲区),_exit()不会

实例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main()
{
 printf("hello");
 exit(0);
}
运行结果:
[root@localhost linux]# ./a.out
hello[root@localhost linux]#


int main()
{
 printf("hello");
 _exit(0);
}
运行结果:
[root@localhost linux]# ./a.out
[root@localhost linux]#
return退出

return 是一种更常见的退出进程方法。执行 return n等同于执行exit(n), 因为调用 main 的运行时函数会将 main 的返回值当做exit() 的参数。非main函数的return,表示函数的结束而不是进程的结束。

⭐进程等待

📖进程等待的必要性

  • 在Linux进程提到过,子进程退出,父进程如果不管不顾,不读取子进程的退出信息,就可能造成“僵尸进程”的问题,进而造成内存泄漏。
  • 另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。
  • 最后,父进程派给子进程的任务完成的如何,父进程也需要知道。如,子进程运行完成,结果对还是不对, 或者是否正常退出。
  • 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息。

📖进程等待的方法

wait()

#include<sys/types.h> #include<sys/wait.h> pid_t wait(int*status); 返回值: 成功返回被等待进程pid ,失败返回 -1 。 参数: 输出型参数,获取子进程退出状态, 不关心则可以设置成为 NULL

✨waitpid()

pid_ t waitpid(pid_t pid, int *status, int options); 返回值:

  • 当正常返回的时候waitpid返回收集到的子进程的进程ID;
  • 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
  • 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;

参数: pid:

  • Pid=-1,等待任一个子进程。与wait等效。
  • Pid>0.等待其进程ID与pid相等的子进程。

status:

  • WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
  • WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

options:

  • WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。

注:

  • 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
  • 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞(即阻塞等待)。
  • 如果不存在该子进程,则立即出错返回。

📖获取子进程的status

  • wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
  • 如果传递NULL,表示不关心子进程的退出状态信息。
  • 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
  • status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只关注status低16比特位):
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main( void )
{
     pid_t pid;
     if ( (pid=fork()) == -1 )
         perror("fork"),exit(1);
     if ( pid == 0 )
     {
         sleep(20);
         exit(10);
     } 
     else 
     {
         int st;
         int ret = wait(&st);
 
         if ( ret > 0 && ( st & 0X7F ) == 0 )
         { // 正常退出
             printf("child exit code:%d\n", (st>>8)&0XFF);
         } 
         else if( ret > 0 ) 
         { // 异常退出
         printf("sig code : %d\n", st&0X7F );
         }
     }
}

进程的阻塞等待方式

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
int main()
{
     pid_t pid;
     pid = fork();
     if(pid < 0)
     {
         printf("%s fork error\n",__FUNCTION__);
         return 1;
     }
     else if( pid == 0 )
     { 
         //child
        printf("child is run, pid is : %d\n",getpid());
        sleep(5);
        exit(1);
     } 
     else
     {
         //father
         int status = 0;
         pid_t ret = waitpid(-1, &status, 0);//阻塞式等待,等待5S
         printf("this is test for wait\n");
         if( WIFEXITED(status) && ret == pid )                                                                                                           
         {
             printf("wait child 5s success, child return code is :%d.\n",WEXITSTATUS(status));
          }
          else
          {
              printf("wait child failed, return.\n");
              return 1;
          }
      }
    return 0;
 }

进程的非阻塞等待方式

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h> 
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
int main()
{
     pid_t pid;
 
     pid = fork();
     if(pid < 0){
     printf("%s fork error\n",__FUNCTION__);
     return 1;
     }
     else if( pid == 0 )
     { 
        //child
         printf("child is run, pid is : %d\n",getpid());
         sleep(5);
         exit(1);
     } 
     else
     {
         int status = 0;
         pid_t ret = 0;
         do
         {
             ret = waitpid(-1, &status, WNOHANG);//非阻塞式等待
             if( ret == 0 )
             {
                 printf("child is running\n");
             }
             sleep(1);
         }while(ret == 0);
 
         if( WIFEXITED(status) && ret == pid )
         {
             printf("wait child 5s success, child return code is :%d.\n",WEXITSTATUS(status));
         }
         else
         {
             printf("wait child failed, return.\n");
             return 1;
         }
     }
 return 0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-08-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【Linux】Linux进程控制>进程创建&&进程终止&&进程等待&&进程程序替换
在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程
用户10925563
2024/06/04
1.1K0
【Linux】Linux进程控制>进程创建&&进程终止&&进程等待&&进程程序替换
Linux进程控制【创建、终止、等待】
进程 创建后,需要对其进行合理管理,光靠 OS 是无法满足我们的需求的,此时可以运用 进程 控制相关知识,对 进程 进行手动管理,如创建 进程、终止 进制、等待 进程 等,其中等待 进程 可以有效解决僵尸 进程 问题
北 海
2023/07/01
4740
Linux进程控制【创建、终止、等待】
进程控制
在linux中fork函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
海盗船长
2020/08/27
7690
【Linux】Linux进程控制 --- 进程创建、终止、等待、替换、shell派生子进程的理解…
1. 在调用fork函数之后,当执行的程序代码转移到内核中的fork代码后,内核需要分配新的内存块和内核数据结构给子进程,内核数据结构包括PCB、mm_struct和页表,然后构建起映射关系,同时将父进程内核数据结构中的部分内容拷贝到子进程,并且内核还会将子进程添加到系统进程列表当中,最后内核空间中的fork代码执行完毕,操作系统中也就已经创建出来了子进程,最后返回用户空间,父子进程执行程序fork之后的剩余代码。
举杯邀明月
2023/04/12
15K0
【Linux】Linux进程控制 --- 进程创建、终止、等待、替换、shell派生子进程的理解…
Linux进程控制——Linux进程等待
前言:接着前面进程终止,话不多说我们进入Linux进程等待的学习,如果你还不了解进程终止建议先了解:
Eternity._
2024/06/14
9060
Linux进程控制——Linux进程等待
【Linux进程控制】五、wait()函数——子进程回收
僵尸进程:子进程结束,父进程没有回收子进程的资源(PCB),这个资源必须要由父进程回收,否则就形成僵尸进程。
mindtechnist
2024/08/08
2300
【Linux进程控制】五、wait()函数——子进程回收
【Linux】进程控制(创建、终止、等待)
在前文中我们了解了fork函数的使用,以及写时拷贝机制的原理等,并且也学习了什么是僵尸进程,但是并没有具体讲到应如何处理僵尸进程,本次章节将对fork函数以及如何终止进程,还有僵尸进程的处理做更为详细的探讨。
诺诺的包包
2023/04/02
3.6K0
【Linux】进程控制(创建、终止、等待)
Linux系统-进程控制
Linux进程控制 零、前言 一、进程创建 1、fork函数 2、fork返回值 写时拷贝 3、fork用法 4、fork失败 二、进程终止 1、退出码 2、退出方法 1) 调用_exit函数 2)调用exit函数 3)main函数return 4)异常退出 3、理解终止 三、进程等待 1、等待方法 2、获取status 3、理解等待 四、进程替换 1、替换原理 2、替换方法 五、实现简易shell 零、前言 前篇我们讲解学习了关于进程的概念知识,本章主要讲解关于进程的控制,深入学习进程 一、进程创建
用户9645905
2022/11/30
1.6K0
Linux系统-进程控制
Linux-进程控制
  进程控制不仅仅是管理程序的执行顺序,还涉及到资源的分配等问题,那么话不多说,开始我们今天的话题!
用户11029129
2024/06/04
1400
Linux-进程控制
【Linux系统编程】—— 深度解析进程等待与终止:系统高效运行的关键
fork函数初识:在linux中fork函数是⾮常重要的函数,它从已存在进程中创建⼀个新进程。新进程为⼦进程,⽽原进程为⽗进程。
用户11286421
2025/01/20
1850
【Linux系统编程】—— 深度解析进程等待与终止:系统高效运行的关键
Linux进程控制
进程是操作系统中的一个重要概念,它是一个程序的一次执行过程,程序是进程的一种静态描述,系统中运行的每一个程序都是在它的进程中运行的。
xxpcb
2020/08/04
2.1K0
【Linux】进程控制
在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
秦jh
2024/09/24
1890
【Linux】进程控制
探索进程控制第一弹(进程终止、进程等待)
在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
南桥
2025/05/22
880
探索进程控制第一弹(进程终止、进程等待)
【Linux】开始掌握进程控制吧!
送给大家一句话: 我并不期待人生可以一直过得很顺利,但我希望碰到人生难关的时候,自己可以是它的对手。—— 加缪
叫我龙翔
2024/03/30
1510
【Linux】开始掌握进程控制吧!
Linux进程控制
hello,my friend!今天,我们要开始学习新的内容了--->进程控制,进程控制涉及到操作系统如果管理和控制运行在计算机系统内的进程。我们将从fork函数,Linux进程退出,Linux进程等待,Linux进程替换等方面学习。那么接下来我们就开始敲黑板了!!
破晓的历程
2024/06/24
1790
Linux进程控制
Linux进程控制
函数也被称为子程序,与进程退出时返回退出码类似,函数执行完毕也会返回一个值,这个值通常用于表示函数的执行结果或状态。
绝活蛋炒饭
2024/12/16
1900
Linux进程控制
Linux进程控制
在进程概念这篇文章中,我们浅浅地了解了一下fork函数,它的功能是让父进程去创建一个子进程,并且有两个返回值,对应着父进程的返回值和子进程的返回值。那么,为什么会这样?接下来我们好好地讨论一下fork函数。
二肥是只大懒蓝猫
2023/03/30
2.5K0
Linux进程控制
linux下的进程控制
我们的一个系统在父进程退出后子进程偶尔出现不能正常的退出问题,这篇文章就是记录解决这个问题的过程。在unix系统上我们通过fork函数产生一个新的进程,这个新产生的进程我们称为子进程,调用fork函数的进程则是父进程。
XZAN
2018/12/13
2.2K0
【Linux】进程控制
fork常用法:1.一个父进程希望复制自己,使父子进程同时执行不同的代码段。2.一个进程要执行一个不同的程序。
平凡的人1
2023/10/15
2730
【Linux】进程控制
【Linux系统编程】七、进程等待
​ 之前我们讲过,子进程退出,父进程如果还在执行中,就会造成 “ 僵尸进程 ” 的问题,进而造成内存泄漏。另外,进程一旦变成僵尸状态,那就刀枪不入,“ 杀人不眨眼 ” 的 kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息。
利刃大大
2025/02/18
1360
【Linux系统编程】七、进程等待
相关推荐
【Linux】Linux进程控制>进程创建&&进程终止&&进程等待&&进程程序替换
更多 >
LV.1
这个人很懒,什么都没有留下~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档