【Linux】15.Linux进程概念(4)

news2025/1/19 13:55:15

文章目录

    • 程序地址空间前景回顾
    • C语言空间布局图:
      • 代码1
      • 代码2
      • 代码3
      • 代码4
      • 代码5
      • 代码6
      • 代码7


程序地址空间前景回顾

历史核心问题:

pid_t id = fork();

if(id == 0)

else if(id>0)

为什么一个id可以放两个值呢?之前没有仔细讲。


C语言空间布局图:

749795927c63a65fcb3a88b14d8319a1


代码1

来看一段代码:

#include <stdio.h>
#include <stdlib.h>

int main(){
    char *str = "hello linux";
    *str = 'H';
    
    printf("xxx=%s\n",getenv("xxx")); // 获取环境变量xxx的值
    return 0;
}

这段代码编译会报错。

因为"hello linux"储存在字符常量区,具有只读属性, *str = 'H';这个代码表面上我们是想要把第一个h换成H,但是因为只有只读属性,无法更改。


代码2

接下来写一段代码:

#include <stdio.h>
#include <stdlib.h>

int g_val_1;//未初始化全局变量
int g_val_2 = 100;//已初始化全局变量

int main(){
    printf("code addr:%p\n",main); // 打印main函数的地址,位于代码段
    const char* str="hello linux";
    printf("read only string addr:%p\n",str); // 打印字符串常量的地址,位于只读数据段
    printf("init global value affr:%p\n",&g_val_2); // 打印已初始化的全局变量地址,位于数据段
    printf("uninit global value affr:%p\n",&g_val_1); // 打印未初始化的全局变量地址,位于BSS段
    char *men = (char*)malloc(100);
    printf("head addr:%p\n",men); // 打印堆区地址
    printf("stack addr:%p\n",&str); // &str是指针变量本身的地址,而不是它指向的字符串的地址
    
    return 0;
}

运行结果:

fa863055925237dd0de37d040841b6f9

可以看到,下方的始终要比上方的地址大一点。


代码3

我们还可以把代码再改一下:

#include <stdio.h>
#include <stdlib.h>

int g_val_1;//未初始化全局变量
int g_val_2 = 100;//已初始化全局变量

int main(){
    printf("code addr:%p\n",main); // 打印main函数的地址,位于代码段
    const char* str="hello linux";
    printf("read only string addr:%p\n",str); // 打印字符串常量的地址,位于只读数据段
    printf("init global value affr:%p\n",&g_val_2); // 打印已初始化的全局变量地址,位于数据段
    printf("uninit global value affr:%p\n",&g_val_1); // 打印未初始化的全局变量地址,位于BSS段
    char *men = (char*)malloc(100);
    printf("head addr:%p\n",men); // 打印堆区地址
    printf("stack addr:%p\n",&str); // &str是指针变量本身的地址,而不是它指向的字符串的地址
    printf("stack addr:%p\n",&men); // 打印men指针变量本身的地址,位于栈区 
    int a; // 定义3个局部变量,它们都位于栈区
    int b; // 栈区地址从高到低分配
    int c;
    printf("stack addr:%p\n",&a); // 打印局部变量的地址,位于栈区
    printf("stack addr:%p\n",&b); // 打印局部变量的地址,位于栈区
    printf("stack addr:%p\n",&c); // 打印局部变量的地址,位于栈区
    
    return 0;
}

运行结果:

0c2838dd3e6c3b63b6b76796e86b14bc

为什么栈区地址看起来不规律,但实际上栈的增长方向是从高地址到低地址:

  1. 不同类型变量的对齐要求不同
  2. 编译器优化会重排变量位置
  3. 指针变量(str和men)通常会放在一起
  4. 整型变量(a,b,c)通常会放在一起

代码4

我们还可以把代码再改一下:

#include <stdio.h>
#include <stdlib.h>

int g_val_1;//未初始化全局变量
int g_val_2 = 100;//已初始化全局变量

