• 【Linux】冯诺依曼体系结构


    🌈前言

    本篇文章进行操作系统中进程的学习!!!


    🌷1、冯诺依曼体系结构

    我们常见的计算机,如:笔记本,不常见的计算机,比如:服务器,它们都遵循冯诺依曼体系结构

    在这里插入图片描述


    截至目前,我们所认识的计算机,都是有一个个的硬件组件组成:

    • 输入设备:键盘、话筒、鼠标、摄像头、网卡和显卡等等

    • 输出设备:显示器、音响、磁盘、网卡和显卡等等

    • 中央处理器(CPU):含有运算器和控制器等

    • 存储器:存储器就是一个内存


    为什么输入设备和输出设备之间还要一块存储器呢?

    木桶原理

    • CPU运算速度 > 寄存器 > L1~L3Cache(高速缓冲存储器) > 内存 >> 外设(磁盘) >> 光盘磁带

    • 外设不和CPU直接交互,而是和内存交换,CPU也是如此,CPU只与内存进行交互

    • 内存在我们看来,就是体系结构的一大缓存,为了解决CPU与外设速度不均的问题!!!

    • 从成本角度:寄存器 > 内存 > 磁盘,成本低,能获得较高的性能,让计算机蔓延全世界


    关于冯诺依曼,必须强调几点:

    • 不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备)

    • 外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取

    • 一句话,所有设备都只能直接和内存打交道

    • 我们编写的代码,要运行,必须加载到内存局部性原理

    • 几乎所有硬件,只能被动的完成某种功能,不能主动的完成某种功能,一般都是要配合软件完成的(OS+CPU)


    我们平时在qq跟网友聊天,是怎么实现的呢?(省略网络)

    在这里插入图片描述

    聊天时传输文件呢?

    在这里插入图片描述


    🌸2、操作系统(Operator System)

    🌹2.1、概念

    任何计算机系统都包含一个基本的程序集合,称为操作系统(OS),它包含:

    • 内核(进程管理,内存管理,文件管理,驱动管理)

    • 其他程序(例如函数库(API), shell程序等等)

    设计OS的目的:

    • 与硬件交互,管理所有的软硬件资源

    • 为用户程序(应用程序)提供一个良好的执行环境

    定位:

    • 在整个计算机软硬件架构中,操作系统的定位是: 一款纯正的“搞管理”的软件

    🍀2.2、如何理解管理

    概念:

    • 我们人与人之间的管理是通过管理者发出决策后,然后通过执行者去完成的这个过程

    • 执行者拿到被管理者对象的数据后,将执行的结果反馈给管理者

    管理的本质:

    • 对数据进行管理

    • 不是对被管理对象直接进行管理,而是只要拿到被管理者对象所有的相关数据后。管理者对数据的管理,就可以体现出间接的对人管理

    管理的核心理念:先描述,再组织

    • 人认识世界的方式:通过属性认识世界的

    • 在面向对象的语言中:我们经常说“”一切皆对象

    • 一切事物都可以通过抽取对象的属性,来达到描述对象的目的

    // 比如有一个学生,可以通过它的属性去描述它
    class Student
    {
    // 学生的属性 -- 学号、名字、身高、体重等等...
    private:
    	string name;	// 名字
    	string Id;		// 学号
    	int height;		// 身高
    	int weight;		// 体重
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    管理理解一:管理的本质其实是对数据的管理

    在这里插入图片描述

    管理理解二:

    在这里插入图片描述


    总结:

    1. 计算机管理硬件,通过描述属性,用struct结构体

    2. 组织起来,用链表或其他高效的数据结构来进行管理


    🍃3.2、系统调用和库函数概念

    • 在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用

    • 系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发

    在这里插入图片描述

    操作系统为什么要给我们提供服务?

    • 计算机和操作系统被设计出来就是为了给我们人提供服务的

    • 我们在使用编程语言进行开发时,就会间接的调了系统调用

    • OS给我们提供服务,可以提高我们开发的效率,比如printf就间接调用了系统调用在这里插入图片描述

    总结:

    • 操作系统是不相信任何人的,不会直接暴露自己的任何数据结构,代码逻辑,其他数据相关细节

    • 操作系统是通过系统调用的方式,向外提供接口服务的!!!

    • Linux是用C写的,这里所谓的“接口”,就是C语言中的函数

    • 我们学习系统编程,本质就是学习系统的接口(函数接口)


    🍁3、进程

    🍂3.1、概念

    进程的基本概念:

    • 课本概念:程序的一个执行实例,正在执行的程序等

    • 内核观点:担当分配系统资源(CPU时间,内存)的实体

    • 操作系统中有内存管理、进程管理、文件管理和驱动管理,而进程是它们中的一种

    描述进程(PCB):

    • 进程信息会被放在一个叫进程控制块的数据结构中,可以理解为进程属性的集合

    • 课本上称之为PCB(process control block),Linux操作系统下的PCB是:task_struct

    task_struct是PCB中的一种:

    • 在Linux中描述进程的结构体叫做task_struct

    • task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息

    在这里插入图片描述


    🍃3.2、查看进程信息

    查看进程的方法有二种:

    1. 通过【ps】指令进行查询(建议)
    [lyh_sky@localhost lesson10]$ ls
    makefile  test  test.cpp
    [lyh_sky@localhost lesson10]$ cat test.cpp 
    #include 
    #include 
    using namespace std;
    
    int main()
    {
        while (1)
        {
            cout << "hello world" << endl;
            sleep(3);
        }
        return 0;
    }
    [lyh_sky@localhost lesson10]$ cat makefile 
    test:test.cpp
    	g++ test.cpp -o test
    .PHONY:clean
    clean:
    	rm -rf test
    [lyh_sky@localhost lesson10]$ ./test
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    // 查看进程 -- ps ajx拿到全部进程,然后通过管道流入到grep中,通过grep查找test的进程
    // grep也是一个进程,也会显示这个进程的信息
    [lyh_sky@localhost lesson10]$ ps ajx | grep 'test'
      3140   3403   3403   3140 pts/1      3403 S+    1000   0:00 ./test
      2998   3405   3404   2998 pts/0      3404 S+    1000   0:00 grep --color=auto test
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    显示ps指令第一行数据,并且搜索打印指定进程的信息,&&是逻辑与左边运行成功就执行右边指令

    [lyh_sky@localhost lesson10]$ ps ajx | head -1 && ps ajx | grep 'test'
      PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
      3140   3718   3718   3140 pts/1      3718 S+    1000   0:00 ./test
      2998   3723   3722   2998 pts/0      3722 S+    1000   0:00 grep --color=auto test
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述


    1. 进程的信息可以通过 /proc 系统文件夹查看(要先知道进程PID为多少)
    • 比如:要获取test程序的进程信息,可以通过ls -al /proc/PID
    [lyh_sky@localhost lesson10]$ ls
    makefile  test  test.cpp
    [lyh_sky@localhost lesson10]$ cat test.cpp 
    #include 
    #include 
    #include 
    using namespace std;
    
    int main()
    {
        while (1)
        {
            cout << "hello world" << endl;
            // 获取进程PID,后面讲
            cout << "PID: " << getpid() << endl;
            sleep(3);
        }
        return 0;
    }
    [lyh_sky@localhost lesson10]$ cat makefile 
    test:test.cpp
    	g++ test.cpp -o test
    .PHONY:clean
    clean:
    	rm -rf test
    [lyh_sky@localhost lesson10]$ ./test
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    在这里插入图片描述


    我们学习C语言中文件操作时,使用open打开文件,状态为’w’,没有这个文件会自动在当前路径创建一个新的文件,如何理解“当前路径”呢?

    • 当前路径:当前进程所在的路径,进程会自己进行维护

    • PID和当前路径都属于进程的内部属性,在进程控制块(PCB)的结构体中
      在这里插入图片描述


    🍄3.3、通过系统调用获取进程PID/PPID

    进程分为二种:

    • 父进程id(PPID)

    • 子进程id(PID)

    #include 
    #include 
    #include 
    
    int main()
    {
    	printf("pid: %d\n", getpid());
    	printf("ppid: %d\n", getppid());
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    [lyh_sky@localhost lesson10]$ ls
    makefile  test  test.cpp
    [lyh_sky@localhost lesson10]$ cat test.cpp 
    #include 
    #include 
    #include 
    using namespace std;
    
    int main()
    {
        while (1)
        {
          cout << "父进程PPID: " << getppid() << endl;
            cout << "子进程PID: " << getpid() << endl;
            sleep(3);
        }
        return 0;
    }
    [lyh_sky@localhost lesson10]$ cat makefile 
    test:test.cpp
    	g++ test.cpp -o test
    .PHONY:clean
    clean:
    	rm -rf test
    [lyh_sky@localhost lesson10]$ ./test 
    父进程PPID: 3140
    子进程PID: 5419
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    在这里插入图片描述

    为什么我们的父进程总是不变呢?

    • 因为我们在几乎在命令行所执行的所有指令,都是bash进程的子进程

    • bash进程就是shell命令解析器,shell也是一款软件,也要被加载到内存中,电脑开机时自动加载shell

    • OS也是一款软件,电脑开机时也会自动加载操作系统

    在这里插入图片描述


    🍅3.4、通过系统调用创建进程 – fork()

    • fork函数是用来创建子进程的,fork有两个返回值

    • 父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)

    • 父进程会返回子进程的PID,子进程会返回0

    [lyh_sky@localhost lesson10]$ cat test.cpp 
    #include 
    #include 
    #include 
    using namespace std;
    
    int main()
    {
    	// 调用系统接口创建子进程
        pid_t id = fork();
    
    	// fork()返回0说明是一个子进程,反之父进程
        if (id == 0)
        {
            while (1)
            {
                cout << "我是子进程, 我的pid: " << getpid()
                  << ", 我的父进程是: " << getppid() << endl << endl;
                sleep(3);
            }
        }
        else
        {
            while (1)
            {
                cout << "我是父进程, 我的pid: " << getpid()
                  << ", 我的父进程是: " << getppid() << endl;           
                sleep(3);
            }
        }
        return 0;
    }
    
    [lyh_sky@localhost lesson10]$ cat makefile 
    test:test.cpp
    	g++ test.cpp -o test
    .PHONY:clean
    clean:
    	rm -rf test
    	
    [lyh_sky@localhost lesson10]$ ./test
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    在这里插入图片描述


    fork()是如何做到会有不同的返回值呢?

    • C/C++中,if…else和多个死循环是不可能同时执行的

    • fork()之后,父进程和子进程会共享代码,一般都会执行后续的代码 – printf/cout打印二次问题

    • fork()之后,父进程和子进程返回值不同,可以通过不同返回值判断,让父进程执行不同的代码


    fork()为什么父进程返回子进程pid,子进程返回0?

    • 父进程与子进程的比例是:1比n(n >= 1)

    • 因为父进程必须有标识子进程的方案,fork()之后,给父进程返回子进程的pid

    • 子进程最重要的是知道自己被创建成功了,因为子进程找父进程成本非常低(getppid)


    fork()为什么会返回二次值?

    • 图解:

    在这里插入图片描述

  • 相关阅读:
    linux系统定时任务与延迟任务
    【Shell编程】Shell中的正则表达式
    Intel汇编-内联汇编使用处理跳转
    Vue3+ts -01
    【面试:并发篇30:多线程:happen-before】
    java毕业设计橱柜定制系统Mybatis+系统+数据库+调试部署
    Springboot整合redis
    设计模式之外观模式
    gRPC学习笔记(一)
    Weblogic(CVE-2017-10271)与 Struts2(s2-045) 反序列化漏洞复现
  • 原文地址:https://blog.csdn.net/weixin_59400943/article/details/127659932