目录
1.冯·诺依曼体系结构
我们常见的计算机乃至服务器,绝大部分都遵循冯·诺依曼体系结构:

其中,
寄存器:内存
外设:
1.输入设备:鼠标,键盘,摄像头,话筒,磁盘,网卡...
2.输出设备:显示器,播放器硬件,磁盘,网卡...
中央处理器:
1.运算器:对数据进行计算任务(算术运算,逻辑运算)
2.控制器:对计算硬件流程进行一定控制
这五大个体相互独立,因此必须用”总线“(1.系统总线 2.IO总线)链接起来。
2.操作系统(Operator System)
2.1 什么是操作系统?
任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。
笼统的理解,操作系统包括:
内核(进程管理,内存管理,文件管理,驱动管理)
其他程序(例如函数库,shell程序等等)
2.2 为什么要有操作系统
在谈操作系统之前,我们先看一个完整的计算机都有什么框架

由此可见,操作系统是一款帮助用户进行管理软/硬件资源的软件(手段),给用户提供一个高效、稳定、安全的运行环境(目的)。
2.3 操作系统怎么工作
操作系统为了保证自身数据安全,也为了保证给用户提供正常服务,操作系统以接口(操作系统自身提供的用C实现的自身内部函数调用)的方式给用户提供调用的入口,来获取操作系统内部的数据,同时所有访问操作系统的行为,仅且只能通过系统调用完成。
操作系统通过驱动程序拿到软硬件资源信息,由于操作系统由C语言实现,因此软硬件信息在操作系统中由struct组成,对资源对象的管理实际上就是对数据结构的管理(增删查改)。
3.进程
3.1 进程的基本信息
PCB:进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合,即PCB,在Linux中的PCB是task_struct,task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。
一个已经加载到内存的程序/正在运行的程序叫做进程,在操作系统中,进程=内核PCB数据结构对象(描述该进程的所有属性值)+自身代码和数据(磁盘中的可执行程序),操作系统通过管理一个进程的PCB来进行管理。

3.2 Linux如何进行进程管理
在Linux内核中,最基本的组织进程task_struc采用双链表组织。
其中,我们可以通过系统调用来获取进程的标识符即进程ID(pid)以及父进程ID(ppid);同样也可以通过系统调用fork函数创建一个进程。
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <sys/types.h>
4 int main()
5 {
6 pid_t id = fork();
7 if(id==0)
8 {
9 //子进程
10 while(1)
11 {
12 printf("pid:%d ppid: %d\n",getpid(),getppid());
13 sleep(3);
14 }
15 }
16 else if(id > 0)
17 {
18 //父进程
19 while(1)
20 {
21 printf("pid: %d ppid: %d\n",getpid(),getppid());
22 sleep(1);
23 }
24 }
25 else
26 {
27 //errori
28 }
29 // pid_t id = getpid();
30 // while(1)
31 // {
32 // printf("my pid is: %d\n",getpid());
33 // printf("my parent ppid is: %d\n",getppid());
34 // sleep(3);
35 //}
36 return 0;
37 }
这样就通过fork函数创建了一个新的子进程,对于上述代码,运行后会发现,两个if和else if中的两个死循环同时在运行,这是因为一个进程是由数据和代码共同组成的,对于父进程而言,数据和代码是我们已经输入好的,而子进程由于是在代码运行中才创建的,因此子进程并没有自己的数据代码,它与父进程共享代码,因此子进程同样也会执行父进程的代码;但是父进程与子进程执行的代码却又不相同,这是因为为了让父进程与子进程能够分配不同的工作,fork对其产生了不同的返回值。
那么fork函数如何返回两个返回值呢?fork函数在执行时,会先创建子进程的PCB,接着将其指向父进程的代码,此时父子进程都具有独立的task_struct,可以被CPU调度运行,最后在执行return语句。也就是说,在父进程的代码与子进程共享后,return语句才会被执行,因此父子进程会分别进行相应的return,因此fork函数也就实现了同时产生两个返回值。
进程是由代码+数据组成,子进程的代码与父进程共享,那数据呢?首先要了解,进程是拥有独立性的,即一个进程并不会影响另一个进程。父子进程共享代码,但是代码是不可修改的,因此不会对彼此造成影响,但数据不同,父子进程并不能共享同一份数据,这就需要操作系统为子进程拷贝数据,为了防止空间资源浪费,操作系统只有在子进程需要的时候从父进程处为子进程拷贝数据,这种拷贝也被称为写时拷贝。
4.进程状态
4.1 一般操作系统中的进程状态
在操作系统学科中,进程状态一般分为运行,阻塞,挂起三种状态。
对于运行状态而言,进程由连续的数据结构组织在一起,CPU通过运行队列来调度相应的进程,这些进程会被依次加入到运行队列,每一个在运行队列的进程状态都被称为运行态(随时准备被调度)。这么多进程CPU并不会同时进行,而是采取一种时间片的方式,即对每一个进程分配一个时间段,该进程只能在该时间进行运行,时间结束将被CPU剥夺并分配给下一个进程,如果进程在时间片结束前阻塞或结束,则CPU当即进行切换。而不会造成CPU资源浪费。(把大量的进程从CPU上放入和拿下的动作被称为进程切换)

