学习目标:
1、计算机的基本组成
2、进程概述
3、简单分页的内存管理
4、C程序两个问题
学习内容:
1、计算机的基本组成
冯·诺依曼体系 计算机有五大部件组成:
- 计算器
- 控制器
- 存储器 – 内存、主存
- 输出设备 – 磁盘、显示器 -output
- 输入设备 – 磁盘、键盘、鼠标 -input
IO 输入、输出。读取或写入的效率相对于内存操作,是比较低的 (CPU - 一级缓存 - 二级缓存 - 内存 - IO)
-
系统总线类型
- 控制总线: 由于各个连接数据总线和控制总线的部件都是共享这两个总线的,所以他是用来指定某一时刻由谁来控制总线的
- 数据总线 : 用于在各个功能部件间传输数据的,是双向传输总线
- 地址总线: 用于支出数据总线上的源数据或目的数据在主存单元的地址或者IO设备的地址 --》 地址总线决定了CPU的寻址能力
-
扩充:32位机器 CPU的计算能力,一次能够计算的数据的数据宽度,ALU的宽度,一次能处理的数据的最大位数(32位 – 最大的处理数字)
- x86(32位) 指针都是4字节; x64(64位)指针都是8字节 - 指令:地址码+操作码
2、进程概述
进程的概念:一个正在运行的程序。(程序:二进制可执行文件)
- 进程是一组有序指令+数据+资源(内存资源、CPU资源)的一个集合。
执行一个程序,使之成为一个进程:操作系统先得分配一个PCB变量,再需要将二进制文件加载到内存上,操作系统使用调度策略来分配CPU去执行加载到内存上的指令和数据。
操作系统如何管理进程:
- 将进程相关的一些属性信息(一组)保存到一个地方(内存) — 通过C语言的结构体将这些属性信息组织在一块 — PCB(进程控制块)task_struct
- PCB:即是进程控制块,是进程存在的唯一标志,用来描述进程的属性信息。
- 操作系统维护管理所有的PCB是通过:双向循环链表(任务队列 task list)(有优先级的队列)
-
进程结束时,操作系统先释放进程主体,再释放PCB。
-
僵尸进程:PCB 还在,进程实体已经释放了。
-
进程的状态
就绪:所有的条件,资源都准备完成,只等CPU空间来调度执行。
执行:正在被CPU执行的其中的命令
阻塞:所需要某些事件(条件)还没好
- 并发与并行
更多是并发编程。
3、简单分页的内存管理
简单分页:将内存空间划分为大小相等的(相对于内存大小而言,这个大小很小,比较经典的就是4k)区块,将其称为页帧。对所有的页帧可以编号。4G内存有2^20个页帧。
操作系统为每一个进程维护一个页表。页表中记录了该页加载到内存上的那个页帧,还有一些控制信息。
编译链接:连接过程合并所有的段,调整段的大小和起始位置,合并符号表,进行符号解析,给符号分配虚拟地址,符号重定位。
程序中的地址都是虚拟地址,在程序运行时,需要通过虚拟地址找到内存上的一个确定位置。除了与这个虚拟地址有关,还与进程的页表有关。
交换分区
在磁盘上开辟一块空间作为对内存的补充。
一个进程并不需要所有的页都在内存上驻留。
可以执行比内存空间还大的一些进程。也可以执行更多的进程。
操作系统发展史
操作系统
管理计算机上的软硬件资源,为用户提供一个交互的窗口。
串行处理 — 批处理 — 多道程序设计 — 分时系统 — 实时系统
4、C程序两个问题
主函数的参数
主函数默认至少接受一个参数,就是执行程序命令本身。
传递参数时,按照空格来区分各个参数。
传递的参数的类型都是字符串,与用户给定的参数的类型无关。
#include <stdio.h>
#include<string.h>
#include<stdlib.h>
/*
envp:是一个字符指针数组,环境变量
*/
int main(int argc,char *argv[],char *envp[])
{
int i=0;
while(envp[i]!=NULL)
{
printf("%s\n",envp[i]);
i++;
}
return 0;
}
#if 0
/*
argc:整形 传递的参数个数
argv:字符指针数组,每一个数组都是一个字符指针(字符串) 传递的参数列表
*/
int main(int argc,char *argv[])
{
printf("%d\n",argc);
int num=0;
sscanf(argv[1],"%d",&num);
printf("num=%d , num*num =%d\n",num,num*num);
for(int i=0;i<argc;i++)
{
printf("%d:%s\n",i,argv[i]);
}
return 0;
}
#endif
输出缓冲区
在Linux系统上,一个进程默认打开第三个文件:
标准输入stdin 标准输出 stdout 标准错误输出stderr
printf函数仅仅是将内容写到标准输出缓冲区中,等满足一定条件,才会将内容输出到界面上:
- 遇到 “\n”
- 进程结束时
- 主动刷新 fflush(stdout);
- 缓冲区满
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
printf("hello");//printf仅仅是将内容写到输出缓冲区中
sleep(3);
printf("world");//结果是一块输出
exit(0);
}
exit和_exit的区别
exit会刷新缓冲区的内容;
_exit不会刷新缓冲区,直接结束进程。
atexit函数
atexit注册函数:向操作系统注册一个函数,这个函数当前不会被调用,程序结束时,调用exit之后,exit函数退出之前,调用之前注册的函数。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
void func1()
{
printf("func1...\n");
}
int main()
{
atexit(func1);
atexit(func2);
printf("main start\n");
sleep(2);
printf("main end\n");
exit(0);
}
若遇到_exit,则不会调用:
也可以使用atexit调用多个函数(最多注册32个函数),进程结束时,按照出栈的顺序来调用: