外设、CPU 之间的第三者:内存
冯诺依曼体系结构:外设只与内存交互,CPU 只与内存交互。内存有什么作用?内存具有数据存储的能力,可以预装数据。在计算机中,每一部外设与 cpu 都遵守冯诺依曼体系结构。
问:从外设到 CPU,还要经过内存,岂不是更加慢了?
答:不是的,CPU要访问 1 条数据,外设直接给内存装载 100 条数据,这样就只有装载的时候那 1 次慢,其余的 99 次都是快。由于三者之间是独立的,在 CPU拿数据的同时,外设可以往内存中装载数据,互不干扰。
结论 1:站在硬件的角度和数据层面上,CPU 和内存打交道,外设和内存打交道;
结论 2:数据需要处理,必须预装到内存中,通过操作系统完成;
结论 3:程序要运行前,必须加载到内存上,为什么?因为可执行文件是在外设当中的,外设只与内存打交道;
结论 4:寄存器不仅是 CPU 有,其他外设和内存都有,数据交互都与寄存器相关;
结论 5:各种硬件单元间链接用的都是总线(IO总线、系统总线);
结论 6:外设在加载到内存前,会发出电脉冲(控制信号)给 CPU,随后 CPU 告诉内存提取提取数据。
操作系统的上下关系:
操作系统是一款管理软硬件资源的软件,有进程管理、驱动管理、内存管理、文件系统。目的是为了给用户提供一个良好的执行环境。
OS 与用户之间沟通:
因为操作系统要保障自身安全,用户不能直接访问 OS ,所以封装了许多系统调用接口,而每个接口都用库(libc)封装起来。
OS 与外设之间沟通:
当操作系统需要拿数据时,会向驱动发送信号,而外设通过驱动转换返回给操作系统,期间操作系统和外设没有直接交往,转换数据一般通过寄存器转换。
因为自己完成外设的成本太高(因为外设太多且升级改款),所以单独创建出驱动层,让操作系统和硬件进行解耦。大多外设厂家自己会有驱动,连接电脑时会预装到内存,断开后退出。
操作系统如何管理进程:
什么叫进程?一个可执行程序从外设中,加载的内存上,就成为了进程,简称跑起来了。
操作系统如何管理内存呢?当一个可执行程序加载到内存上时,会创建相对应的 进程控制块:PCB(在 Linux 中叫做 task_struct ),操作系统把每一个进程所对应的 task_struct 用链表的方式连接起来,所以说,操作系统对进程的管理,就是对链表的增删查改。
也可以说操作系统对进程的管理是:先描述,在组织!!!操作系统先拿到数据,在描述结构体属性信息,构建 PCB ,进而对 PCB 进行管理。
查看进程代码:
ps aux | head -1 && grep -v grep | grep myproc
// 显示更多 ajx
所以,创建进程就是创建 PCB,插入到链表中,删除进程就是移除 PCB,从链表移开( PCB 会有一个 head 作为头节点)。
进程控制块:PCB 、task_struck
PCB 中包含了哪些属性信息呢:
{ 1:pid、ppid 唯一标识符
2:nice 优先级
3:mm_struct 进程空间地址,找到代码和数据的结构体指针
4:时间片
5:上下文信息
6:连接信息
。。。 }
pid:
首先是 pid :pid 是表示该进程的唯一标识符,而 ppid 是该进程的父进程的唯一标识符。
#include <unistd.h>
#include <sys/types.h>
// 获取进程ID
getpid();
getppid();
如果要杀掉进程,可以输入:kill -9 id 终止进程,每个程序最终的父进程都是 -bash ,它是登录时创建的。
状态:
进程有多种状态:S 代表休眠 、R 代表运行等等,没有 + 号说明进程在后台。
// 让进程变成后台进程
./myproc &
问:为什么有时候有的死循环是 S+ 休眠状态?
答:OS 要刷新到显示器上,因为外设的速度太慢(显示器有行刷新),OS 的速度太快,虽然显示器在刷新,感觉在运行中,但其实大部分时间 OS 都是在休眠,以免消耗资源。
上下文指令:
指令寄存器保存的是下一行的指令地址,每一次 CPU 执行下一行命令都会通过 eip 拿,不管是遇到循环指令或者进程临时切换, eip 都会记着下一条指令,而 CPU 做的 3 件事:取指令->分析指令->执行指令,CPU向 eip 取指令再执行,同时 eip 记录下一条指令。
1、每个进程都有自己的 “时间片” ,都是基于时间片轮转的。当一个进程正在执行时,时间片结束,如果某行代码数据的计算还在 CPU 中,没来得及返回,那么它会怎么办?
当进程切换时,寄存器的数据会保存到 task_struck 中(不太准确)。
2 、CPU 内部只有一套寄存器,内存会产生一份临时拷贝到 CPU 的寄存器中,而当前保存的中间数据,就叫做上下文数据。
3、进程被切换,可能在任何时间点,原因是时间片到了,或者有更高优先级的进程抢占。
4、对进程的管理就是对链表的增删查改(PCB)。
优先级:
进程之间各有优先级,如何区分谁的更高谁的更低,进程会有一个默认 80 的 PRI:它所进程可悲执行的优先级,越小越早执行。而为了修正 PRI ,可以通过修改优先级的修正系数 nice 值, nice 值有 40 个级别,区间是 [ -20, 19 ]。
如何修改优先级:
输入指令 top -> r -> 进程 PID -> nice 值 -> q退出,ps -la 可查看运行进程的相关信息。注意的是,每次修改都会以老的 PRI 计算,如果你是 80 ,那就每次对 80 进行修正。
环境变量:
为什么输入命令时,可以直接执行命令,列如:执行 ls 命令,其实 ls 存在目录 /bin/ls 下,为何不用输入地址执行?就是因为 ls 是一种命令行参数。
echo $PATH :显示环境变量路径
如果想让可执行文件直接不带 ./ 就可以执行,就 sudo cp -f exe文件 /usr/bin/ ,但是不建议这样做,因为会污染环境池,就像电脑里不注释的文件夹,久了都不清楚是什么文件,不敢删除。
所以建议用另一种方法:将自己路径导到 PATH 中,不过每次重启都会重新刷新不见。
输入命令 :
export PATH=$PATH:/home/username/所在地址
家目录显示:
$HOME
显示当前所支持的环境变量信息:
env
显示所有变量,包括环境变量:
set // 本地变量 VAL=100 不在环境变量中,需要 export
unset
其实,main 函数是可以带参的,完整的是:
int main(int argc, char* argv[], char* envp[])
//命令行参数个数 //命令行列表 //环境变量
环境变量的组织方式是一个 char 类型的指针数组,以 NULL 结尾,argc、argv[] 是 C 语言设计的,是命令行参数,与系统无关,envp[] 才是系统决定的,它们都是环境变量。
环境变量是一个系统级别的全局变量,bash 之下都可以获取,会给 main 传参( , , envp),只传环境变量,不传本地变量。
查看命令行参数以及环境变量的 3 种方法:
// 3/
//printf("%s\n ",getenv("MYVAL"));
// 2/
//int i = 0;
//extern char** environ;
//while (environ[i])
//{
// printf("environ[%d] : %s\n", i, environ[i]);
// i++;
//}
// 1/
//int i = 0;
//while (envp[i]){
// printf("envp[%d] : %s\n", i, envp[i]);
// i++;
//}
// 命令行参数
//if (argc == 2)
//{
// if (strcmp(argv[1], "-a") == 0){
// printf("-a -a\n");
// }
// else if (strcmp(argv[1], "-b") == 0){
// printf("-b -b\n");
// }
// else{
// printf("is else\n");
// }
//}
//else{
// printf("no no\n");
//}
为什么要有环境变量?为了更好的定位,在哪,是谁等等。