
冯·诺依曼结构也称普林斯顿结构,是一种将程序指令存储器和数据存储器合并在一起的存储器结构。程序指令存储地址和数据存储地址指向同一个存储器的不同物理位置,因此程序指令和数据的宽度相同,数学家冯·诺依曼提出了计算机制造的三个基本原则,即采用二进制逻辑、程序存储执行以及计算机由五个部分组成(运算器、控制器、存储器、输入设备、输出设备),这套理论被称为冯·诺依曼体系结构。

我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系。截至目前,我们所认识的计算机,都是有一个个的硬件组件组成。
中央处理器的速度是非常快的,主要用于处理一些加减乘除模运算、循环、逻辑判断等,而输入和输出设备的速度是非常慢的。
需要注意的是:
操作系统是一个任何计算机系统都包含一个基本的程序集合。
操作系统包括:
设计操作系统的目的:
操作系统是如何进行管理的呢?
学校里各个角色的组织就和操作系统的管理很像,校长是如何管理整个学校成千上万的学生呢?
学生是被管理者,校长是管理者。校长并不会直接关注学生各种信息,也就是说管理者和被管理者并不直接打交道,但是我们的信息又是怎么被校长知道的,这时候就产生出辅导员这个角色,辅导员把我们的信息汇总成表,提交给校长,让校长做决策,校长就把学生的信息聚合起来,产生关联,并做决策,比如开除同学,分发奖学金等,这本质上就是对数据结构的增删查改。
所以总而言之,校长管理学生的方式就是先描述学生的信息,然后再将学生的信息组织起来,并进行决策。这种方式被称为先描述,再组织。
同样的,操作系统管理软硬件也是这样,先描述,再组织。

系统调用和库函数概念:
首先我们要明确,操作系统是如何管理进程的?先描述,再组织。
进程的信息可以通过 /proc 系统文件夹查看。

大多数进程信息同样可以使用top和ps这些用户级工具来获取


#include
#include
int main()
{
printf("pid: %d\n", getpid());
printf("ppid: %d\n", getppid());
return 0;
}


fork 是创建子进程,也就意味着 fork 之后,这个子进程才能被创建成功,父进程和子进程都要运行相同的代码,fork 之后,父进程和子进程谁先运行,不是由 fork 决定的,而是由系统的调度优先级决定的。
fork 创建子进程之后,父子进程共享一份代码,当子进程或父进程要修改代码和数据时,操作系统会采用写时拷贝,生成两份相同的代码和数据,再由子进程或父进程来修改代码和数据,这样就能达到互不干扰的效果,所以在操作系统中,进程是具有独立性的。
fork 在创建子进程之后,操作系统就多了一个进程,这个子进程是由父进程为模板创建的,会继承程序的代码和数据,内核数据结构task_struct也会以父进程为模板,初始化子进程的task_struct。
fork有两个返回值,通过一个程序来验证一下:
#include
#include
int main()
{
int result = fork();
printf("pid: %d, result: %d\n", getpid(), result);
sleep(1);
return 0;
}


/*
* 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):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
ps aux / ps axj 命令


下面我们用一个例子来验证一下僵尸进程:
#include
#include
#include
using namespace std;
// 验证僵尸进程
int main()
{
pid_t id = fork();
if(id == 0){
//child
while(true){
cout << "child is running!" << endl;
sleep(2);
}
}
else{
//parent
cout << "parent do nothing!" << endl;
sleep(50);
}
return 0;
}
要验证该程序,需要启动三个终端:
第一个终端
编写监控命令行脚本:

第二个终端
启动对应的进程:

第三个终端
用信号杀死子进程

结果:

在操作系统领域中,孤儿进程指的是在其父进程执行完成或被终止后仍继续运行的一类进程。这些孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
验证孤儿进程:
#include
#include
#include
using namespace std;
// 验证孤儿进程
int main()
{
pid_t id = fork();
if(id == 0){
// child
while(true){
cout << "child is running!" << endl;
sleep(2);
}
}
else{
//parent
cout << "parent is running!" << endl;
sleep(10);// 10s后父进程终止
exit(1);
}
return 0;
}
结果:
