Linux程序的地址空间,进程终止

news2024/11/19 13:44:20

个人主页:点我进入主页

专栏分类:C语言初阶  C语言进阶  数据结构初阶    Linux    C++初阶    算法

欢迎大家点赞,评论,收藏。

一起努力,一起奔赴大厂

一.程序的地址空间

1.1程序的地址空间的引入

        我们知道frok可以创建子进程,子进程会继承父进程的大部分属性,如果我们我们给出一个g_val,在子进程和父进程中输入g_val的值以及g_val的地址,会是怎样的结果呢?我们看下面代码:

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

int main()
{
    int g_val=100;
    pid_t id=fork();
    if(id==0)
    {
        int cnt=5;
        while(cnt--)
        {
            printf("I an child process,g_val=%d,&g_val=%p,pid=%d, ppid=%d\n",g_val,&g_val,getpid(),getppid());
            sleep(1);
        }
    }
    else if(id>0)
    {
        while(1)
        {
            printf("I an father process,g_val=%d,&g_val=%p,pid=%d, ppid=%d\n",g_val,&g_val,getpid(),getppid());
            sleep(1);
        }
    }
    return 0;
} 

我们运行后可以看到

父进程的g_val的地址和子进程的g_val的地址相同 ,如果我们,我们将子进程的代码修改,每次让g_val的值加1,会是怎样的结果?我们可以看到一下

g_val的值不同,但是g_val的地址却一样,这是为什么呢?在c语言时我们知道一个地址对应一个值,为什么在这里会出现一个地址对应两个值?这打破了我们的常规认知,其实不然,在以前我们写的都是一个进程,在这里是两个进程,这其中自然有一些差别,那这是如何实现的呢?这与程序的地址空间有关,这里的地址也不是物理地址,是虚拟地址

1.2程序地址空间概图

1.2.1不修改值时

        程序的地址空间是怎样的呢?我们可以看下面的图

 父进程有一个g_val虚拟地址是0x7ffe258b87bc,在父进程的地址空间中会有个初始化数据,里面的g_val地址也是虚拟地址,同时还有一个页表,页表有两部分,一部分是虚拟地址,一部分是物理地址,它相当于一个映射进程的虚拟地址对应者相应的物理地址。由于子进程会继承父进程的代码和数据以及大部分属性,所以子进程会继承父进程的task_struct,继承父进程的地址空间,父进程的页表,也就是说会进程一次浅拷贝

1.2.2修改值时

        在上面我们有一个修改g_val的值,那它和上面的过程一样吗?事实上是类似的,当我们想要修改值时,会发生一次写时拷贝,写时拷贝是什么呢?写的时候再进程修改,我们看下面的图

 子进程会将父进程的代码。数据。mm_struct,task_struct进行拷贝,在物理空间中重新开辟一次g_val进行写。

1.3理解地址空间

        进程有独立的地址空间,独立的页表,如果父进程和子进程不写,一个全局变量(类似于g_val的变量),默认会被子进程和父进程共享(只读)。那为什么这样干呢?如果我们不采用写时拷贝,只要有一个子进程就会在物理内存上开辟一块空间,这样有一定的缺点,有时候我们并不会修改值,会造成没有意义的空间开辟,那我们采用写时拷贝会不会很慢呢?当然不会,我们开辟的时候就拷贝,和用的时候再拷贝,我们采用写时拷贝还会减少拷贝的次数,效率更快,更节省空间,写时拷贝就是我都你不会用

1.3.1为什么要有地址空间(地址空间的优点)

        首先页表和地址空间可以将无序变为有序,我们知道我们开辟的空间是随机开辟的,通过页表的映射关系可以有效的将空间变得有序。其次可以将进程管理模块(页表和地址空间)和内存模块(物理空间)解耦,在程序进程检测时它检测到地址空间有就可以进行执行,因此我们只在地址空间上申请内存,填写页表的左侧,用的时候再在物理空间上开辟内存,这样也是可以的,还有一种情况我们先看下面的图:

假如 我们有2MB的内存,我们映射到物理空间上后,可以先执行1MB的内存,当者1MB执行完后可以先将这1MB内存释放,删除页表的关系,但是地址空间不变,这样我们看着还是2MB。最后访问越界时,如果页表中查不到这个地址,os就会阻止这个请求,实现对物理内存的保护,例如我们一个数组a[10],我们访问a[200]就会被os拦截。

1.3.2进一步理解页表和写时拷贝

        在页表中不仅仅有虚拟地址和物理地址这两部分,还有一个检测是否在内存中以及权限,我们先说是否在内存中,它用一个0个1来表示0不在内存中,1在内存中,那什么时候会显示0呢?

当内存不足时,会将一部分页表的内容唤出到磁盘中,还记得我们进程挂起的swap分区吗,这里和那个类似,当被唤出到磁盘中后这里就会显示为0 。

当我们创建子进程后父进程和子进程中 权限都会被修改为r,当我们想要修改时发生写时拷贝,权限修改为w。当操作系统检测到错误时会先进行检测是否不在物理内存中(缺页中断,也就是页表中为0),然后检测是否需要发生写时拷贝(检测权限是否为r),都不是才进程异常处理

