【Linux系统编程(进程编程)】创建进程的场景,fork和vfork的使用及区别

文章目录

  • 一、进程关键概念
  • 二、创建进程函数fork的使用
    • 一、进程创建实战
  • 三、创建进程函数fork的使用补充
  • 四、进程创建发生了什么事?
  • 五、创建新进程的实际应用场景 & fork总结
    • 一、fork创建一个子进程的一般目的?
    • 二、fork编程实战
  • 六、vfork也能创建进程
    • 一、验证子进程先运行
    • fork
    • vfork
        • 子进程没有退出
        • 子进程有退出
    • 二、验证vfork子进程共享父进程的内存空间

一、进程关键概念

问1. 什么是程序,什么是进程,有什么区别?
问2. 如何查看系统中有哪些进程?
问3. 什么是进程标识符?
问4. 什么叫父进程,什么叫子进程?
问5. C程序的存储空间是如何分配?

问1. 什么是程序,什么是进程,有什么区别?
程序是静态的概念,进程是动态的概念。
程序是静态的概念,gcc xxx.c -o pro 磁盘中生成的pro文件,叫做程序。
进程是动态的概念,是程序的一次运行活动,通俗讲就是程序跑起来了,系统中多了一个进程。

问2. 如何查看系统中有哪些进程?
a. 使用ps指令查看
ps
ps -aux 生成一大堆
而实际使用ps配合grep查询程序是否存在某一个进程,如:ps -aux|grep init是查询init相关的进程。利用管道进行查询,避免显示太多,查找不方便。
b. 利用top指令查看,类似windows任务管理器,动态显示。

问3. 什么是进程标识符?
每个进程都有一个非负整数表示唯一ID,叫做pid,类似身份证。

pid=0:成为交换进程(swapper)
作用——进程调度
pid=1:init进程
作用——系统初始化

pos机显示刷卡界面,ktv点歌机显示点歌界面,这些由init进程来做。程序运行后,内核加载完毕,文件系统起来时候,运行第一个进程就是init进程,init进程就会读取配置文件,去启动一些其他的启动进程(其他的开机程序)。

编程调用getpid函数获取自身的进程标识符;getppid获取父进程的进程标识符。
image.png

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
    pid_t pid;

    pid = getpid();

    printf("this process pid is %d\n", pid);

    while(1);
    return 0;
}

问4. 什么叫父进程,什么叫子进程?
进程A创建了进程B
那么A叫做父进程,B叫做子进程,父子进程是相对的概念,理解为人类世界的父子关系。

二、创建进程函数fork的使用

一、进程创建实战

使用fork函数创建一个进程
pid_t fork(void);

fork函数调用成功,返回两次
返回值为0,代表当前进程是子进程
返回值非负数,代表当前进程为父进程
调用失败,返回-1

image.png
image.png

请看printf输出,执行了两遍,这是为什么呢?
image.png

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
        pid_t pid;

        pid = getpid();

        fork();

        printf("this process pid is %d\n", pid);

        return 0;
}
~    

第十一行fork(),之前执行一次,之后因为创建了进程,所以执行了两次,看到了两次输出。

image.png

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
        pid_t pid;

        pid = getpid();

        fork();

        printf("this process pid is %d, current process id is %d\n", pid, getpid());

        return 0;
}

getpid()获取当前进程的进程id,可以用来区分进程。第十三行,如果两个id相同,说明都是pid的值,属于父进程,若是俩id不同,则是子进程,不同的id是子进程调用getpid()的原因。

进一步 多写一些调试信息:
image.png

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
        pid_t pid;

        pid = getpid();//原来进程ID

        fork();//创建进程


        //原来进程 与当前进程比较
        if(pid == getpid())
        {       //当前进程是原来进程,则为父进程
                printf("this is father process\n");
        }
        else
        {
                //当前进程不是原来进程,则为子进程
                printf("this is chlid process, pid:%d\n",getpid());
        }


        return 0;
}

