进程概念一
冯诺依曼体系结构
我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系。
截止目前为止, 我们所认识的计算机,都是一个个的硬件组成
- 输入设备:包括键盘,鼠标,网卡,磁盘,话筒
- 输出设备:显示器,磁盘,网卡,声卡,音响
输入设备,输出设备称之为外围设备,外围设备一般都比较慢一些,以磁盘为例相对于内存,磁盘是比较慢的。
这里我们可以思考一下,为什么不设计如下图所示的样子呢?
这里我们可以想到木桶原理,外设的效率非常低,但是CPU
效率很高,整体效率就是以外设为主的。
所以因为有了内存的存在,我们可以对数据进行预加载,CPU
以后在进行数据计算的时候,根本不需要访问外设了,而是直接访问内存就可以了。
但是这里我们怎么知道哪些内容需要预加载进CPU
当中,通过大量的测试发现,预加载的内容一般都是加载内容旁边的内容。
关于冯诺依曼体系的几点:
- 这里的存储器指的是内存
- 不考虑缓存情况,这里的
CPU
能且只能对内存进行读写,不能访问外设(输入设备或输出设备)- 外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存读取
- 所有的外围设备只能和内存打交道
理解:可执行程序是不是一个文件?是 -> 磁盘 -> 外设。必须先加载到内存中。
在硬件层面单机和跨机之间数据是如何流向的。
你和朋友,聊qq
,你发送了一个在吗?整个信息如何在体系结构中流动的
结论:
- 在数据层面,一般
CPU
不和外设直接沟通,而是直接和内存打交道 - 外设只会和内存打交道
操作系统
概念
任何计算机系统都包含一个基本的程序集合,称为操作系统(OS
)。笼统的理解,操作系统包括:
- 内核(进程管理,内存管理,文件管理,驱动管理)
- 其他程序(例如函数库,
shell
程序等等)
设计OS
的目的
- 与硬件交互,管理所有的软硬件资源
- 为用户程序(应用程序)提供一个良好的执行环境
操作系统为什么对软硬件资源进行管理呢?
操作系统对下通过管理好软硬件资源(手段),对上给用户提供(安全、稳定、高效、功能丰富等)执行环境(目的)
定位
在整个计算机软硬件架构中,操作系统的定位是:一款纯正的“搞管理”的软件
操作系统,一款进行软硬件资源管理的软件
如何理解“管理”
- 管理者和被管理者是不需要直接沟通的。对管理做建模
- 管理的本质:对被管理对象的数据做管理
管理的本质:先描述,在组织
操作系统给我们提供良好的服务,并不代表操作系统相信我们,反而操作系统不相信任何人!
我们并不能进入操作系统来随心所欲的访问数据。
系统调用接口,其实就是操作系统设计的C
函数,这里的操作系统就像银行的柜台一样,我们只能去做规定的操作。
系统调用和库函数概念
- 在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用
- 系统调用在使用上,功能比较基础,对用户的要求也比较高。所以有心的开发者可以对部分系统调用进行适度封装,从而形成了库,有了库就很利于更上层的用户或者开发者进行二次开发
承上启下
那在还没有学习进程之前,操作系统是怎样进行进程管理的呢?很简单,先把进程描述起来,再把进程组织起来!
进程
基本概念
- 课本概念:程序的一个实例,正在执行的程序等
- 内核观点:担当分配系统资源(
CPU
时间,内存)的实体什么是进程?内核关于进程的相关数据结构和当前进程的代码和数据
描述进程-PCB
- 进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合
- 课本上称之为
PCB
,Linux
操作系统下的PCB
是:task_struct
task_struct-PCB
的一种
- 在
Linux
当中描述进程的结构叫做task_struct
task_struct
是Linux
内核的一种数据结构,它会被装载到RAM
(内存)里并且包含着进程的信息
task_struct
内容分类
- 标识符:描述进程的唯一标识符,用来区别其他进程
- 状态:任务状态,退出代码,退出信号灯
- 优先级:相对于其他进程的优先级
- 程序计数器:程序中即将被执行的下一条指令的地址
- 内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
- 上下文数据:进程执行时处理器的寄存器中的数据
I/O
状态信息:包括显示的I/O
请求,分配给进程的I/O
设备和被进程使用的文件列表- 记账信息:可能包括处理器时间总和,使用的时钟总和,时间限制,记账号等
组织进程
可以在内核源码里找到它。所有运行在系统里的进程都以task_struct
链表的形式存在内核里。
查看进程
- 进程的信息可以通过
/proc
系统文件夹查看
如果需要查看pid
为1的进程可以通过这种手段来查看
/proc/1
- 大多数信息可以通过
top
和ps
这些用户工具来获取
如下代码:
我们可以通过这个代码来查看有关这个进程的信息。
ps ajx | grep test | grep -v grep
通过系统调用获取进程标识符
- 进程id(
PID
)- 父进程id (
PPID
)
bash
本质上也是一个进程
命令行启动的所有程序,最终都会变成进程,而该进程对应的父进程都是bash
如何创建子进程的呢?
fork
之后,执行流会变成2个执行流fork
之后,谁先运行由调度器决定fork
,fork
之后的代码共享,通常通过if
和else if
来进行执行流分流
通过系统调用创建进程fork
关于fork
介绍
- 在父进程当中
fork
会返回新创建子进程的id
- 在子进程当中
fork
会返回0 - 如果出现错误,返回一个负值
fork
会有两个返回值- 父子进程共享代码,数据各自开辟空间,私有一份(采用临时拷贝)
fork
通过用if
来分流
使用例子:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int ret = fork();
if (ret < 0) {
perror("fork");
return 1;
}
else if (ret == 0) { //child
printf("I am child : %d!, ret: %d\n", getpid(), ret);
}
else { //father
printf("I am father : %d!, ret: %d\n", getpid(), ret);
}
sleep(1);
return 0;
}
进程在运行的时候,是具有独立性的!父子进程,运行的时候,也是一样的都是具有独立性的
进程 = 内核数据结构 + 进程的代码和数据
数据:当有一个执行流尝试修改数据的时候,操作系统会自动给我们当前进程触发写时拷贝
fork
如何理解两个返回值的问题?
fork
本质是操作系统提供的一个函数,当这个函数执行的时候会创建出一个执行流,此时就会有两个执行流,然后就会有两条return
语句。