二.进程的终止

2.1进程终止的3中状态

2.1.1代码跑完结果正确

我们先运行下面代码

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

int main()
{
    printf("pid=%d,ppid=%d\n",getpid(),getppid());
    return 0;
} 

运行后输入指令

echo $?

echo $?是返回我们前一个程序的返回码,我们将上面代码return 100然后运行然后输入指令echo$?可以看到

当我们再执行一次ehco $?可以看到

这是输出echo $?的退出码。

2.2.1代码跑完,结果错误

        在这里我们需要先知道退出码有什么作用,在一个父进程中我们需要知道子进程的退出状态,是成功还是失败?失败的原因是什么?所以出现了退出码这个概念。退出码有很多我们需要知道每一个退出码是什么吗?其实不需要,我们有一个函数可以知道每个退出码是什么意思,我们看下面代码

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

int main()
{
    for(int errorcode=0;errorcode<=255;errorcode++)
    {
        printf("errorcode=%d->%s\n",errorcode,strerror(errorcode));
    }
    printf("pid:%d,ppid=%d\n",getpid(),getppid());
    return 0;
} 

我们运行代码

2.2.3出现异常

        运行时,进程崩溃了,os会将进程杀掉一旦出现了异常退出码就没有意义了,为什么出现异常原因是进程出现了异常,本质说os进程发出信号。例如我们运行下面代码:

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

int main()
{
    while(1)
    {
        printf("pid=%d,ppid=%d\n",getpid(),getppid());
        sleep(1);
    }
    return 0;
} 

我们输入指令

kill -9 pid

可以看到

我们可以输入指令

kill -11 pid

2.2检测进程终止顺序

        首先检测进程是否出现异常,出现异常就不用管退出码,如果出现退出码,代码一定跑完了,再看退出码,异常后退出码是什么都没有任何意义。所以衡量一个进程退出我们只需要知道程序的退出码和推出信号。在进程的task_struct中有一个退出码和一个推出信号,当进程结束时会将这个给父进程。

2.3进程如何终止(exit和_exit)

        首先可以通过return实现进程终止。也可以通过exit来实现进程终止,exit在结束时会冲刷一次缓冲区,我们运行下面代码

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

int main()
{
    printf("hello process.exe\n");
    printf("exit");
    exit(0);
} 

运行后可以看到

两个都输出了。

还有一种是_exit函数,我们看下面代码

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

int main()
{
    printf("hello process.exe\n");
    printf("exit");
    _exit(0);
} 

运行后

我们可以看到exit会冲刷一次缓冲区,_exit不会冲刷缓冲区。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

R语言入门:“Hellinger“转化和“normalize“转化(弦转化)的公式表示与R代码实现

1、写在前面 vegan包中的decostand()函数为群落生态学研究提供了一些流行的(和有效的)标准化方法。有关decostand()函数标准化的一些标准化方法可以看我的另一篇笔记&#xff1a;R语言入门&#xff1a;vegan包使用decostand()函数标准化方法 由于在网络上没有找到关于这两个转…

AI 语音机器人系统怎么搭建

搭建AI语音机器人系统通常包括以下几个关键步骤&#xff1a; 确定需求和技术选型&#xff1a;首先要明确AI语音机器人需要实现的功能&#xff0c;选择合适的技术框架和工具&#xff0c;如自然语言处理工具、语音识别工具等。 搜集和准备数据&#xff1a;收集和整理与业务相关…

2.Python实战小项目—用Python批量压缩图片

2.Python实战小项目—用Python批量压缩图片 一摘要二个人简介三原理四流程五实战演示 一摘要 在Python中&#xff0c;批量压缩图片是一项相对直接且实用的任务&#xff0c;尤其适合需要处理大量图像数据的场合。Pillow库提供了一种简便的方式来达成这个目标&#xff0c;其强大的…

怎样在外网登录访问CRM管理系统?

一、什么是CRM管理系统&#xff1f; Customer Relationship Management&#xff0c;简称CRM&#xff0c;指客户关系管理&#xff0c;是企业利用信息互联网技术&#xff0c;协调企业、顾客和服务上的交互&#xff0c;提升管理服务。为了企业信息安全以及使用方便&#xff0c;企业…

我为什么想成为一名程序员

#为什么你选择成为一名程序员# 目录 原因&#xff1a; 后续选择&#xff1a; 结尾&#xff1a; 原因&#xff1a; 本人是一个00后&#xff0c;出生在农村当时经济相对来说比较落后&#xff0c;村里面基本上都没几个人有手机。当时有些小伙伴他们拿着自己大人的手机在那里玩…

Youtube DNN

目录 1. 挑战 2. 系统整体结构 3.召回 4. 排序 5. 训练和测试样本的处理 1. 挑战 &#xff08;1&#xff09;规模。很多现有的推荐算法在小规模上效果好&#xff0c;但Youtobe规模很大。 &#xff08;2&#xff09;新颖度。Youtobe语料库是动态的&#xff0c;每秒都会有…

Java中的栈和队列

