Linux创建子进程

简介: Linux创建子进程

Linux创建子进程


目录

Fork函数原理

Fork创建子进程

getpid和getppid

循环创建N个子进程

父子进程共享哪些内容

父子进程gdb调试

exec函数族原理

-execlp和execl函数

execlp函数

exec函数族的特性

孤儿进程和僵尸进程

孤儿进程

僵尸进程

wait回收子进程

获取子进程退出值和异常终止信号

Fork函数原理

1.创建一个子进程

返回值为整数没有参数

2.父进程有的子进程都有,子进程执行fork();下面的

成功子进程会给父进程返回一个0,父进程返回子进程的id

Fork创建子进程

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int main()
{
•   printf("before fork--1-------\n");
•   printf("before fork--2-------\n");
•   printf("before fork--3-------\n");
•   printf("before fork--4-------\n");
•   printf("before fork--5-------\n");
•   pid_t pid=fork();
•   if(pid==-1)
•   {
•       perror("fork error");
•       exit(1);
•   }
•   else if(pid==0)
•   {
•       printf("-------child is created\n");
•   }
•   else if(pid>0)
•   {
•       printf("-------parent process:my chid is%d\n",pid);
•   }
•   printf("===========end of file\n");
•   return 0;
}

before fork 1,2,3,4执行一次

===========end of file执行两次,因为fork之后的子进程和父进程都会执行

getpid和getppid

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int main()
{
    printf("before fork--1-------\n");
    printf("before fork--2-------\n");
    printf("before fork--3-------\n");
    printf("before fork--4-------\n");
    printf("before fork--5-------\n");
    pid_t pid=fork();
    if(pid==-1)
    {
        perror("fork error");
        exit(1);
    }
    else if(pid==0)
    {
        printf("-------child is created,pid=%d\n,parent_pid=%d",getpid(),getppid());
    }
    else if(pid>0)
    {
        printf("-------parent process:my chid is%d,mypid=%d,myparent_pid=%d\n",pid,getpid(),getppid());
    }
    printf("===========end of file\n");
    return 0;
}

ps aux | grep 7876

父进程的父进程是bash

循环创建N个子进程

1.一个fork函数调用可以创建一个子进程,那么创建N个子进程应该如何实现

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int main()
{
    int i;
    for(i=0;i<5;i++){
    pid_t pid=fork();       //每次所有父进程,子进程,子进程产生的子进程都会产生一个子进程
    if(pid==-1)
    {
        perror("fork error");
        exit(1);
    }
    else if(pid==0)
    {
        printf("-------child is created,pid=%d\n,parent_pid=%d",getpid(),getppid());
    }
    else if(pid>0)
    {
        printf("-------parent process:my chid is%d,mypid=%d,myparent_pid=%d\n",pid,getpid(),getppid());
    }
    }
    return 0;
}

2.

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int main()
{
    int i;
    for(i=0;i<5;i++){
    if(fork()==0)
    break;
    }
    if(i==5)
       printf("I'm parent");
    else
    printf("I'm %dth child",i+1);
    return 0;
}

每个进程会争取cpu,导致顺序不一致

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int main()
{
    int i;
    for(i=0;i<5;i++){
    if(fork()==0)
    break;
    }
    if(i==5)
    {
        sleep(5);
       printf("I'm parent");
       }
    else{
    sleep(i);
    printf("I'm %dth child",i+1);
    }
    return 0;
}

父子进程共享哪些内容

1.父子进程之间在fork之后

相同处:全局变量,data,text,栈,堆,环境变量,用户ID,宿主目录,进程工作目录,信号处理方式

不同处:进程Id,fork返回值,父进程id,进程运行时间,闹钟(定时器),未决信号集

似乎,子进程复制了父进程0~3G用户内容,以及父进程的PCB,但是pid不同,实际不是,父子进程间遵循读时共享写时复制的原则。这样设计,无论子进程执行父进程的逻辑还是执行自己的逻辑都能节省内存开销

2.躲避父子进程共享全局变量的知识误区

3.共享:1.文件描述符(打开文件的结构体) 2.mmap建立的映射区

父子进程gdb调试

1.使用gdb调试的时候,gdb只能跟踪一个进程。可以在fork函数调用之前,通过指令设置gdb调试工具跟踪父进程或者是跟踪子进程。默 认跟踪父进程

2.set follow-fork-mode child命令设置gdb在fork之后跟踪子进程

3.set follow-fork-mode parent跟踪父进程,默认父进程

exec函数族原理