int main(){
    printf("code addr:%p\n",main); // 打印main函数的地址,位于代码段
    const char* str="hello linux";
    printf("read only string addr:%p\n",str); // 打印字符串常量的地址,位于只读数据段
    printf("init global value affr:%p\n",&g_val_2); // 打印已初始化的全局变量地址,位于数据段
    printf("uninit global value affr:%p\n",&g_val_1); // 打印未初始化的全局变量地址,位于BSS段
    char *men = (char*)malloc(100);
    char *men1 = (char*)malloc(100);
    char *men2 = (char*)malloc(100);
    printf("head addr:%p\n",men); // 打印堆区地址
    printf("head addr:%p\n",men1); // 打印堆区地址
    printf("head addr:%p\n",men2); // 打印堆区地址
    printf("stack addr:%p\n",&str); // &str是指针变量本身的地址,而不是它指向的字符串的地址
    printf("stack addr:%p\n",&men); // 打印men指针变量本身的地址,位于栈区 
    int a; // 定义3个局部变量,它们都位于栈区
    int b; // 栈区地址从高到低分配
    int c;
    printf("stack addr:%p\n",&a); // 打印局部变量的地址,位于栈区
    printf("stack addr:%p\n",&b); // 打印局部变量的地址,位于栈区
    printf("stack addr:%p\n",&c); // 打印局部变量的地址,位于栈区
    
    return 0;
}

运行结果:

6f5d7ab1cd43b361f72047501f2b1045

堆区地址上升。


代码5

验证一个语法问题:

为什么用static修饰的变量不会被释放呢?

我们把代码改一下:

#include <stdio.h>
#include <stdlib.h>

int g_val_1;//未初始化全局变量
int g_val_2 = 100;//已初始化全局变量

int main(){
    printf("code addr:%p\n",main); // 打印main函数的地址,位于代码段
    const char* str="hello linux";
    printf("read only string addr:%p\n",str); // 打印字符串常量的地址,位于只读数据段
    printf("init global value affr:%p\n",&g_val_2); // 打印已初始化的全局变量地址,位于数据段
    printf("uninit global value affr:%p\n",&g_val_1); // 打印未初始化的全局变量地址,位于BSS段
    char *men = (char*)malloc(100);
    char *men1 = (char*)malloc(100);
    char *men2 = (char*)malloc(100);
    printf("head addr:%p\n",men); // 打印堆区地址
    printf("head addr:%p\n",men1); // 打印堆区地址
    printf("head addr:%p\n",men2); // 打印堆区地址
    printf("stack addr:%p\n",&str); // &str是指针变量本身的地址,而不是它指向的字符串的地址
    printf("stack addr:%p\n",&men); // 打印men指针变量本身的地址,位于栈区 
    static int a; // 定义3个局部变量,它们都位于栈区
    int b; // 栈区地址从高到低分配
    int c;
    printf("a = stack addr:%p\n",&a); // 打印局部变量的地址,位于栈区
    printf("stack addr:%p\n",&b); // 打印局部变量的地址,位于栈区
    printf("stack addr:%p\n",&c); // 打印局部变量的地址,位于栈区
    
    return 0;
}

运行结果:

4483091cf2545a9e5d8925b2bcc545f2

说明static修饰的局部变量,编译的时候就已经被编译到全局数据区了。


代码6

我们改一下代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int g_val = 100;

int main()
{
    pid_t id = fork();
    if(id == 0){
        //子进程
        while(1){
            printf("i am child, pid: %d, ppid: %d, g_val: %d, &g_val: %p\n",getpid(),getppid(),g_val,&g_val);
            sleep(1);
        }
    }
    else{
        //父进程
        while(1){
            printf("i am parent, pid: %d, ppid: %d, g_val: %d, &g_val: %p\n",getpid(),getppid(),g_val,&g_val);
            sleep(1);
        }
    }
    return 0;
}

运行结果:

f46d17003ed59b8528ee71e304f73f75


代码7

然后改一下代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int g_val = 100;

int main()
{
    pid_t id = fork();
    if(id == 0){
        int cnt = 5;
        //子进程
        while(1){
            printf("i am child, pid: %d, ppid: %d, g_val: %d, &g_val: %p\n",getpid(),getppid(),g_val,&g_val);
            sleep(1);
            if(cnt) cnt--;
            else {
                g_val = 200;
                printf("子进程change g-val : 100->200\n");
                cnt--;
            }
        }
    }
    else{
        //父进程
        while(1){
            printf("i am parent, pid: %d, ppid: %d, g_val: %d, &g_val: %p\n",getpid(),getppid(),g_val,&g_val);
            sleep(1);
        }
    }
    return 0;
}