fork()之前,包括fork()这行,都是父进程在运行。而if-else父子进程都会执行。
子进程和父进程都会执行fork后面if-else分支。而且父子进程会执行不同分支,一个执行if,另一个肯定执行else。因为他们都会执行这句话if里边的pid == getpid(),而父进程时,表达式和为真,子进程时,表达式为假。

通过pid区分父子进程
image.png

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
	pid_t pid;
	pid_t pid2;

	pid = getpid();//原来进程ID

	printf("before fork pid = %d\n",pid);

	fork();//创建进程

	pid2 = getpid();
	
	printf("after fork pid = %d\n",pid2);

	//原来进程 与当前进程比较
	if(pid == pid2)
	{	//当前进程是原来进程,则为父进程
		printf("this is father process\n");
	}
	else
	{
		//当前进程不是原来进程,则为子进程
		printf("this is chlid process, pid:%d\n",getpid());
	}
	
	
	return 0;
}

通过fork()函数返回值区分父子进程。返回值为0为子进程,返回非零值为父进程。
image.png

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
	pid_t pid;

	printf("father pid = %d\n",getpid());

	pid = fork();//创建进程

	if(pid > 0)
	{
		printf("this is father process, pid:%d\n",getpid());
	}
	else if(pid == 0)
	{
		printf("this is chlid process, pid:%d\n",getpid());
	}
	
	
	return 0;
}

image.png

三、创建进程函数fork的使用补充

fork返回值有可能是0,有可能是其他的。在父子进程都把retpid打印出来。
fork之后,新的进程拷贝了一份代码和变量,新进程和旧的进程都有一份retpid。fork之后,给retpid分配了一个0,一个非零。代码看一下:
image.png
进入父进程,打印的retpid是子进程的pid
进入子进程,打印的retpid是0
所以,fork后有了两个retpid变量,一个分配给父进程,一个分配给子进程。不同的是:当fork返回非零时,返回给父进程的retpid为子进程的进程id,而fork返回零时,返回给子进程的retpid为0。

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
        pid_t pid;
        pid_t pid2;
        pid_t retpid;

        pid = getpid();//pid是父进程
        printf("before fork pid = %d\n",pid);


        retpid = fork();//fork创建进程 返回retpid


        pid2 = getpid();
        printf("after fork pid = %d\n",pid2);


        if(pid == pid2)
        {
                printf("this is father process. retpid:%d\n", retpid);
        }
        else
        {
                printf("this is chlid process. retpid:%d, child pid:%d\n", retpid, getpid());
        }


        return 0;
}

也就是说,函数fork调用成功,则:(man手册中翻译理解)

  • 在父进程中,返回子进程PID
  • 在子进程中,返回0

四、进程创建发生了什么事?

image.png
局部变量a的分配,不确定。
image.png
代码段共享
数据段拷贝(写时拷贝)
以前是全部拷贝,现在是写时拷贝。
image.png


#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
    pid_t pid;
    int data = 10;

    printf("father pid = %d\n",getpid());

    pid = fork();//创建进程

    if(pid > 0)
        {
            printf("this is father process, pid:%d\n",getpid());
        }
    else if(pid == 0)
        {
            printf("this is chlid process, pid:%d\n",getpid());
        }

    printf("data=%d\n",data);

    return 0;
}

如下,子进程对data修改,会执行写时拷贝。
image.png

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
    pid_t pid;
    int data = 10;

    printf("father pid = %d\n",getpid());

    pid = fork();//创建进程

    if(pid > 0)
    {
        printf("this is father process, pid:%d\n",getpid());
    }
    else if(pid == 0)
    {
          data += 10;
          printf("this is chlid process, pid:%d\n",getpid());
    }

    printf("data=%d\n",data);

    return 0;
}

五、创建新进程的实际应用场景 & fork总结

一、fork创建一个子进程的一般目的?

(1)一个父进程希望复制自己,使父、子进程同时执行不同的代码段。这在网络服务进程中是常见的——父进程等待客户端的服务请求。当这种请求到达时,父进程调用fork,使子进程处理此请求。父进程则继续等待下一个服务请求到达。

