生活中我们常见的计算机(eg:笔记本),不常见的计算机(eg:服务器),大部分都遵守冯诺依曼体系
特点
• 数据存储在"内存"当中
• 计算机当中的数据一切皆二进制
对冯诺依曼体系结构中各部分之间关系的理解
CPU ,寄存器,存储器之间的关系犹如古代皇上,太监,大臣之间的关系
• CPU的功能相当于皇上,寄存器的功能相当于太监,存储器的功能相当于大臣,大臣将(存储器)反馈上来的奏折(数据)交由太监(寄存器),再由太监(寄存器)将奏折(数据)传递给皇上(CPU)进行批阅(处理),等皇上(CPU)批阅完奏折(处理完数据),再将奏折(数据)交于(传递)太监(寄存器)反馈(返回)给大臣(存储器)
任何计算机系统都包含一个基本的程序集合,称为操作系统(OS),笼统的理解,操作系统包括:
• 内核(进程管理,内存管理,文件管理,驱动管理)
• 其他程序(eg:函数库,shell程序)
操作系统 = 操作系统内核 + 一堆应用
操作系统是软件,操作系统在管理计算机的软硬件资源
设计目的
• 与硬件交互,管理所有的软硬件资源
• 为用户程序(应用程序)提供一个良好的执行环境
那么OS是如何进行管理的呢?
管理 = 描述 + 组织
• 描述:自定义数据类型 eg:struct结构体
• 组织:使用链表或其他高效的数据结构
系统调用接口(函数)和库函数的区别
• 系统调用接口(函数):操作系统为程序员提供的函数,程序员调用这些函数更好的使用操作系统管理的资源
• 库函数:程序员对部分系统调用函数进行适度的封装,从而形成了库
eg:strcpy strlen malloc free
什么是程序?
程序就是经过源代码编译出来的文件,且程序是静态的
什么是进程?
进程是程序运行是的示例,是动态的(进程是程序运行状态时产生的一个实例);从内核的角度来看,进程是操作系统分配资源的实体
ps aux 查看当前操作系统的进程信息
让如下代码跑起来
通过管道符过滤查找当前运行的程序产生的进程信息
ps aux | grep ./main
此时会查看到两个./main的进程信息
为什么会产生两个./main的进程信息
• 第一行是正在运行程序产生的进程信息
• 第二行是因为在执行ps aux | grep ./main命令时,
在命令结束之前,将grep产生的进程信息也保存下来最终返回
进程信息被放在一个叫进程控制块(PCB)的数据结构中,可以理解为进程属性的集合;Linux操作系统下的PCB是task_struct,相当于结构体
• 在Linux中描述进程的结构体叫做task_struct
• task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程信息
并行:多个进程在同一时刻拥有不同的CPU进行运算,称之为并行
eg:八车道,同一时刻每个车道都可以有一个车通行
并发:多个进程在同一时刻只能由一个进程拥有CPU进行运算,称之为并发
eg:单车道同一时刻只能有一个车通过
• 进程标识符(PID):在操作系统中唯一标识一个进程,用来区别其他的进程
• 进程状态:用来表示进程的任务状态,退出代码,推出信号等
• 程序计数器:保存进程即将运行的下一条指令的地址
• 上下文数据:保存了寄存器中的内容
• 内存指针:指向了程序地址空间(指向堆区,数据段,代码段等)
• I/O状态信息:保存了当前进程打开文件的信息
==> /proc:保存了当前操作系统维护的所有进程信息,每一个进程都是一个文件夹
查看当前进程的进程号
cd到该文件夹下
cd到该文件夹下的fd文件夹查看它对应的I/O流
• 记账信息:使用cpu的时长,占用内存的大小
• 优先级:相对于其他程序的优先级
从大的方面说,进程有三个状态:就绪状态,运行状态,阻塞状态
细分角度来水,进程共有7种状态:
• R状态:运行状态–>包含就绪和运行两种状态(等待和运行两种状态)
• S状态:可中断睡眠状态–>进程在等待事件完成
• D状态:不可中断睡眠状态
• T状态:暂停状态
–>ctrl+z使一个进程变成暂停状态,暂停后进程仍然存在
–>ctrl+c暂停后进程不存在
• t状态:跟踪状态–>在gdb调试时会出现
• X状态:死亡状态
• Z状态:僵尸状态(后面和僵尸进程一起讲)
fg可以使Ctrl+z暂停的程序回复运行
getpid()函数:用来获取当前进程的pid号,谁调用获取谁的
getppid()函数:获取当前进程的父进程的pid号,谁调用获取谁的父进程
测试如下
fork()函数:可以让程序员在代码中创建一个子进程
pid_t fork(void)
• 没有参数
• pid_t:本质是整形
• 返回值:若创建子进程失败,返回-1;若创建子进程成功,对父进程,返回 >0 的数(其实就是返回子进程的PID号);对于子进程,返回 ==0 的数
• 原理:fork创建子进程的PCB是拷贝父进程的PCB
• 父子进程是相互独立的
ps -ef | grep ./main 可查看子进程的父进程
僵尸进程的产生:
子进程先于父进程退出,子进程在退出的时候,会告诉父进程,但父进程没有来得及回收子进程的资源,导致子进程的PCB没有被释放,就产生了僵尸进程,该进程对应的状态叫僵尸状态
测试如下:
创建一个test.c
如下代码父进程永远不退出(里面设置了死循环),子进程执行完代码就退出
使用命令ps aux | grep test查看test的进程信息,如下图所示PID为2337的进程为一个僵尸进程
僵尸进程的危害:
子进程的PCB没办法得到释放,想到与内存泄漏
解决办法:
kill命令
• kill + PID :杀死进程
• kill -9 + PID:强制杀死进程
但以上命令并不能杀死僵尸进程
• 重启操作系统,代价大,不推荐
• 杀掉僵尸进程的父进程,代价相对大,不推荐
进程等待-进程控制(后面讲),推荐的方式
孤儿进程的产生:
父进程先于子进程退出,子进程会被1号进程领养,子进程在退出的时候,就会由1号进程回收子进程的退出资源;称这种进程为孤儿进程
注意: 进程状态中没有孤儿状态
什么是1号进程
操作系统启动时产生的第一个进程是1号进程
测试如下:
创建出来的子进程是从fork之后代码运行还是从fork之前的代码开始运行?
父进程在执行fork时程序计数器中保存的的是下一条语句(判断语句),fork子进程,子进程拷贝父进程PCB是程序计数器中也保存到下一条判断语句,所以子进程从判断语句开始,子进程是从fork之后的代码运行
到底是父进程先运行还是子进程选运行?
这是不确定的,取决于操作系统的内核的调度(谁先拿到CPU资源)
若有两个CPU,可能父子进程同时运行
若只有一个CPU,取决于谁先拿到CPU资源谁先运行