进程概念(详细版)
本文主要介绍进程的相关知识
文章目录
- 认识冯诺依曼体系结构
- 操作系统的基本概念
- 操作系统的作用是什么
- 系统调用和库函数相关概念
- 进程基本概念
- 描述进程进程控制块(PCB)
- task_struct 结构体
- 进程是如何被操作系统管理起来的
- 先描述
- 再组织
- 描述好,组织好,才好管理
- 进程的查看
- 获取进程标识符
- getpid()和getppid()
- 进程的创建
- fork()创建进程
- 进程的状态
- 操作系统原理中的进程状态
- 进程运行和进程终止
- 进程阻塞
- 进程挂起
- 进程
- R (running)运行状态
- S (sleeping)睡眠状态
- D (disk sleep) 深度睡眠状态
- T (stopped) 暂停状态
- Z (zombie) 僵尸状态
- X (dead) 死亡状态
- 僵尸进程
- 僵尸进程的危害
- 孤儿进程
- 进程优先级
- 概念
- 如何查看进程优先级
- PR(process priority)和NI (nice)是什么?
- 注意点
- 如何改变(调节)进程优先级
- 环境变量
- 概念:
- 常见环境变量
- 环境变量表
- 如何查看环境变量
- 如何获取环境变量
- 三种方式
- 通过main函数的参数获取
- 通过第三方变量environ获取
- 通过系统调用获取
- getenv()获取环境变量
- putenv()设置环境变量
- 环境变量的全局属性
- 本地变量
- 环境变量的定义
- 进程地址空间
- 程序地址空间
- 进程地址空间
- mm_struct()结构组成
- 为什么需要有虚拟地址这个东西呢?
- 感知地址空间的存在
认识冯诺依曼体系结构
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存失败,源站可能有防盗链机制,建议将图片保存下来直接上传下上传(iHNmXu6tRsGe-1677761095381)(C:\Users\华哥\Pictures\素材\20190318201755609.png)(C:\Users\华哥\Pictures\素材\20190318201755609.png)]](https://img-blog.csdnimg.cn/fbbe10e73bc84ba98eb789e8018488e5.png)
几乎所有的计算机都是遵守冯诺依曼体系的,计算机是由一个个硬件组成的,配套着操作系统就可以发挥出其功能。
冯诺依曼体系将计算机的硬件分成了主要的三大块
- 1.输入输出设备:输入设备有键盘鼠标,写字板,网卡等;输出设备有显示器 打印机等
- 2.cup(中央处理器,由运算器和控制器组成),主要完成一些算数运算和逻辑运算。
- 3.存储器,也就是内存,内存的存在是为了适配外设和CPU速度不均的问题。
为什么要有内存这个东西呢?
从技术角度分析
cpu的运算速度 > 寄存器的速度 > 缓存的速度 > 内存的速度 > 外设的速度(磁盘等)
因为数据大多都是需要cpu计算的 但是输入输出设备的速度和cpu的速度完全不是一个级别的,cpu速度远远快于外设速度,那么需要计算的数据直接交给cpu处理的话就会因为外设的速度慢而使得cpu的效率发挥不出来,所以cpu都是不和外设直接打交道的,都是通过内存来交流的,因为内存的速度相比外设快,外设可以将数据加载到内存,然后让cpu找内存拿数据,处理完后返回给内存,再回到外设,这样设计就可以达到视频外设和cpu速度不均的问题,提高效率。
看到这里我们可能会有一个疑惑,cpu和外设速度相差大就让外设加载数据到内存,那么外设加载的速度还是很慢啊,加载到内存也还是会很慢啊,那cpu还是会需要等待内存给它提供来自外设的数据,效率不还是没有提升吗?
这里举例说明吧,我们计算机是有预装载的机制的,就像我们开机的时候,需要等上个几十秒,这就是计算机在把操作系统加载到内存,有了操作系统计算机才可以运行起来,这个等待的过程就是预装载,刚开机会觉得电脑有些卡,但是过了一段时间就很顺畅,这就是因为计算机在加载数据到内存里;还有就是我们在写一个程序的时候会编译运行,运行之前这个程序一定是要被加载到内存的,但是并不是我们什么时候运行就什么时候加载,而是计算机会提前把我们写的代码加载到内存中,这就是预装载,可以是的数据提前被加载到内存,提高效率。(预装载是体系结构规定的)
操作系统的基本概念
任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。笼统的理解,操作系统包括:
- 内核(进程管理,内存管理,文件管理,驱动管理)
- 其他程序(例如函数库, shell程序等等)
操作系统的作用是什么
操作系统生来就是为了管理软硬件资源的,是一个搞管理的软件
系统调用和库函数相关概念
- 操作系统可以向用户提供服务,但是操作系统不会把自己完全地暴露给用户,而是向用户提供一些接口,供用户开发使用,这些接口就叫做系统调用。
- 系统调用在使用上功能比较基础,对用户的要求相对较高,所以开发者可以将系统调用适度封装,形成库,有了库,就很有利于上层用户或开发者进行二次开发了。库函数也就是对系统调用的封装的产物。

思考:c/c++代码可以在windows和linux不同的系统下跑起来的,但是不同的系统的系统接口(系统调用)是不同的,那在两个系统上跑的都是一样的代码(c、c++代码),代码的执行结果为什么是一样的呢?
这就要说到c/c++的库了,库会帮我们分辨不同的平台,进而使得我在linux下就是调用的linux的系统调用,在Windows下就是调用的windows的系统调用,是类似多态的原理的,调用printf语句,在linux下就会调linux的对应的系统调用将数据打印到显示器,反之亦然。同一个对象执行相同动作,表现出不同的结果,这就是多态。
进程基本概念
进程的概念有两个层面上的解释:
教科书上的解释:进程是程序的一个执行实例,是正在执行的程序。
内核上的观点:担当分配系统资源(cpu 时间,内存)的实体。
描述进程进程控制块(PCB)
进程的信息会被放到一个叫进程控制块的数据结构中,可以理解成它是进程属性的集合。linux中的PCB实际上是一个叫做task_struct的结构体
task_struct 结构体
task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息
task_struct中的内容
- 标示符: 描述本进程的唯一标示符,用来区别其他进程。
- 状态: 任务状态,退出代码,退出信号等。
- 优先级: 相对于其他进程的优先级。
- 程序计数器: 程序中即将被执行的下一条指令的地址。
- 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
- 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
- I/ O状态信息: 包括显示的I/O请求,分配给进程的I/ O设备和被进程使用的文件列表。
- 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
- 其他信息
//截取源码部分
struct task_struct {volatile long state;//状态 /* -1 unrunnable, 0 runnable, >0 stopped */void *stack;//栈atomic_t usage;unsigned int flags; /* per process flags, defined below */unsigned int ptrace;int lock_depth; /* BKL lock depth *//* task state */int exit_state;int exit_code, exit_signal;int pdeath_signal; /* The signal sent when the parent dies *//* ??? */unsigned int personality;unsigned did_exec:1;unsigned in_execve:1; /* Tell the LSMs that the process is doing an* execve */unsigned in_iowait:1;/* Revert to default priority/policy when forking */unsigned sched_reset_on_fork:1;pid_t pid;//标识符pid_t tgid;//...
};
进程是如何被操作系统管理起来的
六字真言:“先描述,再组织”
操作系统上跑的是一个个程序,也就是进程,那么操作系统就是要管理这些进程,怎么管理呢?
进程有它的对应的属性(下面会有介绍),因为linux操作系统是用c语言写的,所以进程的属性是用结构体来描述的(先描述),这些结构体会被一个双链表链接起来,所以对进程的管理也就演变成了对数据结构的管理。
先描述
将进程用一个数据结构描述,linux中用task_struct 结构体来描述,将进程的所有属性都包含在这个结构体中(里面会有指针指向进程的代码和数据)。一个进程的组成就是其task_struct 结构体和其对应的代码和数据。
再组织
将一个个进程的数据结构组织在一起,linux中使用双链表将这些结构体一个个连起来,完成对进程数据结构的组织。
描述好,组织好,才好管理
管理进程就是变成了对数据结构的管理,管理好这些数据结构就可以管理好进程!!!
进程在内存中的样子:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cwsCeyYl-1677761095387)(进程概念/image-20221010005652659.png)]](https://img-blog.csdnimg.cn/aa66ae0b89ad4635aedd79c7f0e68b09.png)
如何将进程运行起来:

进程的查看
介绍几种方式:
一、ps命令查看
ps axj | grep 文件名 | grep -v grep
将proc程序跑起来,形成一个进程

用上面的命令进行查看进程!

二、通过/proc系统文件夹进行查看
这里的查看某个进程是需要明确该进程的pid才可以进行查看的,因为在/proc文件夹中大多都是存的进程的pid,通过pid来标识进程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pHAqbk2Z-1677761095396)(C:\Users\华哥\AppData\Roaming\Typora\typora-user-images\image-20221009002430289.png)]
三、通过top命令查看