下边模拟一下网络请求,为每个请求创建一个服务进程。

(现在还存在select poll epoll等IO多路复用技术,暂不展开)

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
        pid_t pid;
        int data = 10;

        while(1){
                printf("please input a data:");
                scanf("%d", &data);
                if(data == 1){
                        pid = fork();

                        if(pid > 0)
                        {
                        }
                        else if(pid == 0)
                        {
                                while(1){
                                        printf("do net request, response to :%d\n", data);
                                        sleep(5);
                                }
                        }

                }else{
                        printf("wait, do nothing\n");
                }
        }

        return 0;
}

(2)一个进程要执行一个不同的程序。这对shell是常见的情况。在这种情况下,子进程返回后立即调用exec

二、fork编程实战

image.png
一个现有的进程可以调用fork函数创建一个新进程。
返回值:子进程中返回0,父进程中返回子进程ID,出错返回-1
由fork创建的新进程被称为子进程(child process),fork函数被调用一次,但返回两次。两次返回的唯一区别是子进程的返回值0。而父进程的返回值是新子进程的进程ID。将子进程ID返回给父进程的理由是:因为一个进程的子进程可以有多个,并且没有一个函数使一个进程可以获取其所有子进程的进程ID。fork使子进程得到返回值为0的理由是:一个进程只会有一个父进程,所以一个子进程总是可以调用getppid以获得其父进程的进程ID(进程ID 0 总是由内核交换进程使用,所以一个子进程的进程ID不可能为0)。【自己理解就是:子进程执行fork指令时,返回值为0,是利用 0 来区分父子进程】
子进程和父进程继续执行fork调用之后的指令。子进程是父进程的副本。例如,子进程获得父进程数据空间、堆和栈的副本。注意,这是子进程拥有的副本。父、子进程并不共享这些存储空间部分。父、子进程共享正文段(7.6节)。
由于fork之后经常跟随着exec,所以现在的很多实现并不执行一个父进程数据段、栈和堆的完全复制。作为替代,使用了写时复制(Copy-On-Write,COW)技术。这些区域由父、子进程共享,而且内核将他们的访问权限变成只读的。如果父、子进程中的任何一个试图修改这些区域,则内核只为修改区域的那块内存制作一个副本,通常是虚拟存储器系统中的一“页”。

六、vfork也能创建进程

vfork函数 也可以创建进程,与fork有什么区别??
关键区别一:
vfork 直接使用父进程存储空间,不拷贝。
关键区别二:
vfork保证子进程先运行,当子进程调用exit退出后,父进程才执行。

一、验证子进程先运行

fork

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
        pid_t pid;

        pid = fork();

        if(pid > 0)
        {
                while(1){
                        printf("this is father process, pid:%d\n",getpid());
                        sleep(1);
                }
        }
        else if(pid == 0)
        {
                while(1){
                        printf("this is chlid process, pid:%d\n",getpid());
                        sleep(1);
                }
        }


        return 0;
}

image.png
结果证明:使用fork函数创建进程,父子进程同时运行。

vfork

子进程没有退出

在刚刚代码基础上 仅仅把fork换成vfork

执行效果:
image.png
结果说明:子进程没退出,父进程就不执行。

子进程有退出

子进程执行三次,子进程退出。

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
        pid_t pid;
        int cnt = 0;

        pid = vfork();

        if(pid > 0)
        {
                while(1){
                        printf("this is father process, pid:%d\n",getpid());
                        sleep(1);
                }
        }
        else if(pid == 0)
        {
                while(1){
                        printf("this is chlid process, pid:%d\n",getpid());
                        sleep(1);
                        cnt++;
                        if(cnt == 3){
                                break;
                        }
                }
        }
        
        
        return 0;
}        

image.png
结果说明:子进程退出后,父进程才执行。