1.前言 在计算机科学中&#xff0c;数据结构是用来组织和存储数据的方式&#xff0c;以便可以高效地访问和修改。栈和队列是两种最基本的数据结构&#xff0c;它们在各种计算过程中都有广泛的应用。本文将介绍栈和队列的概念、特性以及它们的一些常见应用。 2.栈 2.1概念 栈…

姑苏寻韵~庆开放原子开源大赛 OpenTiny 前端 Web 应用开发挑战赛路演圆满落幕。

春日已至&#xff0c;姑苏古城迎来了一场编程的盛宴——开放原子开源大赛OpenTiny前端Web应用开发挑战赛。历时三个月的激烈角逐&#xff0c;OpenTiny与众多开发者携手共赴这场智慧的较量。决赛路演于4月14日在苏州&#xff08;太湖&#xff09;产业软件园圆满落下帷幕~ 开放原…

编译一个基于debian/ubuntu,centos,arhlinux第三方系统

目录 前言 准备工作 下载linux源码进行编译 linux源码下载 网站 问题 解决办法 编译 可能会遇到的问题 chroot下载debian环境 进入虚拟环境 把chroot的根目录文件打包为.gz文件 编译init文件&#xff08;用于系统启动时的一系列引导&#xff09; 给予文件夹权限 …

Zabbix 监控软件(一)

通常我们服务搭建成功 但不清楚服务器的运行状况&#xff0c;这时候就需要会使用监控系统查看服务器状态以及网站流量指标&#xff0c;利用监控系统的数据去了解上线发布的结果&#xff0c;和网站的健康状态。 利用一个优秀的监控软件&#xff0c;我们可以: ●通过一个友好的界…

基于java+springboot+vue实现的物业管理系统(文末源码+Lw+ppt)23-23

摘 要 快速发展的社会中&#xff0c;人们的生活水平都在提高&#xff0c;生活节奏也在逐渐加快。为了节省时间和提高工作效率&#xff0c;越来越多的人选择利用互联网进行线上打理各种事务&#xff0c;通过线上物业管理系统也就相继涌现。与此同时&#xff0c;人们开始接受方…

Leetcode算法训练日记 | day31

专题九 贪心算法 一、分发饼干 1.题目 Leetcode&#xff1a;第 455 题 假设你是一位很棒的家长&#xff0c;想要给你的孩子们一些小饼干。但是&#xff0c;每个孩子最多只能给一块饼干。 对每个孩子 i&#xff0c;都有一个胃口值 g[i]&#xff0c;这是能让孩子们满足胃口的…

如何查看项目中使用的Qt版本

如何查看项目中使用的Qt版本 1.点击左下角电脑按钮查看Qt版本。 2.点击左侧栏项目按钮查看Qt版本。

Android开发——Fragment

Demo fragment_blank.xml <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_pare…

Windows系统远程桌面服务(RDP)的安全层和加密级别

1.【win10企业版/server2012】 1.服务器运行gpedit.msc&#xff0c;打开本地组策略&#xff0c;进入列表目录【计算机配置】-【管理模板】-【windows组件】-【远程桌面服务】-【远程桌面会话主机】-【安全】 2.双击“远程(RDP)连接要求使用指定的安全层”选项&#xff0c;配置…

Redis入门到通关之数据结构解析-RedisObject

文章目录 ☃️概述☃️源码 ☃️概述 RedisObject 是 Redis 中表示数据对象的结构体&#xff0c;它是 Redis 数据库中的基本数据类型的抽象。在 Redis 中&#xff0c;所有的数据都被存储为 RedisObject 类型的对象。 RedisObject 结构体定义如下&#xff08;简化版本&#xf…

linux负载均衡 和 系统负载分析笔记

1 负载均衡 1.1 计算负载 1.1.1 PELT算法简介 从Linux3.8内核以后进程的负载计算不仅考虑权重&#xff0c;⽽且跟踪每个调度实体的历史负载情况&#xff0c;该算法称为PELT(Per-entity Load Tracking) 《奔跑吧Linux内核》卷1&#xff1a;基础架构&#xff1b;P505 相关资料…

LeetCode216:组合总和Ⅲ

题目描述 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9 每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组合可以以任何顺序返回。 解题思想 使用回溯算法 代码 class So…

Cubemx+RTL8201驱动

Cubemx 生产的驱动只能选择LAN8742&#xff0c;无法适配RTL8201&#xff0c;这篇文字就分享一下如何逐步修改lan8742的代码去适配。 一、地址问题 入口函数&#xff1a;int32_t LAN8742_Init(lan8742_Object_t *pObj) 主要是做PHY 地址检测&#xff0c;但是我们查阅RTL8201手…

2024中国国际中医药健康服务博览会(7月深圳中医药展)

聚焦中医国粹&#xff0c;助力健康中国 2024第五届中国国际中医药健康服务&#xff08;深圳&#xff09;博览会 暨粤港澳大湾区中医药高质量发展大会 邀请函 时间&#xff1a;2024年7月31日-8月2日 地址:深圳会展中心&#xff08;福田&#xff09; 支持单位&#xff…