获取进程标识符
getpid()和getppid()
man 3 getpid

getpid()是获取进程本身的标识符 pid
getppid()是获取当前进程的父进程的 pid
代码演示:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{while(1){printf("我是一个进程!\n,我的pid:%d ppid:%d\n",getpid(),getppid());sleep(1);}return 0;
}

进程的创建
fork()创建进程
注意的点:
- fork()是有两个返回值的 你没听错!如果创建成功会返回子进程的pid给父进程,会给子进程返回给0;如果创建失败就会返回-1。
为什么对父进程就是返回子进程的pid,对创建出的子进程就是返回的0呢?
因为一个父进程可以拥有多个子进程,所以需要父进程需要去标识每个子进程,那么就需要知道子进程的pid,故调用fork()函数创建子进程,fork()会给父进程返回创建出来的子进程的pid;对于子进程,它只有一个父进程,并且一个进程被创建出来就是知道自己和其父亲的pid的,所以fork()对创建出来的进程的返回值为0即可;当创建失败就返回-1;
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{pid_t id=fork();while(1){if(id>0){printf("我是父进程! 我的pid:%d ppid:%d \n",getpid(),getppid());}else if(id==0){printf("我是子进程! 我的pid:%d ppid:%d \n",getpid(),getppid());}else {printf("创建失败!\n");}sleep(1);//循环一次休眠一秒}return 0;
}
可以看出运行的结果是交替打印父子进程的信息 因为这里调用fork()创建出了子进程,那么就会有两个进程,从而会使得程序有两个执行流,父子进程都会执行各自的执行流,所以在循环的作用下就会出现交替打印父子进程信息的结果,这也是父子进程执行各自执行流的结果。

-
父子进程代码是共享的,数据会各自开辟空间 私有一份(会有写时拷贝,也就是当子进程要改变父进程中的数据就会独立开辟一块空间,建立自己的数据,使得自己不会影响到父进程的数据,实现进程的独立性)
-
fork()一般都会用if else 语句实行分流操作 使得子进程去完成具体的某项工作 而不是和父进程执行一样的操作

思考:上面的代码中的pid_t id =fork() 为什么一个id可以接收不同的返回值呢?
进程的状态
操作系统原理中的进程状态
进程运行和进程终止
进程运行就是进程在运行队列中准备就绪,等待cpu执行其代码的状态;进程终止就是进程通过return或exit或遇到异常退出,随时等待操作系统回收其资源的状态。
进程阻塞
什么是进程阻塞?
一个进程不仅仅会申请cpu的资源,还可能会申请其他设备的资源,如磁盘网卡显示器等,当一个进程在等待队列中准备就绪,当cup资源就绪时,开始执行该进程的代码,如果该进程的代码涉及到申请其他资源的时候(例如访问磁盘资源,涉及文件相关操作),如果此时的某个设备资源正在别其他进程使用,使得该进程申请资源时资源不就绪,那么该进程的task_struct 就会被操作系统从运行队列中移除,然后放到操作系统中描述对应设备资源的数据结构中(一般是一个等待队列里),我们称进程等待某个资源就绪的状态为阻塞状态。

进程挂起
什么是进程挂起?
当内存中的进程太多(其数据和代码都会一并加载到内存中),就会使得内存的空间不足,那么操作系统就会将短期内不会被调度的进程的代码和数据置换回磁盘中,以此来缓解内存空间不足。代码和数据被置换回磁盘的进程就会被操作系统挂起!!!(此操作一般会涉及磁盘的高频访问)

进程
为了表示进程运行的情况,linux中的进程都具有其对应的状态。
linux内核源代码:
/** The task state array is a strange "bitmap" of* reasons to sleep. Thus "running" is zero, and* you can test for combinations of others with* simple bit tests.*/
static const char *task_state_array[] = {"R (running)", /* 0 */"S (sleeping)", /* 1 */"D (disk sleep)", /* 2 */"T (stopped)", /* 4 */"T (tracing stop)", /* 8 */"Z (zombie)", /* 16 */"X (dead)" /* 32 */
};
R (running)运行状态
- R是运行状态,但是并不意味着它一定在运行,它表明进程要么就是在运行中,要么就是在运行队列中
演示:
#include<stdio.h>
#include<unistd.h>
int main()
{while(1){//啥也不干 就一直死循环 就会一直在运行队列里 就是R状态}return 0;
}

S (sleeping)睡眠状态
- 意味着进程在等待某种资源,一般不是cpu资源(这里的睡眠有时候也叫做可中断睡眠,可被操作系统杀死)
#include<stdio.h>
#include<unistd.h>
int main()
{while(1){printf("i am a process\n");sleep(1);}return 0;
}

D (disk sleep) 深度睡眠状态
- 磁盘休眠状态,也叫不可中断睡眠状态,在这个状态的进程通常会等待IO的结束,操作系统是无法杀掉D状态的进程的
- 这里可以用dd命令自己去试试。
- D状态对应操作系统层面上的阻塞状态,只不过一般是进程等待磁盘资源时才是D,等待其他资源一般是S
T (stopped) 暂停状态
- 可以发送SIGSTOP信号(19)给进程来停止(T)进程。这个被暂停的进程可以通过发送SIGCONT信号(18)让进程继续运行
窗口1:

窗口2:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V27IKIzV-1677761095420)(进程概念/image-20221013124909383.png)]](https://img-blog.csdnimg.cn/8bb2da26596c415c9e8664d9789c9eba.png)
监视窗口:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1IfgTBoA-1677761095424)(进程概念/image-20221013124630380.png)]](https://img-blog.csdnimg.cn/58bfda523ff84374a0665fac5355626b.png)
上面的操作涉及进程信号知识,后面的博客会讲解,这里只需知道19 号信号可以使进程暂停即可!
Z (zombie) 僵尸状态
- 僵尸状态是一个比较特殊的状态,当进程退出并且父进程(使用wait()系统调用等待子进程)没有读取到子进程退出的返回代码时就会产生僵尸进程
- 僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
- 只要子进程退出,父进程还在运行,但是父进程没有读取子进程状态,子进程就会进入Z状态
演示在下面!
X (dead) 死亡状态
- X状态只是一个返回状态,是不会在任务列表看到此状态的
僵尸进程
#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>
int main()
{pid_t id= fork();if(id>0){//parentprintf("i am parent\n");sleep(15);//休眠15s但是不退出exit(0);}else if(id==0){//childint n=10;//10s后退出 如果父进程未读取其退出代码 该进程会变成僵尸进程 成Z状态while(n--){printf("i am child\n");sleep(1);}printf("子进程退出!\n");exit(0);}else {printf("creat fail!\n");}return 0;
}
程序运行:

脚本监控进程状态:
//shell脚本
while :;do ps axj | head -1 && ps axj | grep myproc | grep -v grep;sleep 1;echo "#######################################################" ;done

僵尸进程的危害
- 进程的退出状态必须要被维持下去,因为它要告诉它的父进程,父进程交给它的任务它完成的怎么样了,但是如果父进程一直不读取,那么子进程就会一直处于Z状态!
- 维护退出状态本身就是维护进程的相关数据结构,所以操作系统需要维护进程的task_struct (pcb进程控制块),僵尸进程的退出信息一直不被读取,那么操作系统就需要一直维护它的PCB!
- 维护僵尸进程的PCB会造成内存资源的浪费,存在内存泄漏!!!
孤儿进程
什么是孤儿进程?
顾名思义,就是其父亲不要它了,这个进程成了孤儿。也就是一个进程的父进程提前退出,但是其子进程还没有退出,那么该子进程就会变成失去父亲的孤儿进程(孤儿进程会被1号进程收养,回收资源)
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{pid_t id= fork();if(id==0){//childint n=10;while(n--){sleep(1);printf("i am child! pid:%d i am running!\n",getpid());}}else if(id>0){int m=5;//parentwhile(m--) {printf("i am parent! pid:%d i am running!\n",getpid());sleep(1);}exit(0);}else{//error printf("fork fail!\n");}return 0;
}

监控:

进程优先级
因为计算的各种资源是有限的,但是进程却很多,那么进程之间就会有竞争,类比现实生活中的排队现象,我们都是站成一条长队,排在前面的就优先,同样的对应进程间也有需要进行排队使用资源,于是就有了有进程优先级。
概念
- cpu资源分配的先后顺序就是指进程的优先权
- 优先权高的进程有优先执行的权利,配置进程优先权对多任务环境的linux很有用,可以改善系统性能。
- 还可以把进程运行到指定的cpu上,这样就可以把不重要的进程安排到某个cpu,大大改善系统整体性能。
如何查看进程优先级
使用top命令就可以看到

PR(process priority)和NI (nice)是什么?
上图中的PR就是进程优先级。
NI是进程优先级的修正数值,可以通过改变进程的NI值对进程优先级进行适度的调节!
注意点
- PR越小,进程的优先级越高,代表进程越快被执行!
- NI值范围是[-20,19]的 一共40个级别
优先级公式:PRθ(new)=PRθ(old)+NI(NIϵ[−20,19])优先级公式:PR_\theta(new)=PR_\theta(old)+NI \space\space\space (NI\epsilon[-20,19]) 优先级公式:PRθ(new)=PRθ(old)+NI (NIϵ[−20,19])
如何改变(调节)进程优先级
更据上面的公式可以知道,进程优先级的调节是通过修改对应进程的NI值来实现的。
//步骤
1、输入top命令
2、再依次输入 r + 修改的进程pid + 回车 + 对应的nice值


前后对比:

环境变量
概念:
- 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数
如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但
是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性
常见环境变量
- 1、PATH :指定命令的搜索路径
- 2、HOME: 指定用户的主工作目录(即用户登录到linux系统中时,默认的目录)
- 3、 SHELL:当前Shell,它的值通常是/bin/bash
环境变量表
每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以’\0’结尾的环境字符串。

如何查看环境变量
//执行下面指令即可
1.查看所有环境变量 env命令会将所有的环境变量都显示出来
env
2.查看具体名称的环境变量
echo $环境变量名
如何获取环境变量
三种方式
通过main函数的参数获取
我们平常看到的main函数大多是不带参数的,但实际上它是有参数的,main函数可以带三个参数!!!
//第一个参数是执行main函数对应的进程的指令的个数,第二个是执行其对应进程的指令字符串,第三个是其对应进程的环境变量(字符指针数组,里面存的是一个个的环境变量字符串)
int main(int argc,char* argv[],char* env[])
{printf("%d\n",argc);for(int i=0;i<argc;i++)printf("%s\n",argv[i]);for(int i=0;i<argc;i++)printf("%s\n",env[i]);return 0;
}

通过第三方变量environ获取
- libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用的时候,要用extern声明。
#include <stdio.h>
int main(int argc, char *argv[])
{//可以通过environ找到存储环境变量的指针数组,进而打印里面的环境变量(一个个字符串)extern char **environ;int i = 0;for(; environ[i]; i++){printf("%s\n", environ[i]);}return 0;
}
通过系统调用获取
getenv()获取环境变量
char getenv(const char name)**
参数name为所需要获取的环境变量的名称,如果该环境变量在环境表中存在那么就会返回所要获取的环境变量(字符串),反之如果不存在就会返回空指针!
putenv()设置环境变量
#include<stdio.h>
#include<stdlib.h
int main(int argc,char* argv[],char* env[])
{extern char** environ;int i;printf("putenv之前环境变量表:\n");for(i=0;environ[i];i++)printf("%s\n",environ[i]);putenv("MYPATH = 66666666666666666666666");printf("putenv之后环境变量表:\n")for(i=0;environ[i];i++)printf("%s\n",environ[i]);return 0;
}
注:由于putenv会存在安全问题(野指针),可以使用setenv()来代替putenv() !
环境变量的全局属性
在命令行中我们可以定义两种变量,一种是本地变量,一种是环境变量。
本地变量

本地变量不具有全局属性,不会被子进程继承!!!
因为我们自己写的程序运行起来形成的进程,都是有bash创建的子进程,那么我们在命令行中定义本地变量,再到我们自己写的程序中去找在bash中定义的本地变量如果找得到说明本地变量是可以被子进程继承的,否则不可以!!!
注意:这里要清楚的理解我们写的代码形成的进程都是由bash创建的子进程!!!
#include<stdio.h>
#include<stdlib.h>
int main()
{printf("%s\n",getenv("LocalVal"));return 0;
}

环境变量的定义
我们只需要使用关键字export就可以将一个本地变量导出为环境变量!!!
export LocalVal=1234567//那么LocalVal就会被导进bash的环境表中了 注:=两边不要加空格!!!
#include<stdio.h>
#include<stdlib.h>
int main()
{printf("%s\n",getenv("MyVal"));return 0;
}

进程地址空间
程序地址空间
我们在学语言的时候经常听到什么全局变量是存在静态区,常量字符串是存在常量区,定义的普通变量是存在栈上的,malloc出来的数据是存在堆上的,这里的堆,栈,常量区,静态区都是程序地址空间的叫法!

但是一个程序运行起来就是一个进程,那么原来的程序地址空间的叫法是不准确的,应该叫做进程地址空间。
进程地址空间
什么是进程地址空间呢?
所谓的进程地址空间是操作系统通过软件的方式,给进程提供一个软件视角(进程地址空间),认为自己是独占着整个系统的所有资源(内存),每个进程运行起来的时候,操作系统都会先给其创建一个进程地址空间(mm_struct),将进程所需要的空间先规划好,当其真正需要使用的时候再给它分配,这样就避免了进程一运行操作系统就需要立马给其分配内存,提高了内存的使用效率。
如何管理地址空间呢?
还是六字真言:“先描述,再组织”
既然进程地址空间是一个个的内核数据结构mm_struct 那么管理好mm_struct 就可以管理好进程地址空间!
mm_struct()结构组成
这里是大概的简略的组成(其真实组成远不止这么点东西,这里只介绍其区域划分),其实源码是有一个vm_area_struct 结构体来完成各个区域的划分的(了解即可,无需深究!)


//笼统理解为下面结构
struct mm_struct
{//各个区域的划分unsigned int code_start;unsigned int code_end;unsigned int stack_start;unsigned int stack_end;unsigned int init_data_start;unsigned int init_data_end;unsigned int uninit_data_start;unsigned int uninit_data_end;...
}
注意:这里的进程地址空间并不是直接对应的物理地址,而是虚拟地址,物理地址和虚拟地址是通过页表转换的!
为什么需要有虚拟地址这个东西呢?
-
出于安全考虑,进程虽然认为只有其自己独占整个内存,但是这是操作系统为其画的大饼,实则是多个进程在共用一块内存,那么就会有多个进程会用到同一块物理空间的可能,如果每个进程都是直接访问的物理内存就会容易出现野指针的问题,例如原本多个进程共用的空间,其中一个进程终止了对该空间释放,那么就会使得原来与其共用一块内存的进程访问该空间时出现野指针,所以直接访问物理地址是具有安全隐患的!!!
-
通过添加一层软件层,完成有效的对进程操作内存的风险管理(权限管理),本质是为了保护物理内存各个进程的数据安全
-
将内存申请和内存使用在时间上解耦。通过虚拟地址空间,来屏蔽底层申请内存的过程,达到进程读写内存操作和OS进行内存管理进行软件层面上的分离。

感兴趣的可以去写代码验证上面的排列。
感知地址空间的存在
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int a=10;
int main()
{pid_t id=fork();if(id>0){//parentint n=10;while(n--){printf("我是父进程 pid:%d ppid:%d a:%d &a:%p\n",getpid(),getppid(),a,&a); sleep(1);}}else if(id==0){//childint n=10;while(n--){if(n==5){printf("我是子进程 我修改了a 为 200!\n");a=200;}printf("我是子进程 pid:%d ppid:%d a:%d &a:%p\n",getpid(),getppid(),a,&a);sleep(1);}}else {perror("fork error!");}return 0;
}


相关文章:
进程概念(详细版)
进程的概念本文主要介绍进程的相关知识 文章目录认识冯诺依曼体系结构操作系统的基本概念操作系统的作用是什么系统调用和库函数相关概念进程基本概念描述进程进程控制块(PCB)task_struct 结构体进程是如何被操作系统管理起来的先描述再组织描述好,组织好࿰…...
学习大数据应该掌握哪些技能
想要了解大数据开发需要掌握哪些技术,不妨先一起来了解一下大数据开发到底是做什么的~ 1、什么是大数据? 关于大数据的解释,比较官方的定义是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模…...
【spring】Spring Data --Spring Data JPA
Spring Data 的委托是为数据访问提供熟悉且符合 Spring 的编程模型,同时仍保留着相关数据存储的特殊特征。 它使使用数据访问技术、关系和非关系数据库、map-reduce 框架和基于云的数据服务变得容易。这是一个伞形项目,其中包含许多特定于给定数据库…...
mysql数据库之视图
视图(view)是一种虚拟的存在,视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。 通俗的讲,视图之保存了查询的sql逻辑,不保存查询结…...
数据库事务详解
概述事务就是数据库为了保证数据的原子性,持久性,隔离性,一致性而提供的一套机制, 在同一事务中, 如果有多条sql执行, 事务可以确保执行的可靠性.数据库事务的四大特性一般来说, 事务是必须满足 4 个条件(ACID):原子性(Atomicity&…...
Nessus: 漏洞扫描器-网络取证工具
Nessue 要理解网络漏洞攻击,应该理解攻击者不是单独攻击,而是组合攻击。因此,本文介绍了关于Nessus历史的研究,它是什么以及它如何与插件一起工作。研究了Nessus的特点,使其成为网络取证中非常推荐的网络漏洞扫描工具…...
操作系统实战45讲之现代计算机组成
我以前觉得计算机理论不让我感兴趣,而比较喜欢实践,即敲代码,现在才发现理论学好了,实践才能有可能更顺利,更重要的是理论与实践相结合。 在现代,几乎所有的计算机都是遵循冯诺依曼体系结构,而遵…...
Simple Baselines for Image Restoration
Abstract.尽管近年来在图像恢复领域取得了长足的进步,但SOTA方法的系统复杂性也在不断增加,这可能会阻碍对方法的分析和比较。在本文中,我们提出了一个简单的基线,超过了SOTA方法,是计算效率。为了进一步简化基线&…...
Python数据可视化:局部整体图表可视化(基础篇—6)
目录 1、饼图 2、圆环图 3、马赛克图 4、华夫饼图 5、块状/点状柱形图 在学习本篇博文之前请先看一看之前发过的关联知识:...
CSDN新星计划新玩法、年度勋章挑战赛开启
文章目录🌟 写在前面🌟 逐步亮相的活动🌟 勋章挑战赛🌟 新星计划🌟 有付费课程才可参与?🌟 成就铭牌🌟 博客跟社区的关系🌟 写在最后🌟 写在前面 哈喽&#…...
Docker之部署Mysql
通过docker对Mysql进行部署。 如果没有部署过docker,看我之前写的目录拉取镜像运行容器开放端口拉取镜像 前往dockerHub官网地址,搜索mysql。 找到要拉取的镜像版本,在tag下找到版本。 拉取mysql镜像,不指定版本数,…...
基于C/C++获取电脑网卡的IP地址信息
目录 前言 一、网卡是什么? 二、实现访问网卡信息 1.引入库及相关的头文件 2.操作网卡数据 3. 完整代码实现 4.结果验证 总结 前言 简单示例如何在windows下使用c/c代码实现 ipconfig/all 指令 提示:以下是本篇文章正文内容,下面案例可供参考…...
28相似矩阵和若尔当标准型
一、关于正定矩阵的一些补充 在此之前,先讲一下对称矩阵中那些特征值为正数的矩阵,这样特殊的矩阵称为正定矩阵。其更加学术的定义是: SSS 是一个正定矩阵,如果对于每一个非零向量xxx,xTSx>0x^TSx>0xTSx>0 正…...
springboot操作MongoDB
启动类及配置import com.mongodb.client.MongoClient;import com.mongodb.client.MongoClients;import com.mongodb.client.internal.MongoClientImpl;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplicatio…...
1月奶粉电商销售数据榜单:销售额约20亿,高端化趋势明显
鲸参谋电商数据监测的2023年1月份京东平台“奶粉”品类销售数据榜单出炉! 根据鲸参谋数据显示,1月份京东平台上奶粉的销量约675万件,销售额约20亿元,环比均下降19%左右。与去年相比,整体也下滑了近34%。可以看出&#…...
跨境数据传输是日常业务中经常且至关重要的组成部分
跨境数据传输是日常业务中经常且至关重要的组成部分。在过去的20年中,由于全球通信网络和业务流程的发展,全球数据流的模式已迅速发展。随着数据从数据中心移到数据中心和/或跨边界移动,安全漏洞已成为切实的风险。有可能违反国家和国际数据传…...
错误: tensorflow.python.framework.errors_impl.OutOfRangeError的解决方案
近日,在使用CascadeRCNN完成目标检测任务时,我在使用这个模型训练自己的数据集时出现了如下错误: tensorflow.python.framework.errors_impl.OutOfRangeError: PaddingFIFOQueue _1_get_batch/batch/padding_fifo_queue is closed and has in…...
springboot项目初始化执行sql
Sprint Boot应用可以在启动的时候自动执行项目根路径下的SQL脚本文件。我们需要先将sql脚本写好,并将这些静态资源都放置在src/main/resources文件夹下。 再配置application.yml: spring.datasource.initialization-mode 必须配置初始化模式initializa…...
Kubernetes之存储管理(中)
NFS网络存储 emptyDir和hostPath存储,都仅仅是把数据存储在pod所在的节点上,并没有同步到其他节点,如果pod出现问题,通过deployment会产生一个新的pod,如果新的pod不在之前的节点,则会出现问题,…...
MySQL workbench的基本操作
1. 创建新的连接 hostname主机名输入“local host”和“127.0.0.1”效果是一样的,指的是本地的服务器。 需要注意的是,此处的密码在安装软件的时候已经设定。 点击【Test Connection】,测试连接是否成功。 创建完的连接可以通过,…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
mac:大模型系列测试
0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何,是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试,是可以跑通文章里面的代码。训练速度也是很快的。 注意…...