1.fork创建子进程后执行的是和父进程相同的程序(但是可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

2.进程还是父进程的子进程,但是执行的内容不一样了,exec调用的部分不会返回给父进程,进程id不变

-execlp和execl函数

execlp函数

加载一个进程,借助PATH环境变量

该函数通常调用系统程序。如:ls,date,cp,cat等命令

int execlp(const char *file,const char *arg,...);

execlp("ls","-l","-d)

失败返回-1

int execl(const *path,const char *arg,...);

指定路径

execl("/bin/ls","ls","-l","-F",NULL);

exec函数族的特性

1.execvp函数

加载一个进程

int execvp(const char *file,char *const argv[]);

char *argv[]={"ls","-l","-h",NULL};

execvp("ls",argv);

2.exec函数一旦调用成功即执行新的程序,不返回,只有失败才会返回,错误值-1。所以通常直接在exec函数调用后直接调用perror()和exit(),无需if判断

孤儿进程和僵尸进程

孤儿进程

父进程先于进程结束,则子进程称为孤儿进程。子进程的父进程成为init进程,称为init进程领养孤儿进程。

orphan.c

ps aux

僵尸进程

进程终止,父进程尚未回收,子进程残留资源(PCB)存放在内核,变成僵尸进程。

PCB标记子进程死亡原因

[zoom]<defunct>

wait回收子进程

1.一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但是它的PCB还保留着,内核在其中保存了一些信息,如果是正常终止则保存退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。一个进程的退出状态可以在shell中用特殊变量$查看因为shell是他的父进程,当它终止时Shell调用wait或waitpid得到它退出的状态同时彻底清除这个进程

2.父进程调用wait函数可以回收子进程的终止信息,该函数有三个功能: 1.阻塞等待子进程退出

2.回收子进程残留资源

3.获取子进程结束状态(退出原因)

3.pid_t wait(int *status);

pid_t waitpid(pid_t pid,int *status,int *options);

成功返回子进程的进程id,失败-1

获取子进程退出值和异常终止信号

1.使用宏函数

1.if(WIFEXITED(status))

为真说明子进程正常终止

WEXITSTATUS(status);

子进程返回值

2.WIFSIGNAL(status)

为真,异常终止

WTERMSIG(status)

取得使进程终止的那个信号的编号

2.所有进程终止都是信号

相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
相关文章
|
2月前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
本文旨在探讨Linux操作系统中的进程管理机制,包括进程的创建、执行、调度和终止等环节。通过对Linux内核中相关模块的分析,揭示其高效的进程管理策略,为开发者提供优化程序性能和资源利用率的参考。
126 1
|
2天前
|
存储 Linux API
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
在计算机系统的底层架构中,操作系统肩负着资源管理与任务调度的重任。当我们启动各类应用程序时,其背后复杂的运作机制便悄然展开。程序,作为静态的指令集合,如何在系统中实现动态执行?本文带你一探究竟!
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
|
14天前
|
存储 网络协议 Linux
【Linux】进程IO|系统调用|open|write|文件描述符fd|封装|理解一切皆文件
本文详细介绍了Linux中的进程IO与系统调用,包括 `open`、`write`、`read`和 `close`函数及其用法,解释了文件描述符(fd)的概念,并深入探讨了Linux中的“一切皆文件”思想。这种设计极大地简化了系统编程,使得处理不同类型的IO设备变得更加一致和简单。通过本文的学习,您应该能够更好地理解和应用Linux中的进程IO操作,提高系统编程的效率和能力。
66 34
|
18天前
|
消息中间件 Linux C++
c++ linux通过实现独立进程之间的通信和传递字符串 demo
的进程间通信机制,适用于父子进程之间的数据传输。希望本文能帮助您更好地理解和应用Linux管道,提升开发效率。 在实际开发中,除了管道,还可以根据具体需求选择消息队列、共享内存、套接字等其他进程间通信方
55 16
|
1月前
|
消息中间件 Linux
Linux:进程间通信(共享内存详细讲解以及小项目使用和相关指令、消息队列、信号量)
通过上述讲解和代码示例,您可以理解和实现Linux系统中的进程间通信机制,包括共享内存、消息队列和信号量。这些机制在实际开发中非常重要,能够提高系统的并发处理能力和数据通信效率。希望本文能为您的学习和开发提供实用的指导和帮助。
148 20
|
3月前
|
Linux
如何在 Linux 系统中查看进程占用的内存?
如何在 Linux 系统中查看进程占用的内存?
487 58
|
2月前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
126 13
|
2月前
|
SQL 运维 监控
南大通用GBase 8a MPP Cluster Linux端SQL进程监控工具
南大通用GBase 8a MPP Cluster Linux端SQL进程监控工具
|
3月前
|
缓存 监控 Linux
linux进程管理万字详解!!!
本文档介绍了Linux系统中进程管理、系统负载监控、内存监控和磁盘监控的基本概念和常用命令。主要内容包括: 1. **进程管理**: - **进程介绍**:程序与进程的关系、进程的生命周期、查看进程号和父进程号的方法。 - **进程监控命令**:`ps`、`pstree`、`pidof`、`top`、`htop`、`lsof`等命令的使用方法和案例。 - **进程管理命令**:控制信号、`kill`、`pkill`、`killall`、前台和后台运行、`screen`、`nohup`等命令的使用方法和案例。
234 4
linux进程管理万字详解!!!
|
2月前
|
运维 监控 Linux
Linux操作系统的守护进程与服务管理深度剖析####
本文作为一篇技术性文章,旨在深入探讨Linux操作系统中守护进程与服务管理的机制、工具及实践策略。不同于传统的摘要概述,本文将以“守护进程的生命周期”为核心线索,串联起Linux服务管理的各个方面,从守护进程的定义与特性出发,逐步深入到Systemd的工作原理、服务单元文件编写、服务状态管理以及故障排查技巧,为读者呈现一幅Linux服务管理的全景图。 ####