运行结果:

a054373b353c63aa0bdbb82444df6f52

我们可以看到子进程改成了200,父进程没有变。

但是这不重要,重要的是他们两个不同的值地址却是一样的。

匪夷所思!但是结论是这样的。

至少我们现在可以认为:如果变量的地址是物理地址,那么是不可能存在上述现象的。所以这个地址绝对不是物理地址。

我们一般叫这个为线性地址,或者虚拟地址。

我们平时写的C/C++,用的指针,指针里面存放的地址全都不是物理地址!

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

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

相关文章

一文读懂服务器的HBA卡

什么是 HBA 卡 HBA 卡&#xff0c;全称主机总线适配器&#xff08;Host Bus Adapter&#xff09; &#xff0c;是服务器与存储装置间的关键纽带&#xff0c;承担着输入 / 输出&#xff08;I/O&#xff09;处理及物理连接的重任。作为一种电路板或集成电路适配器&#xff0c;HBA…

oracle使用case when报错ORA-12704字符集不匹配原因分析及解决方法

问题概述 使用oracle的case when函数时&#xff0c;报错提示ORA-12704字符集不匹配&#xff0c;如下图&#xff0c;接下来分析报错原因并提出解决方法。 样例演示 现在有一个TESTTABLE表&#xff0c;本表包含的字段如下图所示&#xff0c;COL01字段是NVARCHAR2类型&#xff0…

Linux-----线程同步(条件变量)

目录 相关API restrict关键字 线程间条件切换函数 条件变量pthread_cond_t 案例 在前面的锁的基础上进一步提高线程同步效率&#xff0c;也就是两个线程只用锁去执行的话依然会存在资源竞争的情况&#xff0c;也就是抢锁&#xff0c;这里就需要在锁的这边加上限制&#xf…

每日进步一点点(网安)

今日练习题目是PHP反序列化&#xff0c;也学习一下说明是序列化和反序列化 1.PHP序列化 序列化是指将数据结构或对象转换为可传输或可储存的格式的过程。这通常需要将数据转换为字节流或者其他编码格式&#xff0c;以便在不同系统和应用程序之间进行传输或存储 在PHP中&…

Java-数据结构-二叉树习题(1)

对于二叉树的学习&#xff0c;主要的还是得多多练习~毕竟二叉树属于新的知识&#xff0c;并且也并不是线性结构&#xff0c;再加上经常使用递归的方法解决二叉树的问题&#xff0c;所以代码的具体流程还是无法看到的&#xff0c;只能通过画图想象&#xff0c;所以还是必须多加练…

(二)afsim第三方库编译(qt编译)

注意&#xff1a;源码编译的路径不能有中文否则报错&#xff0c;压缩包必须用官网下载的xz格式解压的才可以&#xff0c;否则sudo ./configure命令找不到 先编译openssl3.1.1软件包&#xff0c;否则编译的qt库将不支持network&#xff0c;相关库的编译(上文&#xff08;一&…

【QT用户登录与界面跳转】

【QT用户登录与界面跳转】 1.前言2. 项目设置3.设计登录界面3.1 login.pro参数3.2 界面设置3.2.1 登录界面3.2.2 串口主界面 4. 实现登录逻辑5.串口界面6.测试功能7.总结 1.前言 在Qt应用程序开发中&#xff0c;实现用户登录及界面跳转功能是构建交互式应用的重要步骤之一。下…

【docker踩坑记录】

docker踩坑记录 踩坑记录(持续更新中.......)docker images 权限问题 踩坑记录(持续更新中…) docker images 权限问题 permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Head "http://%2Fvar%2Frun%2Fdocker.s…

搜维尔科技:Xsens人形机器人解决方案的优势

Xsens 致力于推动人形机器人技术的发展&#xff0c;塑造机器人与人类环境无缝融合的未来&#xff0c;通过创新精确和协作&#xff0c;协助生产和服务&#xff0c;改善人类生活和产业。 Xsens通过人形跟随捕捉详细的人体运动数据&#xff0c;使机器人能够学习类人的动作&#x…

国内微电子(集成电路)领域重点高校的特色与优势

