冯诺依曼体系结构
说明:
- 输入设备:键盘、磁盘、网卡、显卡、话筒、摄像头…
- 输出设备:显示器、磁盘、网卡、显卡、音响…
- 存储器:注意指的是内存,不是磁盘。
- 中央处理器(CPU):其中运算器进行算术运算和逻辑运算
CPU不和外设直接交互,主要通过将数据存储在内存中(存储器),再通过cpu读取内存中缓存的数据(主要是外设太慢,cpu快,为了高效处理数据,建立一个缓冲地带),CPU无论是输入还是输出都只与内存打交道,也是为了保护cpu
2,linux操作系统是怎么工作的
什么是操作系统:操作系统是一款用来对软硬件进行资源管理的软件,它是硬件基础上的第一层软件,是硬件和其它软件沟通的桥梁(或者说接口、中间人、中介等),操作系统会控制其他程序运行,管理系统资源,提供最基本的计算功能,如管理或者配置内存,决定系统资源的优先顺序
例如如下的服务程序:
1,文件系统
提供计算机存储信息的结构,信息存储在文件中,文件主要存储在计算机的内部硬盘里,在目录的分层结构中组织文件。文件系统为操作系统提供了组织管理数据的方式
2,设备驱动程序
提供连接计算机的每个硬件设备的接口,设备驱动器使程序能够写入设备,而不需要了解执行每个硬件的细节。简单来说,就是让你能吃到鸡蛋,但不用养一只鸡
3,系统服务程序
当计算机启动时,会自启动许多系统服务程序,执行安装文件系统、启动网络服务、运行预定任务等操作。
操作系统就类似与一个大家长,管理家里的所有人,操作系统在给其他软件提供各种便利的同时,还会约束其他软件不能为所欲为。
具体的图示如下所示
所以在实际的操作系统中,会构建结构体来建立对象的信息,先组织,再描述->构建数据结构的过程,就是一个一个的数据结构,管理者实际是对数据结构的管理。
3,计算机管理硬件
1,先描述,用struct创建这样的抽象类
2,再组织,用双向链表这样的数据结构,将所有的struct链接起来。
3.1,系统调用和库函数概念
1,在开发角度,计算机对外表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用。
2.系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。
4.进程理解
4.1.概念
概念:程序的一个执行实例,正在执行的程序就会创建一个进程
内核观点:担当分配系统资源(CPU时间,内存)的实体
4.2,描述进程-pcb
进程在生成的同时,会创建会相应的struct,供操作系统管理,在linux下,进程创建会产生一个task_struct,称之为PCB,也可以认为,PCB就是进程的属性的集合。
task_struct是linux内核的一种数据结构,它会被装载在RAM(内存)并且包含着进程的信息
task_struct内容分类,
1.标示符: 描述本进程的唯一标示符,用来区别其他进程。
即是进程的pid,所有进程都有自己唯一的pid
2.状态: 任务状态,退出代码,退出信号等。
3.优先级: 相对于其他进程的优先级。
为什么linux需要有优先级,CPU资源是有限的,进程是有多个的,此时我们就需要进程去排队,站在队头的优先级就高,cpu通过拿指令,分析指令,执行指令,再把数据缓存到内存中,此时如果程序是一个死循环的话,其他进程就不能执行了,所以此时就引出了时间片的概念,一个进程在一定的时间片中执行,当未被执行完时,操作系统会记住当前指令的地址,等下次运行到这个进程后,会取出指令的地址,继续执行,直到进程执行完。
程序计数器: 程序中即将被执行的下一条指令的地址。
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
上下文数据: 进程执行时处理器的寄存器中的数据。
I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
其他信息
5.组织进程
可以在内核中看到,它的所有运行在系统里的进程都以task_struct链表的形式存在内核里
通过 ls /proc就可以看到所有进程
再通过ps aux | head -1 && ps aux | grep process ,即可得到当前我们正在运行的进程
也可以通过系统调用获取当前的子进程和父进程
就可以看到进程的pid和ppid
6,fork()初相识
fork通过man查询以后,可以得知,fork有两个返回值,返回父进程的id,子进程返回0,
通过运行此程序就可以看到fork创建的进程返回值有两个,通过if分流 之后,就可以实现一个运行,一个挂起。
进程状态
为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在Linux内核里,进程有时候也叫做任务)。
下面的状态在kernel源代码里定义,
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
R运行状态;并不意为者程序一直在运行,而是处在可被CPU调度的状态,要么处在运行,要么处在运行对列里面。
S睡眠状态,意为者进程在等待某种事件发生,称为浅度睡眠,就比如sleep(100),所处的状态就是休眠,如果休眠时间到了,就会转换成R状态。
D磁盘休眠状态(Disk sleep),有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
举个栗子:李四是进程,张三是磁盘,王五是操作系统
李四要给张三存东西,张三说有点忙,等忙完再存,然后李四就在那等着,结果王五过来干掉了张三,造成数据丢失,
此时给用户带来非常不好的理念,所以引入D状态,操作系统不会干掉你,只有当存取完以后,自动退出进程。
T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
当想让进程停止时,kill -SIGSTOP 27642
进程进入停止状态,等待启动。
启动进程:kill -SIGCONT 27642
Z(zombie)-僵尸进程
僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程,没有读取到子进程退出的返回代码时就会产生僵死(尸)进程,僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
#include <stdio.h>
#include <stdlib.h>
int main()
{
pid_t id = fork();
if(id < 0){
perror("fork");
return 1;
}
else if(id > 0){ //parent
printf("parent[%d] is sleeping...\n", getpid());
sleep(30);
}else{
printf("child[%d] is begin Z...\n", getpid());
sleep(5);
exit(EXIT_SUCCESS);
}
return 0;
}
僵尸进程危害
1,进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态?是的!,2.维护退出状态2,本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护?是的!
3,那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间!
4,内存泄漏?是的!
孤儿进程
父进程如果提前退出,那么子进程后退出,进入Z之后,那该如何处理呢?
父进程先退出,子进程就称之为“孤儿进程”
孤儿进程被1号init进程领养,当然要有init进程回收喽
#include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<sys/types.h>
5 int main()
6 {
7 pid_t ret =fork();
8 if(ret>0)
9 {
10 printf("i am father...... pid::%d...ppid...%d\n",getpid(),getppid());
11 exit(0);
12 }
13 else if(ret==0)
14 {
15 int i=0;
16 for( i=0;i<10;i++)
17 {
18 printf("i am child......pid::%d...ppid...%d\n",getpid(),getppid());
19 exit(0);
20 }
21 }
22 else
23 {
24 printf("error\n");
25 }
26 return 0;
27 }
28
可以看到孤儿进程最后被1进程领养。