对于阻塞状态是指线程运行过程中由于某些原因而暂时停止执行的状态,比如在操作系统调用硬件数据时,键盘正在读取数据,这时键盘的线程会进入等待队列,而在等待队列的进程状态被称为阻塞状态。

挂起状态是指当操作系统内存不足时,在保证正常状态下,对于未运行的进程只保留其PCB而将数据和代码放回磁盘当中来节省资源,此时的进程状态被称为阻塞挂起状态。
4.2 Linux操作系统中的状态如何维护
4.2.1 Linux下的进程状态
对于不同的进程状态在Linux内核源码中有如下定义:
/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
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运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列
S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠 (interruptible sleep))
D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(深度睡眠)(uninterruptible sleep),在这个状态的进程通常会等待IO的结束
T停止状态(stopped):可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可 以通过发送SIGCONT信号让进程继续运行
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态
Z(zombie)-僵尸进程状态
在进程退出时,若父进程没有主动回收子进程信息,子进程会一直保持Z状态,进程的相关资源包括task_struck不能被释放。僵尸进程通常不会占用太多系统资源,但如果大量的僵尸进程积累,可能会导致进程表被耗尽,内存泄漏,影响系统的正常运行。
4.2.2 孤儿进程
父进程如果提前退出,那么子进程后退出,进入Z状态之后,那该如何处理呢?
父进程先退出,子进程就称之为“孤儿进程” ,孤儿进程被1号init进程领养(常是进程ID为1的系统守护进程)
孤儿进程通常会被init进程接管,并由init进程来对其进行善后处理,包括回收其资源。这样,操作系统就能够确保不会出现一大堆没有父进程的孤立进程,从而防止资源泄露和系统资源浪费。
此外,孤儿进程会迅速被init进程回收,因此它们并不会一直存在于系统中。
5.进程优先级
5.1 进程优先级
CPU资源分配的先后顺序,就是指进程的优先权(priority)。那么为什么要进行优先级的排序呢?因为在操作系统内,资源空间是有限的,而进程会存在多个,因此不同进程间存在竞争关系,操作系统为保证进程之间的良性竞争,就要确定优先级,若一个进程长时间得不到CPU资源,该进程的代码长时间无法推进,则被称为该进程的饥饿问题。优先权高的进程有优先执行权利。配置进程优先权对多任务环境的Linux,可能改善其系统性能。 还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。
5.2 Linux中的进程
在Linux系统中,输入ps - l会输入如下内容:

其中,
UID : 代表执行者的身份
PID : 代表这个进程的代号
PPID :代表这个进程是由哪个进程发展衍生而来的,即父进程的代号
PRI :代表这个进程可被执行的优先级,其值越小越早被执行
NI :代表这个进程的nice值,其表示进程可被执行的优先级的修正数值
nice值:是用来调整进程优先级的一个参数。进程的"nice值"决定了进程在CPU调度时的优先级。较低的"nice值"表示较高的优先级,而较高的"nice值"表示较低的优先级。nice值"的范围通常是从-20到+19,其中-20表示最高优先级,+19表示最低优先级。默认情况下,普通用户创建的进程的"nice值"通常为0,且只能在0-19之间调整,root用户可以设置更低的值(高优先级)
PRI和NI:PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice 这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行 所以,调整进程优先级,在Linux下,就是调整进程nice值,而在一般的Linux操作系统下,PRI的默认值为80
竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高 效完成任务,更合理竞争相关资源,便具有了优先级
独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰 并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发
2023.8.12 加油!

被折叠的 条评论
为什么被折叠?



