输入设备,输出设备,存储器(内存设备),中央处理器(CPU)。 四大件。
这里的存储器是?内存。内存掉电易失
磁盘,外设,永久性存储能力。
外设:输入设备
外设:输出设备
磁盘和网卡 既是输入设备也是输出设备
为什么是外设?相对于:内存+CPU
运算器+控制器+其它=CPU存储器 内存外设
很快快 慢
计算的 临时存储的 永久性存储
CPU其实很笨,只能被动接受别人的指令,别人的数据-->执行别人的指令,计算别人数据的目的。
CPU必须先,认识别人的指令? CPU有自己的指令集。 我们写代码,编译本质-->二进制可执行程序。实际上就是翻译成CPU的指令集。
所以编译器存在的意义,翻译我们的程序到机器指令。
CPU在读取和写入的时候,在数据层面,只和内存打交道。为了提高整机效率。
CPU里面的数据,不是天然就存在的。从外设拿数据那就太慢了。所以综合考虑就从内存拿数据。
CPU读取和写入只和内存打交道,提高整机效率。
内存里面不是天然就有数据的
例如:开机的时候加载 操作系统加载到内存。
内存可以看做一个大大的缓存,匹配CPU和外设的速度。
OS操作系统---》帮我们做决策。
操作系统管理 IO过程。
内存I/O 读写就是IO的过程。外设
总结
CPU不和外设直接打交道,只是和内存直接打交道。
所以的外设,有数据需要载入,只能载入到内存中,内存写出,也一定是写到外设中。
总结: ----》》CPU不和外设直接打交道,只和内存直接打交道。
程序要运行必须加载到内存?
为什么要加载到内存?
CPU要执行我的代码,访问我的数据。
只能从内存中读取。(体系结构规定的){为什么这样子规定,效率的问题}
进度条:数据写到内存里面---->没有及时刷新到外设。
两个朋友发消息,冯诺依曼体系结构
| 计算器1 | 计算器2 |
|---|---|
| 键盘输入 | 网卡输入 |
| 输入到内存 | 输入到内存 |
| CPU读取 | 输入到CPU |
| 网卡和显示器输出 | 输入到显示器 |
发文件也是类似的理解
OS是一个进行软硬件的资源管理的软件。
它属于软件,但职责是直接控制硬件、向上屏蔽硬件细节。
| 用户 |
|---|
| 用户接口 |
| 系统接口 |
| 进程管理,文件系统管理 ,内存管理,驱动管理。(os) |
| 驱动 |
| 硬件 |
OS为什么要进行管理呢?
通过合理的管理软硬件资源,为用户提供良好的执行环境。良好:稳定,安全,高效的。
是什么?:OS是一个进行软硬件的资源管理的软件
为什么?:通过合理的管理软硬件资源,为用户提供良好的执行环境。良好:稳定,安全,高效的
怎么办?:管理
举个例子:理解管理者
校长:管理者,决策权利
辅导员:驱动齿,执行权利
我们:被管理者
管理:管理不需要面对面,只需要拿到被管理者的数据。对重大事情有决策的权力。
做决策需要依据的,依据就是根据数据。管理的本质就是对数据做管理。
管理的本质是对:被管理者的数据做管理。
管理者,对重大事有决策的能力。决策的依据就是数据。
管理者: 做决策 校长 OS(操作系统) 软件执行者: 做执行 1.执行上面的命令 2.和被管理者接触,拿到相应的数据。 辅导员 驱动 (磁盘驱动,显示器驱动) 软件被管理者:行动产生数据 学生 硬件 (磁盘,网卡, 显卡,键盘,显示器) 硬件
被管理者的数据太多了,怎么办?
学生的信息种类都是一样的。
对学生做管理,变成对链表数据做管理。
先描述:抽象数据。 c结构体,c++类
后组织:数据结构。数据结构组织起来
OS管理硬件:
struct dev {struct dev* next, int type, int status}
管理的本质:对数据做管理 管理的方法:先描述(语言),在组织 数据结构
OS管理硬件:一模一样的
软件管理软件,软件管理硬件。
OS需要保护自己的同时,还要为用户提供服务。
shell指令,c/c++ lib 编程操作。
OS不相信任何人
1.保护自己, 不让任何人直接进入操作系统
2.对外提供服务,系统调用接口
| 计算机的软硬件体系结构 |
|---|
| 用户 |
| 系统调用接口(系统调用) (本质上就是c式的接口) (man syscalls) (strace ls)实际上就是函数接口 |
| (shell(指令操作)/lib(c/c++)(编程操作)/界面) |
| 操作系统(进程控制,文件系统,内存管理,驱动管理) |
| 驱动 |
| 硬件 |
计算机软硬件体系结构
我们学的c/c++: 学习用户接口和系统调用的接口。
1.冯诺依曼体系
2.操作系统本身理解
3.计算机体系结构
进程 == 内核数据结构 + 进程对应的磁盘代码
那么多的进程需要被管理起来,如何管理呢? 进程控制管。
一个运行起来(加载到内存)的程序---进程-- 一般概念
在内存中的程序的-----------------进程
写好的程序本质就是文件,在磁盘上放着。
那么多的程序,操作系统如何管理呢?
先描述:PCB。进程控制块。Process control block
后组织:
structtask_struct{pid_tpid; // 进程号longstate; // 进程状态intprio; // 优先级structmm_struct*mm; // 地址空间信息structfiles_struct*files; // 打开的文件信息structsignal_struct*signal; // 信号处理相关structtask_struct*parent; // 父进程structlist_headchildren; // 子进程链表structlist_headtasks; // 系统全局进程链表节点structthread_structthread; // CPU 寄存器等上下文信息structsched_entityse; // 调度实体信息};
所谓对进程进行管,变成了对进程对应的PCB进行相关的管理。
对进程的管理-->对链表的增删查改
struct task_struct 内核结构体 --> 内核结构体的对象 ---> 将该结构体和你的代码和数据关联起来----> 先描述,后组织。
进程 == 内核数据结构 + 进程对应的磁盘代码
为什么会有PCB(struct task_struct)的结构体呢?
进制在调度运行的时候,就具有动态属性。 创建一个进程,就要创建一个struct task_struct{}。
ps -axj | head -1 && ps ajx | grep a.out
getpid()
linux进程也是文件的。
/proc/id: 进程目录文件 (了解的)
进程的程序加载到内存
getpid()
getppid():终端运行的进程,一般都是当前bash分配一个子进程。
子进程出问题,不会影响父进程的。进程的独立性。 抖音退出了,不会影响微信的正常运行的。
fork():创建子进程
fork(): 子进程返回0,父进程返回子进程id,失败返回-1
fork之后代码共享,if分流
两个死循环和判断同时执行,证明fork之后是两个执行流。
通过返回值不同,让父子进程执行不同的代码。
#include <stdio.h>#include <unistd.h>#include <sys/types.h>#include <assert.h>intmain(){pid_tid=fork();assert(id>=0);if(id==0) {while(1) {printf("I AM PORCESS, ppid : %d, pid : %d id : %d \n", getppid(), getpid(), id);sleep(2); } }else {while(1) {printf("I AM PORCESS, ppid : %d, pid : %d id : %d \n", getppid(), getpid(), id);sleep(2); } }return0;}
进程id和 fork
#include<stdio.h>#include<unistd.h>#include<sys/types.h>intmain(){#if 1printf("当前bash的id: %d\n", getppid());printf("当前bash的子进程的id:%d\n", getpid());#else// fork之后代码共享// 同一个变量,在不被修改的情况下,竟然有不同的内容inta=1000;// 前面的代码,只有父进程pid_tid=fork();if(id==-1) {perror("fork");return1; }// printf("我是一个进程 我的id:%d, 我的父进程的id: %d , %d \n", getpid(), getppid(),id);// 后面父子进程都有,只要有共享代码修改,就会发生写实拷贝while(1) {if(id==0) { a=0;printf("我是子进程 我的pid:%d, a我的id: %d, a : %d, a的地址 : %p\n", getppid(), getpid(), a, &a);sleep(2); }elseif(id>0) {a=1;printf("我是父进程 我的pid:%d, 我的id: %d, a : %d, a的地址 :%p\n", getppid(), getpid(), a, &a);sleep(2); } }printf("zzz\n");// 父进度的pid就是当前终端// echo $$ 当前终端的PID// 通过fork的返回值进行分流#endifreturn0;}
fork之后代码共享
谁先被调度,不知道的。
父进程返回 子进程pid
子进程返回 0
父进程需要知道孩子是谁,所以返回孩子的 pid。
知道孩子的pid,方便后面的 回收工作的。
子进程拿到的是“我就是孩子”
冯诺依曼体系:CPU只和内存打交道。外设也是只和内存打交道。缓存。
计算机是软件和硬件高度配合的
读 输入-----内存----CUP写 CPU------内存----输出
CPU只能被动接收数据,指令。
CPU已经内置了指令。
二进制可执行程序---CUP认识的指令。(c++代码的编译链接信息。)
控制CPU执行的
管理的本质:对数据进行管理。
OS:是一个软硬件资源管理的软件。
OS对下软硬件资源
OS对上为用户提供良好的执行环境。
管理的本质就是:先描述,后组织。
管理者和被管理者不一定要接触。
管理的本质就是管理数据。
执行者:1被管理者的数据交给管理者 2执行指令。
管理的本质:先描述,后组织
先描述数据:结构体,类和对象。抽象出来
后组织数据:数据结构。链接起来的
OS维护一个数据结构-----维护对应的对象。 c struct , cpp class
OS不相信任何人。
shell, c/c++ lib ,界面, 指令集合
进程---操作系统里面的数据只能通过系统接口访问
什么是进程?
程序就是一段二进制代码,就是一个文件
描述进程的PCB
管理每一个PCB,用数据结构组织起来。
程序加载带内存,操作系统瞬间创建一个PCB
PCB里面有进程的所有信息。 描述进程的全部信息。
进程 == 内核数据结构(PCB) + 进程对应的磁盘代码
PCB
fork创建子进程
进程状态
进程状态1.运行2.新建3.就绪4.挂起5.阻塞6.等待7.停止8.挂机9.死亡
Linux 内核中主要状态定义在 task_struct->state 中。
都是操作系统的说法,太抽象了,不是具体的一款操作系统。
运行队列:放要执行的进程
进程的状态:进程内部的属性,放在PCB里面。不同的场景,不同的状态,等待不同的资源。
阻塞状态:PCB放到不同的队列里面。等待着
挂起:内核数据结构还在,code和data还在磁盘的
挂起状态信息
运行队列(run queue)
1.一个CPU一个运行队列。
2.进程如队列,该进程的PCB放入队列中。
3.只要是在运行队列里面就是 R状态。 不是一定要真的被执行。
等待队列(wait queue)
Linux 里通常不是“全局一个阻塞队列”,
而是“谁让你阻塞,你就挂到谁的等待队列上”。
阻塞进程通常进入的是 各种 wait queue(等待队列),而不是一个统一的“阻塞队列”。
CPU很快,外设很慢。
不同的队列,task_struct拿来拿去的。
进程不同的状态,就是进程等待不同的资源,等待 某种资源就绪。
Linux内核源代码:进程状态
R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里面。
kill -19 暂停状态
kill -18 恢复暂停
kill -9 杀死进程
前台进程,后台进程。 带不带+
深度睡眠:D。disk sleep 磁盘睡眠
dd指令
复习
运行,阻塞,挂起。不同的状态,等待不同的资源。
运行队列 PCB在等待队列里面。
挂起你不一定看得到的
小t可以是暂停,就是在调试。
trace stop
D状态:一般是高IO
僵尸状态:
进程被创建处理---完成任务---1.要知道它完成的怎么样 2.可以不关心结果。
Linux你可以不用,但是我必须保存的。
进程退出的时候,不能立即释放进程对应的资源,保存一段时间,让父进程或者OS来进行读取。资源PCB
进程终止 exit()函数
僵尸状态是一个问题暂时我们不解决的! 进程控制的时候解决。 不回收僵尸状态--可能会造成内存泄漏
wait或者waitpid函数进行回收
僵尸状态是指:一个进程已经执行完毕并退出,但其在进程表中的条目仍然存在,等待父进程读取其退出状态信息的状态。
孤儿进程
父进程被杀掉,父进程的父进程回收资源了的。
父进程先退出, 然后子进程被操作系统1号进程领养了。
1.孤儿进程一定存在
精灵进程,守护进程。
#include<stdio.h>#include<unistd.h>intmain(){// 运行状态// 计算密集型的inta=10;while(1) {a=1+1; }return0;}
linux下的阻塞状态。
#include<stdio.h>#include<unistd.h>intmain(){// IO密集型的while(1) {printf("hell sdf\n");sleep(1); }return0;}
kill -19 pid
kill -18 pid
恢复变成后台进程了的。
后台进程,只能kill -9 杀了的。
调试的时候就是
dead死亡状态。
一般都是在高IO场景的,无法被终止的。
僵尸状态是一个问题的。
进程退出都需要被回收的,linux设计哲学的。
子进程退出,等待父进程回收它的资源。
#include<stdio.h>#include <stdlib.h>#include<unistd.h>#include<sys/types.h>intmain(){pid_tid=fork();if(id<0) {perror("fork failed");return-1; }elseif (id==0) {while(1) {printf("我是子进程 , 我的ppid:%d, 我的pid:%d \n", getppid(), getpid());sleep(2);exit(1); } }else {while(1) {printf("我是父进程 , 我的ppid:%d, 我的pid:%d\n", getppid(), getpid());sleep(2); } }return0;}
任何系统调用,都要先查“成功返回什么,失败返回什么,失败后 errno 表示什么”。
系统调用的健壮性 = 正确判断返回值 + 理解返回值语义 + 处理 errno + 做好资源清理。
父进程先退出了的
守护进程
了解范畴
了解范畴
了解范畴
ps -l
ps -al
1.什么叫做优先级
优先级是有执行的权利,只不过是谁先完成后完成的过程。权限是谁可以做什么的问题
2.为什么会存在优先级
为什么存在优先级?因为资源太少了。
3.Linux的优先级
优先级本质就是PCB里面的整数。
linux支持进程优先级调整的
数字越小,优先级越高。
top r 输入pid 【-20, 19】
每次都是默认的:80 +- ni
竞争性:资源太少了,想运行就得竞争。
独立性:QQ退出了,不会影响微信的野指针模拟实现
并行:多个CPU执行,多个 进程。 一个计算机 运行多个进程。
并发:多个进程轮流到CPU执行。 看起来,大家都在执行。时间片轮转的策略。进程不断切换。 不要用你的速度衡量CPU的速度。
CPU任何一个时刻只能执行一个进程。
pc(eip): 当前正在执行指令的下一条指令。
CPU:1取指令,2分析指令,3执行指令
当我们的进程在运行的时候,一定会产生很多的临时数据。这份数据属于当前进程!!
CPU虽然只有一套寄存器硬件,寄存器里面保存的数据,是属于当前进程的。
寄存器硬件 != 寄存器内的数据。
进程在运行的时候占CPU,进程不是占有到进程结束!!(while(1)) 死循环,那么就不能执行其它指令 了。
进程在运行的时候都有自己的时间片,(离开学校,保留学籍的过程,上下文保护。)(回到学校,恢复学籍,上下文恢复。)
保留就是为了恢复。
寄存器 (寄存,寄存,寄存进程的临时数据。)。
上下文:寄存器里面的数据
进程切换的时候, 要进程上下文保护。
当进程恢复的时候,要进程上下文恢复。
上下文数据:进程的上下文主要保存在该进程的“进程控制块”的数据结构中,而PCB通常存在于操作系统内核空间的内存里。
切换对线程的同步和互斥有助于理解的。
寄存器 被所有进程共享
难学的东西是知识,简单的东西是信息。
进程切换的本质,是 CPU 从一个进程的“执行上下文”切换到另一个进程;因此,凡是与“继续正确执行”相关的状态,都必须被保存和恢复。
| 类别 | 保存 / 恢复内容 | 说明 |
|---|---|---|
| CPU 上下文 | PC、通用寄存器、SP、FLAGS | 核心 |
| 内核栈 | 栈指针、栈内容 | 每进程独立 |
| 地址空间 | 页表基址(CR3) | 切换虚拟内存 |
| PCB | 状态、优先级、时间片 | 调度依据 |
| 信号 | pending / mask | 语义一致 |
| FPU / SIMD | 浮点与向量寄存器 | 延迟保存 |
| TLB | 刷新或标记 | 架构相关 |
复习
僵尸进程是一个进程的状态
孤儿进程,1号进程领养的。
进程优先级,进程调度的先后顺序。基础顺序80 ,调整顺序 -20,19。综合60-99.
进程切换,上下文保存,上下文恢复。寄存器:给 CPU 提供最快速的数据暂存和操作位置。 CPU 芯片内部用于保存二进制数据的高速存储单元。
寄存器是 CPU 内部的高速临时存储单元,专门用来保存当前指令执行时最直接、最关键的数据、地址和状态。
上行文保存在:PCB里面的。
不同的环境变量解决不同的问题的。
env
printenv
在 Linux 中,环境变量(environment variables) 是操作系统中用于存储系统运行环境配置信息的一种 键值对(key-value pair),其主要作用是:用来影响进程的行为,例如查找命令路径、设定语言、用户目录、库路径等。
Linux的指令也是可执行程序。
切入点
要执行一个基本指令(程序),先找到这个程序。
echo $PATH
echo $HOME
添加环境变量
export PATH=$PATH:/home/lic/lesson11/
which本质就是根据环境变量PATH查找数据的。
Linux的环境变量配置:
启动一个shell 就会执行一次配置文件的。
/etc/bashrc
用户
系统配置
/etc/bashrc
echo $HOME
复习
进程优先级一般不需要调整,除非你特别了解。或者你真的需要调整。
进程切换 ,CPU轮转的临时数据,在寄存器 里面的数据(进程的上下文),上下文保护,上下文切换。
CPU:取指令,分析指令,执行指令。
进程切换
时间片轮转。执行进程。
并发:每个进程在时间片跑跑跑。
时间片单位,
CPU---PCB---Code&Data--address--reall address
例子:学籍保留
su -重新登录
su 切换用户
不同的环境变量,使用不同的场景。
user:环境变量的意义,可以表示当前那个用户在使用Linux
// #include <stdlib.h>// char *getenv(const char *name);#include <stdio.h>#include <stdlib.h>intmain(){printf("%s \n", getenv("HOME"));printf("%s \n", getenv("USER"));printf("%s \n", getenv("PWD"));printf("%s \n", getenv("PATH"));return0;}
#include <stdio.h>#include <string.h>#include <stdlib.h>#define USER "USER"intmain(){char*user=getenv(USER);if(strcmp(user, "root") ==0) {printf("user:%s\n", user); }else {printf("权限不足\n"); }return0;}
#include <stdio.h>#include <string.h>#include <stdlib.h>#define USER "USER"#define MY_VAL "myval"int main(){ char* myenv = getenv(MY_VAL); if(NULL == myenv) { printf("%s, not found\n", MY_VAL); return 1; } printf("%s:%s\n", MY_VAL, myenv); return 0;}
环境变量具有全局属性,会被子进程继承下去,也方便子进程满足不同的场景。
unset, export。
ls指令,子进程继承,知道PWD, 然后就知道了。
#include <stdio.h>#include <string.h>#include <stdlib.h>#define USER "USER"#define MY_VAL "myval"#define MYPWD "PWD"int main(){ printf("%s\n", getenv(MYPWD)); return 0;}
本地环境变量和全局环境变量
aaa=123
export aaa 导入全局环境变量
当前bash的全局环境变量,会被子进程继承下去的。
本地变量-- 只会在当前bash内有效的
aaa=123 定义局部环境
export aaa 设置全局环境变量
unset aaa 取消全局环境变量
export MYAVL=2222
ls 继承了PWD,所以不断切换目录,ls知道你当前在那个目录下面。
环境变量是如何被子进程继承??
main函数参数和extern enviro
命令行参数
argc个数
argv内容
#include <stdio.h>int main(int agrc, char* argv[]){ for(int i = 0; i < agrc; i++) { printf("argv[%d] : %s\n", i, argv[i]); } return 0;}
命令行选项
#include <stdio.h>#include <string.h>// ./mycmd -a -b -c// ./mycmd -ab -bc -ac// ./mycmd -abcint main(int agrc, char* argv[]){ if(agrc != 2) { printf("Usage: \n%s [-a/-b/-c/-ab/-ac/-bc/-abc]\n", argv[0]); return 1; } if(strcmp("-a", argv[1]) == 0) { printf("功能a\n"); } if(strcmp("-b", argv[1]) == 0) { printf("功能b\n"); } if(strcmp("-c", argv[1]) == 0) { printf("功能c\n"); } if(strcmp("-ab", argv[1]) == 0) { printf("功能ab\n"); } if(strcmp("-ac", argv[1]) == 0) { printf("功能ac\n"); } if(strcmp("-bc", argv[1]) == 0) { printf("功能bc\n"); } if(strcmp("-abc", argv[1]) == 0) { printf("功能abc\n"); } return 0;}
环境变量表
#include <stdio.h>#include <string.h>int main(int agrc, char* argv[], char* env[]){ for(int i = 0; env[i]; i++) { printf("%d : %s\n", i, env[i]); } return 0;}
extern char星星 environ
#include <stdio.h>#include <string.h>#include <unistd.h>extern char** environ;int main(){ for(int i = 0; environ[i]; i++) { printf("%d : %s \n", i, environ[i]); } return 0}
getenv()
#include <stdio.h>#include <stdlib.h>int main(){ printf("%s \n", getenv("HOME")); printf("%s \n", getenv("USER")); printf("%s \n", getenv("PWD")); printf("%s \n", getenv("PATH")); return 0;}
putenv()
#include <stdlib.h>int putenv(char *string);
putenv不重要
#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>
int stat(const char *pathname, struct stat *statbuf);
环境变量
命令行参数
命令行参数表,环境变量表。
main函数的两张表
内建指令
虚拟地址空间。见见猪跑的
见见猪跑
#include <stdio.h>#include <string.h>#include <unistd.h>extern char** environ;int g_val = 100;int main(){ pid_t id = fork(); if(id < 0) { perror("fork failed"); return 1; } else if(id == 0) { g_val = 1; while(1) { printf("我是子进程、 g_val:%d, &g_val : %p \n", g_val, &g_val); sleep(1); } } else { g_val = 2; while(1) { printf("我是父进程、 g_val:%d, &g_val : %p \n", g_val, &g_val); sleep(1); } } return 0;}
多进程读取同一个地址,怎么可能出现不同的结果? 进程的独立性,同一个地址,不同的数据。
地址没变----》这里的地址,绝对不是物理地址---》曾经我们学习语言级别的地址,绝对不是对应的物理地址。---》虚拟地址(线性地址) c/c++打印出来的全部都是虚拟地址。
进程他会认为自己独占系统资源(实际上不是)。这是设计上的理念。
三son
复习
环境变量,全局属性,方便不同的场景使用
命令行参数,根据选项执行不同的功能。
mian函数表,环境变量表。推荐getenv()
echo 怎么能够打出局部变量
进程地址空间,是虚拟地址空间。
进程独立性,打印一个地址,然而是不同的数据。
进程地址空间,就是一种数据结构。每一个地址空间管理起来。mm_struct
复习
区域划分就是进程地址空间。
mm_struct{} 成员有哪些呢?
1.地址空间的描述的基本空间大小是字节 2.32位---2^32个地址 3.2^32 * 1字节 = 4GB的空间范围 4.每一个字节唯一的地址
2^32个地址保证唯一性就行了。
32位的数据即可
43 默认就是2^32次方个地址。
unsigned int(32bits)
struct mm_struct {uint32_t code_start;uint32_t code_end; uint32_t data_start; uint32_t data_end;uint32_t heap_start;uint32_t heap_end; uint32_t stack_start; uint32_t stack_end; };
[start, end]---[起始地址,区域结束地址]
举个栗子
[start, end]---[起始地址,区域结束地址]
struct mm_struct* p = (struct mm_struct*)malloc(sizeof(struct mm_struct));
66 -----堆栈可以调整------(本质就是修改end或者start) 67 定义局部变量,malloc ,new---->扩大堆栈区域 68 函数调用完毕,freee, delete-->缩小堆栈区域
程序加载到内存里面
OS创建PCB--->里面有程序的信息。
内存IO一般是4KB为单位的。Page页
线性地址:数组就是线性地址。
在进一步
如何理解地址空间呢?
1.如果让进程直接访问物理内存,万一进程越界非法操作呢?非常不安全呢!(页表不仅仅只做映射,还会检查映射是否安全呢) 安全!所以进程都必须要遵守的。 安全的
2.地址空间的存在可以更方便,进程和进程的数据代码的解耦,保证了进程的独立性的特征。 写时拷贝。地址空间通过页表映射。
3.让进程以同一的视角,来看待进程对应的代码和数据等各个区域,方便编译器也以统一的视角进行编译代码。规则一样的,编译完即可直接使用的。
编译代码的时候,使用不用的地址形式。
父子进程这么亲密都无法,相互干扰,何况其它进程呢?
链接库的时候,就是把他们的地址,编译到我的程序 里面去。
1.你的可执行程序,编译链接完成已经就存在地址了,逻辑地址的。
2.虚拟地址空间,不知OS会遵守对应的规则,编译器要遵守!!
编译器就是按照虚拟地址空间进行编译的。
3.程序加载到内存里面,天然就有物理地址了。
虚拟地址---就是你程序编译好的逻辑地址。
CPU读取的指令,存在地址。
两种地址
file 指令
fork函数的问题
#include <stdio.h>int main(int argc, char* argv[], char* env[]){ for(int i = 0; i < argc; ++i) { printf("%d:%s\n", i, argv[i]); } printf("-----------------\n"); for(int i = 0; env[i]; ++i) { printf("%d:%s\n", i, env[i]); } return 0;;}
#include <stdio.h>#include <unistd.h>#include <sys/types.h>int main(){ pid_t id = fork(); if(id == -1) { perror("fork"); return 1; } if(id == 0) { while(1) { printf("子进程: %d,%d\n", getppid(), getpid()); sleep(2); } } else { while(1) { printf("父进程: %d, %d\n", getppid(), getpid()); sleep(2); } } return 0;}
#include <stdlib.h>#include <stdio.h>int main(){ printf("%s\n", getenv("USER")); printf("%s\n", getenv("HOME")); printf("%s\n", getenv("PATH")); printf("%s\n", getenv("LOGNAME")); printf("%s\n", getenv("PWD")); return 0;}