二、验证vfork子进程共享父进程的内存空间

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
        pid_t pid;
        int cnt = 0;

        pid = vfork();

        if(pid > 0)
        {
                while(1){
                        printf("cnt=%d\n", cnt);
                        printf("this is father process, pid:%d\n",getpid());
                        sleep(1);
                }
        }
        else if(pid == 0)
        {
                while(1){
                        printf("this is chlid process, pid:%d\n",getpid());
                        sleep(1);
                        cnt++;
                        if(cnt == 3){
                                exit(0);//break;
                        }
                }
        }
        
        
        return 0;
}

image.png
结果说明:只有子进程在修改cnt。使用vfork子进程调用结束后,父进程中cnt的值发生改变,说明被子进程修改。所以vfork父子共享内存空间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/482076.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

grid布局

文章目录 1. 概念2. 组成2.1. 网格行2.2. 网格列2.3. 网格间距2.4. 网格线2.5. 网格容器2.6. fr 单位 3. 网格跨行跨列3.1. 跨列3.2. 跨行 4. 网格布局案例4.1. 演示效果4.2. 分析思路4.3. 代码实现 1. 概念 网格是一组相交的水平线和垂直线&#xff0c;它定义了网格的列和行。…

【排序算法】实现快速排序值(霍尔法三指针法挖坑法优化随即选key中位数法小区间法非递归版本)

文章目录 &#x1f4dd;快速排序&#x1f320;霍尔法&#x1f309;三指针法&#x1f320;挖坑法✏️优化快速排序 &#x1f320;随机选key&#x1f309;三位数取中 &#x1f320;小区间选择走插入&#xff0c;可以减少90%左右的递归&#x1f309; 快速排序改非递归版本&#x1…

工业相机采图方式、图像格式(BYTE、HObject和Mat)转换

1、概述 机器视觉项目中&#xff0c;如何采集到合适的图像是项目的第一步&#xff0c;也是最重要的一步&#xff0c;直接关系到后面图像处理算法及最终执行的结果。所以采用不同的工业相机成像以及如何转换成图像处理库所需要的格式成为项目开发中首先要考虑的问题。 2、工业…

分布式组件 Nacos

1.在之前的文章写过的就不用重复写。 写一些没有写过的新东西 2.细节 2.1命名空间 &#xff1a; 配置隔离 默认&#xff1a; public &#xff08;默认命名空间&#xff09;:默认新增所有的配置都在public空间下 2.1.1 开发 、测试 、生产&#xff1a;有不同的配置文件 比如…

【ZYNQ】基于ZYNQ 7020的OPENCV源码交叉编译

目录 安装准备 检查编译器 安装OpenCV编译的依赖项 下载OpenCV源码 下载CMake 编译配置 编译器说明 参考链接 安装准备 使用的各个程序的版本内容如下&#xff1a; 类别 软件名称 软件版本 虚拟机 VMware VMware-workstation-full-15.5.0-14665864 操作系统 Ub…

【QT入门】 Qt实现自定义信号

往期回顾&#xff1a; 【QT入门】图片查看软件(优化)-CSDN博客 【QT入门】 lambda表达式(函数)详解-CSDN博客 【QT入门】 Qt槽函数五种常用写法介绍-CSDN博客 【QT入门】 Qt实现自定义信号 一、为什么需要自定义信号 比如说现在一个小需求&#xff0c;我们想要实现跨ui通信&a…

Hive入门

什么是hive&#xff1f; - Hive是Facebook开发并贡献给Hadoop开源社区的。它是建立在 Hadoop体系架构上的一层 SQL抽象&#xff0c;使得数据相关人 员使用他们最为熟悉的SQL语言就可以进行海量数据的处理、 分析和统计工作 - Hive将数据存储于HDFS的数据文件映射为一张数据库…

Java程序设计 4、5章 练习题

一、填空题 1.假设有 String s1 "Welcome to Java"; String s2 s1; String s3 new String("Welcome to Java"); 那么下面表达式的结果是什么&#xff1f; (1) s1 s2 ___________true_______________ (2) s1 s3 ______…

SOPHON算能服务器SDK环境配置和相关库安装