本文旨在梳理国内微电子&#xff08;集成电路&#xff09;领域重点高校的特色与优势&#xff0c;为有志于从事相关领域的学生提供参考。文章将从学科特色、科研实力&#xff08;以ISSCC论文为参考之一&#xff09;、行业认可度等方面进行分析&#xff0c;并强调实验室、导师、研…

leetcode707-设计链表

leetcode 707 思路 本题也是用了虚拟头节点来进行解答&#xff0c;这样的好处是&#xff0c;不管是头节点还是中间的节点都可以当成是中间节点来处理&#xff0c;用同一套方法就可以进行处理&#xff0c;而不用考虑太多的边界条件。 下面题目中最主要的实现就是添加操作addA…

数据结构-栈队列OJ题

文章目录 一、有效的括号二、用队列实现栈三、用栈实现队列四、设计循环队列 一、有效的括号 (链接&#xff1a;ValidParentheses) 这道题用栈这种数据结构解决最好&#xff0c;因为栈有后进先出的性质。简单分析一下这道题&#xff1a;所给字符串不是空的也就是一定至少存在一…

MindAgent:基于大型语言模型的多智能体协作基础设施

2023-09-18 &#xff0c;加州大学洛杉矶分校&#xff08;UCLA&#xff09;、微软研究院、斯坦福大学等机构共同创建的新型基础设施&#xff0c;目的在评估大型语言模型在游戏互动中的规划和协调能力。MindAgent通过CuisineWorld这一新的游戏场景和相关基准&#xff0c;调度多智…

近红外简单ROI分析matlab(NIRS_SPM)

本次笔记主要想验证上篇近红外分析是否正确&#xff0c;因为叠加平均有不同的计算方法&#xff0c;一种是直接将每个通道的5分钟实时长单独进行叠加平均&#xff0c;另一种是将通道划分为1分钟的片段&#xff0c;将感兴趣的通道数据进行对应叠加平均&#xff0c;得到一个总平均…

开发神器之cursor

文章目录 cursor简介主要特点 下载cursor页面的简单介绍切换大模型指定ai学习的文件指定特定的代码喂给ai创建项目框架文件 cursor简介 Cursor 是一款专为开发者设计的智能代码编辑器&#xff0c;集成了先进的 AI 技术&#xff0c;旨在提升编程效率。以下是其主要特点和功能&a…

电脑风扇声音大怎么办? 原因及解决方法

电脑风扇是电脑的重要组件之一&#xff0c;它的作用是为电脑的各个部件提供冷却&#xff0c;防止电脑过热。然而&#xff0c;有时候我们会发现电脑风扇的声音特别大&#xff0c;不仅影响我们的使用体验&#xff0c;也可能是电脑出现了一些问题。那么&#xff0c;电脑风扇声音大…

SpringBoot错误码国际化

先看测试效果&#xff1a; 1. 设置中文 2.设置英文 文件结构 1.中文和英文的错误消息配置 package com.ldj.mybatisflex.common;import lombok.Getter;/*** User: ldj* Date: 2025/1/12* Time: 17:50* Description: 异常消息枚举*/ Getter public enum ExceptionEnum {//…

软考高级5个资格、中级常考4个资格简介及难易程度排序

一、软考高级5个资格 01、网络规划设计师 资格简介&#xff1a;网络规划设计师要求考生具备全面的网络规划、设计、部署和管理能力&#xff1b;该资格考试适合那些在网络规划和设计方面具有较好理论基础和较丰富从业经验的人员参加。 02、系统分析师 资格简介&#xff1a;系统分…

如何通过 Apache Airflow 将数据导入 Elasticsearch

作者&#xff1a;来自 Elastic Andre Luiz 了解如何通过 Apache Airflow 将数据导入 Elasticsearch。 Apache Airflow Apache Airflow 是一个旨在创建、安排&#xff08;schedule&#xff09;和监控工作流的平台。它用于编排 ETL&#xff08;Extract-Transform-Load&#xff0…

STM32 学习笔记【补充】(十)硬件I2C读写MPU6050

该系列为笔者在学习STM32过程&#xff08;主线是江科大的视频&#xff09;中的记录与发散思考。 初学难免有所纰漏、错误&#xff0c;还望大家不吝指正&#xff0c;感谢~ 一、I2C 外设简介 I2C&#xff08;Inter-Integrated Circuit&#xff09;是一种多主多从的串行通信协议…