一.冯诺依曼结构
- (1)输入设备:键盘,鼠标,扫描仪,写字板
- (2)输出设备:显示器,打印机
- (3)存储器:存储器是用来保存程序和数据,以及运算的中间结果和最后结果的记忆装置。计算机的存储系统分为内部存储器(简称内存或主存储器)和外部存储器(简称外存或辅助存储器)。主存储器中存放将要执行的指令和运算数据,容量较小,但存取速度快。外存容量大、成本低、存取速度慢,用于存放需要长期保存的程序和数据。当存放在外存中的程序和数据需要处理时,必须先将它们读到内存中,才能进行处理。
*(4)运算器:计算机中进行算术运算和逻辑运算的主要部件,是计算机的主体。在控制器的控制下,运算器接收待运算的数据,完成程序指令指定的基于二进制数的算术运算或逻辑运算。 - (5)控制器:计算机的指挥控制中心。控制器从存储器中逐条取出指令、分析指令,然后根据指令要求完成相应操作,产生一系列控制命令,使计算机各部分自动、连续并协调动作,成为一个有机的整体,实现程序的输入、数据的输入以及运算并输出结果。
从你登录上qq开始和某位朋友聊天开始,数据的流动过程?
先从键盘上输入数据,数据进入内存,在中央处理器得作用下,将内存中得数据放入网卡;数据从网卡里受到数据,到内存,然后显示到显示器上。
总结:这里的存储器指的是内存 不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备) 外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。 一句话,所有设备都只能直接和内存打交道
二.操作系统
操作系统是一款进行软硬件管理的软件,为用户程序提供一个良好的执行环境。
1.操作系统管理:先描述,再组织。对下要把各种资源管理好,对上为用户提供一个良好的执行环境。
(1)描述:用struct结构体
(2)组织:用链表或者其他高效的数据结构
2.系统调用和库函数概念
- (1)在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分 由操作系统提供的接口,叫做系统调用。
- (2)系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统 调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发
三.进程
1.进程的概念:
进程是运行时程序的封装,是系统进行资源调用和分配时的基本单位,实现了操作系统的并发。
2.描述进行的结构体—PCB
(1)pcb的由来:进行信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
(2)task_struct结构体的分类
-
标示符: 描述本进程的唯一标示符,用来区别其他进程。
-
状态: 任务状态,退出代码,退出信号等。
-
优先级: 相对于其他进程的优先级。
-
程序计数器: 程序中即将被执行的下一条指令的地址。
-
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
-
上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
-
I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
-
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
-
其他信息
四.fork()标识符
1.pid(),ppid()
ppid很多时候都是bash(终端)。
1 #include<stdio.h>
2 #include<unistd.h>
3 int main()
4 {
5 while(1)
6 {
7 printf("I am a process : pid : %d : ppid : %d\n",getpid(),getppid());
8 sleep(1);
9 }
10 return 0;
11 }
2.fork()
fork之后是创建了子进程,父子进程之间代码共享,数据各自私有一份。
fork之后打印出两份代码,所以代码共享。进程运行时是具有独立性的。
- fork()函数有两个返回值。
- (1)给父进程返回子进程的pid;
- (2)给子进程返回0;
23 int main()
24 {
25 pid_t id = fork();
26 if(id < 0)
27 {
28 perror("fork");
29 return 1;
30 }
31 else if(id == 0)
32 {
33 printf("I am a child: pid :%d ppid : %d\n",getpid(),id);
34 }
35 else
36 {
37 printf("I am a child: pid :%d ppid : %d\n",getpid(),id);
38 }
39 return 0;
40 }
五.进程状态
- (1)R运行状态:并不意味着进行一定再运行中,它表明进程要么在运行中要么在运行队列里。
- (2)S睡眠状态:(可中断睡眠)意味着进程在等待事件完成
- (3)D磁盘休眠状态:(不可中断睡眠)在这个状态的进程会通常等待IO的结束。
- (4)T停止状态:可以通过发送SIGSTOP信号给进程用来停止进程。这个被暂停的进程可以同发送SIGCONT信号让进程继续运行。
- (5)x死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
1.僵尸进程
- (1)僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲) 没有读取到子进程退出的返回码时就会产生僵死(尸)进程
- (2)僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码
- (3)所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
僵尸进程的危害:
- (1)进程的退出状态必须维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了,可是父进程如果一直不读取,那么子进程就一直处于Z状态
- (2)维护退出状态本省就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话来说,Z状态一直不退出,PCB一直都要维护。
- (3)那一个父进程创建很多子进程,就是不回收,是不是就会造成内存资源的浪费?
是的!因为数据结构对象本身就要占用内存,如果定义一个结构体变量(对象),就要在内存的某个位置进行开辟空间。
2.孤儿进程
- (1)父进程如果提前退出,那么子进程后退出。
- (2)父进程先退出,子进程就称为“孤儿进程”。
- (3)孤儿进程被1号init进程领养,当然要init进程回收。
五.进程的优先级
- (1)cpu资源分配的先后顺序,就是指进程的优先权
- (2)优先权高的进程优先执行权力。配置进程的优先权对多任务环境下的Linux很有用,可以改善系统的性能。
- (3)还可以把进程运行到指定的cpu上,这样一来,把不重要的进程安排到某个cpu上,可以大大改善系统整体性能。
在Linux系统中敲ps -l就会出现如下的结果
UID: 代表执行者的身份
PID: 代表这个进程的代号
PPID: 代表这个进程是由哪个进程发展衍生来的,即父进程的代号
PRI: 代表这个进程可被执行的优先级,值越小越早被执行
NI: 代表这个进程的nice值
1.PRI and NI
- (1)PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小 进程的优先级别越高
- (2)那NI呢?就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值
- (3)PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice
- (4)这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行
- (5)所以,调整进程优先级,在Linux下,就是调整进程nice值
- (6)nice其取值范围是-20至19,一共40个级别。
2.PRI vs NI - (1)需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进 程的优先级变化。
- (2)可以理解nice值是进程优先级的修正修正数
- (3)PRI和NI之间的差值为80
3.其他概念
- (1)竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高 效完成任务,更合理竞争相关资源,便具有了优先级
- (2)独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
- (3)并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
- (4)并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为 并发
六.环境变量
1.概念:
- (1)环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数。
- (2)如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但 是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
- (3)环境变量是一个变量,程序关闭就会被释放,程序运行就会生成。
2.常见的环境变量 - PATH:指定命令的搜索路径
- HOME:指定用户的主工作目录(即用户登录到Linux系统中时,默认的目录)
- SHELL:当前的shell,它的值通常为/bin/bash
3.环境变量相关的命令
- 1.echo:显示某个环境变量值
- 2.export:设置一个新的环境变量
- 3.env:显示所有的环境变量
- unset:清除环境变量
- set:显示本地定义的shell变量和环境变量
//argc命令行参数的个数
//argv命令行参数
//env传入环境变量
51 int main(int argc,char* argv[],char * env[])
52 {
53 int i = 0;
54 for(; env[i] ; ++i)
55 {
56 printf("%d : %s\n",i,env[i]);
57 }
58 return 0;
59 }
七.程序地址空间
1.图
地址空间 :地址空间(address space)表示任何一个计算机实体所占用的内存大小。比如外设、文件、服务器或者一个网络计算机。地址空间包括物理空间以及虚拟空间。
代码1:
63 int g_val = 100;
64
65 int main()
66 {
67 pid_t id = fork();
68 if(id == 0)
69 {
70 sleep(1);
71 printf("child: %d, %p\n",g_val,&g_val);
72 }
73 else
74 {
75 sleep(2);
76 printf("Father: %d, %p\n",g_val , &g_val);
77 }
78 return 0;
79 }
//child: 100, 0x601044
//Father: 100, 0x601044
//我们发现输出的变量值和地址时一模一样的,
//因为子进程时按照父进程为模板,父子并没有对变量进行任何的修改。
代码2
63 int g_val = 100;
64
65 int main()
66 {
67 pid_t id = fork();
68 if(id == 0)
69 {
70 sleep(1);
71 g_val = 200;
72 printf("child: %d, %p\n",g_val,&g_val);
73 }
74 else
75 {
76 sleep(2);
77 printf("Father: %d, %p\n",g_val , &g_val);
78 }
79 return 0;
80 }
//child: 200, 0x601044
//Father: 100, 0x601044
父子进程,输出地址是一致的,但是变量内容是不一样的
-
(1)变量内容不一样,所有父子进程输出的变量绝对不是同一个变量
-
(2)但地址是一样的,说明,该地址绝对不是物理地址
-
(3)在Linux下,这种地址叫做虚拟地址
-
(4)我们在用c/c++语言所看到的地址,全部都是虚拟地址,物理地址,用户一概看不到,由os统一管理
2.进程地址空间
- (1)虚拟地址空间是一个结构体,叫做mm_struct。
- (2)页表是操作系统维护的虚拟地址到物理地址之间的映射关系,通常有两级页表,和多级页表。
- (3)MMU是一个硬件,内存管理单元。用软件来进行查表是可以的,但是速度效率太低;所以应该软硬件结合二,因为虚拟地址----》物理地址,太过频繁。
- (4)创建一个进程就要创建pcb,地址空间,还要为该进程维护页表,还要构建虚拟地址到物理地址之间的映射关系。
同一个变量,地址相同,其实就是虚拟地址相同,内容不同其实是被映射到了不同的物理地址。
面试题:
1.为什么要有程序地址空间?
- (1)让每一个进程保持独立性,独享系统资源。
- (2)可以让软件通过接口去访问系统资源。
2.程序和进程的区别: - (1)程序是硬盘上的一个文件。
- (1)进程是要在运行期间被操作系统管理的,创建PCB,地址空间,页表,映射关系。