目录 1 SDK大包下载 2 安装libsophon 2.1 安装依赖 1.2 安装libsophon 2 安装 sophon-mw 参考文献&#xff1a; 1 SDK大包下载 首先需要根据之前的博客&#xff0c;下载SDK大包&#xff1a;SOPHON算能科技新版SDK环境配置以及C demo使用过程_sophon sdk yolo-CSDN博客 …

第 6 章 ROS-xacro练习(自学二刷笔记)

重要参考&#xff1a; 课程链接:https://www.bilibili.com/video/BV1Ci4y1L7ZZ 讲义链接:Introduction Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程 6.4.3 Xacro_完整使用流程示例 需求描述: 使用 Xacro 优化 URDF 版的小车底盘模型实现 结果演示: 1.编写 X…

idea使用token方式登录GitHub

总体上分为两大步&#xff1a;1.GitHub生成token。2.idea配置token登录GitHub。 注&#xff1a;idea配置GitHub的前提是本地已经安装了git程序。 一、GitHub生成token 1.登录GitHub 2.进入token创建页面&#xff08;右上角点击头像–>settings–>页面向下滚动左侧菜单栏…

linux热键,man手册介绍

目录 热键 tab ctrl c ctrl r man 区段 快捷键 热键 tab 可以看到以输入的内容为开头的指令,但无法选择: 当输入的内容匹配到的内容只有一个时,可以自动补全 可以用于输入路径时,自动补全文件名 ctrl c 让当前的程序停掉,可以在 程序或指令出问题而自己无法停止时 使用…

HSP_01章_Python 语言概述

文章目录 06 开发环境安装10 注意事项11 学习方法14 Pycharm 常用快捷键14 Python 常用转义字符15 Python 注释Comment16 [Python 中文文档地址](https://docs.python.org/zh-cn/3.11/) 06 开发环境安装 python 版本命令: python cmd 退出: exit() 环境变量配置: 计算机 > 高…

【Linux】从零认识进程 — 中下篇

送给大家一句话&#xff1a; 人一切的痛苦&#xff0c;本质上都是对自己无能的愤怒。而自律&#xff0c;恰恰是解决人生痛苦的根本途径。—— 王小波 从零认识进程 1 进程优先级1.1 什么是优先级1.2 为什么要有优先级1.3 Linux优先级的特点 && 查看方式1.4 其他概念 2…

如何鉴别真假ZLibrary?2024 ZLibrary最新可用地址,持续更新,2024年在 zlibrary 上发现几本有意思的电子书

之前分享过全网电子书都在这了&#xff1a;ZLibrary 官方通道来了&#xff0c;不再担心找不到最新地址&#xff0c;配合这个脚本简直完美&#xff0c;最新ZLibrary可用地址 zlibrary-sg.se 如何确认一个网站是真的ZLibrary &#xff1f;存在一个API 接口/eapi/info &#xff0…

pytest全局配置+前后只固件配置

pytest全局配置前后只固件配置 通过读取pytest.ini配置文件运行通过读取pytest.ini配置文件运行无条件跳过pytest.initest_mashang.pyrun.py 有条件跳过test_mashang.py pytest框架实现的一些前后置&#xff08;固件、夹具&#xff09;处理方法一&#xff08;封装&#xff09;方…

【C++航海王:追寻罗杰的编程之路】stack

目录 1 -> stack的介绍和使用 1.1 -> stack的介绍 1.2 -> stack的使用 1.3 -> stack的模拟实现 1 -> stack的介绍和使用 1.1 -> stack的介绍 stack的文档介绍 1. stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c…

MTransE翻译

MTransE翻译 阅读时间&#xff1a;2024.03.23 领域&#xff1a;知识图谱&#xff0c;知识对齐 作者&#xff1a;Muhao Chen等人 UCLA 出处&#xff1a;IJCAI Multilingual Knowledge Graph Embeddings for Cross-lingual Knowledge Alignment 用于交叉知识对齐的多语言知识图…