结构_结构体 - CSDN
  • C语言中的结构体(struct)

    万次阅读 多人点赞 2016-02-15 20:23:39
    C语言中,结构体类型属于一种构造类型(其他的构造类型还有:数组类型,联合类型)。本文主要介绍关于结构体以下几部分。 1、概念为什么要有结构体?因为在实际问题中,一组数据往往有很多种不同的数据类型。...

    C语言中,结构体类型属于一种构造类型(其他的构造类型还有:数组类型,联合类型)。本文主要介绍关于结构体以下几部分。
    这里写图片描述

    1、概念

    为什么要有结构体?

    因为在实际问题中,一组数据往往有很多种不同的数据类型。例如,登记学生的信息,可能需要用到 char型的姓名,int型或 char型的学号,int型的年龄,char型的性别,float型的成绩。又例如,对于记录一本书,需要 char型的书名,char型的作者名,float型的价格。在这些情况下,使用简单的基本数据类型甚至是数组都是很困难的。而结构体(类似Pascal中的“记录”),则可以有效的解决这个问题。
    结构体本质上还是一种数据类型,但它可以包括若干个“成员”,每个成员的类型可以相同也可以不同,也可以是基本数据类型或者又是一个构造类型。
    结构体的优点:结构体不仅可以记录不同类型的数据,而且使得数据结构是“高内聚,低耦合”的,更利于程序的阅读理解和移植,而且结构体的存储方式可以提高CPU对内存的访问速度。

    结构声明(structure declaration)

    结构声明(也见有称做定义一个结构体)是描述结构如何组合的主要方法。
    一般形式是:
    struct 结构名{
    成员列表
    };
    struct关键词表示接下来是一个结构。
    如声明一个学生的结构:

    struct Student{         //声明结构体
        char name[20];      //姓名
        int num;            //学号
        float score;        //成绩
    };

    上面的声明描述了一个包含三个不同类型的成员的结构,但它还没创建一个实际的数据对象,类似C++中的模板。每个成员变量都用自己的声明来描述,以分号结束。花括号之后的分号表示结构声明结束。结构声明可以放在函数外(此时为全局结构体,类似全局变量,在它之后声明的所有函数都可以使用),也可以放在函数内(此时为局部结构体,类似局部变量,只能放在该函数内使用,如果与全局结构体同名,则会暂时屏蔽全局结构体)。

    要定义结构变量,则一般形式是:
    struct 结构体名 结构体变量名;
    如:

    struct Student stu1;    //定义结构体变量

    这里写图片描述
    1)、结构体变量的定义可以放在结构体的声明之后:

    struct Student{         //声明结构体
        char name[20];      //姓名
        int num;            //学号
        float score;        //成绩
    };
    struct Student stu1;    //定义结构体变量

    2)、结构体变量的定义也可以与结构体的声明同时,这样就简化了代码:

    struct Student{        
        char name[20];       
        int num;             
        float score;         
    }stu1;                  //在定义之后跟变量名

    3)、还可以使用匿名结构体来定义结构体变量:

    struct {                //没有结构名
        char name[20];       
        int num;            
        float score;         
    }stu1;  
    

    但要注意的是这样的方式虽然简单,但不能再次定义新的结构体变量了。

    访问结构成员

    虽然结构类似一个数组,只是数组元素的数据类型是相同的,而结构中元素的数据类型是可以不同的。但结构不能像数组那样使用下标去访问其中的各个元素,而应该用结构成员运算符点(.)。即访问成员的一般形式是:
    结构变量名 . 成员名
    如 stu1 . name 表示学生stu1的姓名。

    但如果结构体中的成员又是一个结构体,如:

    struct Birthday{                //声明结构体 Birthday
        int year;
        int month;
        int day;
    };
    struct Student{                 //声明结构体 Student
        char name[20];              
        int num;                    
        float score;                 
        struct Birthday birthday;   //生日
    }stu1;

    则用 stu1.birthday.year 访问出生的年份。

    结构体变量的初始化

    1)、结构体变量的初始化可以放在定义之后:

    可以对结构体的成员逐个赋值:

    struct Student stu1, stu2;      //定义结构体变量
    strcpy(stu1.name, "Jack");
    stu1.num = 18;
    stu1.score = 90.5;

    注意:不能直接给数组名赋值,因为数组名是一个常量。如:

    stu1.name = "Jack"; //…main.c:26:15: Array type 'char [20]' is not assignable

    或者可以对结构体进行整体赋值:

    stu2 = (struct Student){"Tom", 15, 88.0};

    注意:此时要进行强制类型转换,因为数组赋值也是使用{},不转换的话系统无法区分!如:

    int arr[5] = {1, 2, 3, 4, 5};       //数组的初始化
    stu2 = {"Tom", 15, 88.0};           //…main.c:31:12: Expected expression

    2)、结构体变量的初始化也可以与定义同时:

    struct Student{                 //声明结构体 Student
        char name[20];               
        int num;                    
        float score;                 
    }stu = {"Mike", 15, 91};        //注意初始化值的类型和顺序要与结构体声明时成员的类型和顺序一致

    此时不需要强制类型转换
    也可以部分初始化:

    struct Student stu4 = {.name = "Lisa"};

    也可以按照任意的顺序使用指定初始化项目:

        struct Student st = { .name = "Smith",
                              .score = 90.5,
                              .num = 18 };

    3)、可以用一个已经存在的结构体去初始化一个新的相同类型的结构体变量,是整体的拷贝(每一个成员都一一赋值给新的结构体变量),而不是地址赋值。如:

    stu3 = stu1;
    printf("stu1 addr: %p\nstu3 addr: %p\n", &stu1, &stu3);
    printf("stu1.num: %d\nstu3.num: %d\n", stu1.num, stu3.num);
    printf("stu1.num addr: %p\nstu3.num addr: %p\n", &stu1.num, &stu3.num);
    //输出结果:
    stu1 addr: 0x10000104c
    stu3 addr: 0x100001084
    stu1.num: 18
    stu3.num: 18
    stu1.num addr: 0x100001060
    stu3.num addr: 0x100001098

    2、结构体变量的存储原理

    1)结构体数据成员对齐的意义

    内存是以字节为单位编号的,某些硬件平台对特定类型的数据的内存要求从特定的地址开始,如果数据的存放不符合其平台的要求,就会影响到访问效率。所以在内存中各类型的数据按照一定的规则在内存中存放,就是对齐问题。而结构体所占用的内存空间就是每个成员对齐后存放时所占用的字节数之和。
    计算机系统对基本数据类型的数据在内存中存放的限制是:这些数据的起始地址的值要求是某个数K的倍数,这就是内存对齐,而这个数 K 就是该数据类型的对齐模数(alignment modulus)。这样做的目的是为了简化处理器与内存之间传输系统的设计,并且能提升读取数据的速度。
    结构体对齐不仅包括其各成员的内存对齐(即相对结构体的起始位置),还包括结构体的总长度。

    2)结构体大小的计算方法和步骤
    i. 将结构体内所有数据成员的长度值相加,记为 sum_a ;
    ii. 将各数据成员为了内存对齐,按各自对齐模数而填充的字节数累加到sum_a上,记为sum_b。
    对齐模数是 #pragma pack 指定的数值与该数据成员自身长度相比较得到的数值较小者。该数据相对起始位置应该是对齐模数的整数倍。
    iii. 将和 sum_b 向结构体模数对齐。
    该模数则是 #pragma pack 指定的数值与结构体内最大的基本数据类型成员长度相比较得到的数值较小者。结构体的长度应该是该模数的整数倍。

    数据类型自身对齐:
    这里写图片描述
    所谓“对齐在N上”,是指“存放的起始位置是%N = 0”.

    3)在没有#pragma pack宏的情况下:
    例子1:

    这里写图片描述

    内存分配状态为:

    这里写图片描述

    对于结构体的第一个成员 a,起始位置为0x…38 (也为 4 的倍数),所占内存为 0x…38 ~ 0x…3b,共占4个字节;
    对于结构体的第二个成员 b,自身长度为1,对齐模数也为1,所以内存分配可以紧接着a的结尾位置 0x…3b,所以起始位置为 0x…3c,共占1个字节;
    对于结构体的第三个成员 c,自身长度为2,对齐模数也为2,所以起始位置距离a 的起始位置应该是2的倍数,所以 0x…3d处只距离5,不符合要求,所以空着,继续往下找,而在 0x…3e处满足要求,所以可以作为c的起始位置,共占2个字节;
    此时3个成员及其中间空着的位置所占内存单元总和为8,而结构体内最大的基本数据成员是 a,其长度为4,所以结构体模数为 4,而8是4的倍数,满足要求,故不再加内存。

    例子2:
    与例子1相比,三个类型的声明顺序变了:

    这里写图片描述

    内存分配状态为:
    这里写图片描述

    要注意的是,对 a而言,对齐模数为 4,所以当 b的起始位置在0x7f…830之后,0x7f…831、0x7f…832、0x7f…833的位置距离起始位置0x7f…830分别是1,2,3,都不是 4 的倍数,所以那三个位置都空着,直到0x7f…834才满足要求,所以作为 a 的起始位置。当最后 一个成员 c 占的内存末尾在0x7f…839时,所有数据成员及其之间的空位所占内存单元总和为10,而结构体模数为4,10不是4的倍数,所以要扩大到12才满足要求,此时又多了2个空位置,就是0x7f…83a和0x7f…83b。

    例子3:
    当结构体中有数组时:

    这里写图片描述

    内存分配状态为:
    这里写图片描述

    亦即相同类型数据的数组之间多分配的空间会被相邻数组的元素所占用。

    4)在存在#pragma pack宏的情况下:
    方法类似,只是模数可能会按上面说的规则而有所变化。

    这里写图片描述

    内存分配状态为:
    这里写图片描述

    注意,当没有#pragma pack(2)时,成员a要确定自身的q起始位置,是以自身的长度4为对齐模数,但有了#pragma pack(2),则将括号里的2与a的长度4比较,2为较小者,所以以2为a的对齐模数,即地址从0x7f…839往下找到0x7f…83a时,已经距离结构体的起始位置0x7f…838为2,是2的倍数,满足要求(虽然不是4的倍数),可以作为a的起始位置。而最后,所有数据成员及其之间的空位所占内存单元总和为8,因为2和4(结构体中最大的数据成员长度)的较小者为2,而8是2的倍数,所以刚好满足要求,不用在分配空位置,所以结构体总长度即为8。

    3、结构体数组

    结构类型作为一种数据类型,也可以像基本数据类型那样,作为数组的元素的类型。元素属于结构类型的数组成为结构型数组。如开头提出的问题,生活中经常用到结构数组来表示具有相同数据结构的一个群体,如一个班的学生的信息,一个书店或图书馆的书籍信息等。

    1)结构数组定义
    一般格式:
    struct 结构名 {
    成员列表
    } 数组名[数组长度];
    如:

    struct Student{                 //声明结构体 Student
        char name[20];
        int num;
        float score;
    }stu[5];                        //定义一个结构结构数组stu,共有5个元素

    2)结构数组的初始化

    定义结构数组的同时进行初始化

    struct Student stu[2] = {{"Mike", 27, 91},{"Tom", 15, 88.0}};  

    先定义,后初始化
    整体赋值:

    stu[2] = (struct Student){"Jack", 12, 85.0};

    或者将结构体变量的成员逐个赋值:

    strcpy(stu[3].name, "Smith");
    stu[3].num = 18;
    stu[3].score = 90.5;

    输出结构体:

    //结构体数组的长度:
    int length = sizeof(stu) / sizeof(struct Student);
    //逐个输出结构数组的元素
    for (int i = 0; i < length; i++) {
        printf("姓名:%s  学号:%d  成绩:%f \n", stu[i].name, stu[i].num, stu[i].score);
    }

    //输出结果:
    这里写图片描述

    在这个例子中,要注意的是:
    这里写图片描述

    4、结构与指针

    当一个指针变量用来指向了一个结构变量,这个指针就成了结构指针变量。
    结构指针变量中的值是所指向的结构变量的首地址。可以通过指针来访问结构变量。

    1)定义结构指针变量的一般形式:
    struct 结构名 * 结构指针变量名
    如:

    struct Student *pstu;       //定义了一个指针变量,它只能指向Student结构体类型的结构体变量

    结构指针变量的定义也可以与结构体的定义同时。而且它必须先赋值后使用。
    数组名表示的是数组的首地址,可以直接赋值给数组指针。但结构变量名只是表示整个结构体变量,不表示结构体变量的首地址,所以不能直接赋值给结构指针变量,而应该使用 & 运算符把结构变量的的地址赋值给结构指针变量。即:

    这里写图片描述

    注意:结构名、结构变量名、结构体指针的区别。

    2)通过结构指针间接访问成员值

    访问的一般形式:
    (*结构指针变量). 成员名 或 结构指针变量 -> 成员名
    如:

    (*pstu).name    
    pstu->name

    注意(pstu)的小括号不能省略,因为成员符“.”优先级为1,取地址符“”优先级为2,去掉括号就相当于*(pstu.name)了。

    5、结构体的嵌套

    1)结构体中的成员可以又是一个结构体,构成结构体的嵌套:

    struct Birthday{                //声明结构体 Birthday
        int year;
        int month;
        int day;
    };
    struct Student{                 //声明结构体 Student
        char name[20];              
        int num;                    
        float score;                 
        struct Birthday birthday;   //生日
    }; 
    

    这里写图片描述
    2)结构体不可以嵌套跟自己类型相同的结构体,但可以嵌套定义自己的指针。如:

    struct Student{                 //声明结构体 Student
        char name[20];
        int num;
        float score;
        struct Student *friend;     //嵌套定义自己的指针
    }

    3)甚至可以多层嵌套:

    struct Time{                    //声明结构体 Time
        int hh;                     //时
        int mm;                     //分
        int ss;                     //秒
    };
    struct Birthday{                //声明结构体 Birthday
        int year;
        int month;
        int day;
        struct Time dateTime        //嵌套结构
    };
    struct Student{                 //声明结构体 Student
        char name[20];
        int num;
        float score;
        struct Birthday birthday;   //嵌套结构
    }
    //定义并初始化
    struct Student stud = {"Jack", 32, 85, {1990, 12, 3, {12, 43, 23}}};
    //访问嵌套结构的成员并输出
    printf("%s 的出生时刻:%d时 \n", stud.name, stud.birthday.dateTime.hh);
    //输出结果:Jack 的出生时刻:12时 

    注意如何初始化和对嵌套结构的成员进行访问。

    6、结构与函数

    结构体的成员可以作为函数的参数,属于值传递(成员是数组的除外)。如:

    struct Student{                 //声明结构体 Student
        char name[20];
        int num;
        float score;
    };
    void printNum(int num){         //定义一个函数,输出学号
        printf("num = %d \n", num);
    }
        struct Student student0 = {"Mike", 27, 91};
        printNum(student0.num);     //调用printNum 函数,以结构成员作函数的参数
    //运行结果:num = 27 

    注意,函数printNum并不知道也不关心实际参数是不是结构成员,它只要求实参是int类型的就可以了。

    结构变量名也可以作为函数的参数传递,如:

    void PrintStu(struct Student student){      //定义 PrintStu 函数,以结构变量作函数的形参
        student.num = 100;                      //修改学号
        printf("PrintStu 修改后:姓名: %s, 学号: %d, 内存地址: %p \n", student.name, student.num, &student);
    }
        struct Student student0 = {"Mike", 27, 91};
        PrintStu(student0);                     //调用 PrintStu 函数,以结构变量名作函数的参数
        printf("           原来:姓名: %s, 学号: %d,  内存地址: %p \n", student0.name, student0.num, &student0);
    

    //输出结果:
    这里写图片描述

    形参和实参的地址不一样,是在函数中创建了一个局部结构体,然后实参对形参进行全部成员的逐个传送,在函数中对局部结构体变量进行修改并不影响原结构体变量。这样传送的时间空间开销都比较大,特别是当成员有数组的时候,程序效率较低。所以可以考虑使用指针:

    void PrintStu2(struct Student *student){      //定义 PrintStu2 函数,以结构指针作函数的形参
        student->num = 100;                       //修改学号
        printf("PrintStu2 修改后:姓名: %s, 学号: %d, 内存地址: %p \n", student->name, student->num, student);
    }
        struct Student student0 = {"Mike", 27, 91};
        PrintStu2(&student0);                     //调用 PrintStu 函数,以结构变量的地址作函数的参数
        printf("           原来:姓名: %s, 学号: %d,  内存地址: %p \n", student0.name, student0.num, &student0);

    //输出结果:
    这里写图片描述

    形参和实参的地址是一样的,所以是地址传递,在 PrintStu2 函数中,student 与&student0 指向同一块内存单元,用指针student修改结构变量会影响原结构变量。

    展开全文
  • C语言的四种程序结构

    万次阅读 2018-01-02 12:13:59
    1、顺序结构 顺序结构的程序设计是最简单的,只要按照解决问题的顺序写出相应的语句就行,它的执行顺序是自上而下,依次执行。例如;a = 3,b = 5,现交换a,b的值,这个问题就好像交换两个杯子水,这当然要用到第...

    1、顺序结构
    顺序结构的程序设计是最简单的,只要按照解决问题的顺序写出相应的语句就行,它的执行顺序是自上而下,依次执行。

    例如;a = 3,b = 5,现交换a,b的值,这个问题就好像交换两个杯子水,这当然要用到第三个杯子,假如第三个杯子是c,那么正确的程序为: c = a; a = b; b = c; 执行结果是a = 5,b = c = 3。

    如果改变其顺序,写成:a = b; c = a; b = c; 则执行结果就变成a = b = c = 5,不能达到预期的目的,这是我们初学者最容易犯这种错误,所以一定要多加注意。

    顺序结构可以独立使用构成一个简单的完整程序,常见的输入、计算,输出三步曲的程序就是顺序结构,例如计算圆的面积,其程序的语句顺序就是输入圆的半径r,计算s = 3.14159*r*r,输出圆的面积s。

    不过大多数情况下顺序结构都是作为程序的一部分,与其它结构一起构成一个复杂的程序,例如分支结构中的复合语句、循环结构中的循环体等

    这里写图片描述

    2、分支结构
    顺序结构的程序虽然能解决计算、输出等问题,但不能做判断再选择。对于要先做判断再选择的问题就要使用分支结构。

    分支结构的执行是依据一定的条件选择执行路径,而不是严格按照语句出现的物理顺序。分支结构的程序设计方法的关键在于构造合适的分支条件和分析程序流程,根据不同的程序流程选择适当的分支语句。

    分支结构适合于带有逻辑或关系比较等条件判断的计算,设计这类程序时往往都要先绘制其程序流程图,然后根据程序流程写出源程序,这样做把程序设计分析与语言分开,使得问题简单化,易于理解。

    程序流程图是根据解题分析所绘制的程序执行流程图。

    这里写图片描述

    学习分支结构不要被分支嵌套所迷惑,只要正确绘制出流程图,弄清各分支所要执行的功能,嵌套结构也就不难了。嵌套只不过是分支中又包括分支语句而已,不是新知识,只要对双分支的理解清楚,分支嵌套是不难的。下面我介绍几种基本的分支结构。

    ①if(条件)
    {
    分支体
    }

    这种分支结构中的分支体可以是一条语句,此时“{ }”可以省略,也可以是多条语句即复合语句。

    它有两条分支路径可选,一是当条件为真,执行分支体,否则跳过分支体,这时分支体就不会执行。如:要计算x的绝对值,根据绝对值定义,我们知道,当x>=0时,其绝对值不变,而x<0时其绝对值是为x的反号,因此程序段为:if(x<0) x=-x;

    ②if(条件)
    {分支1}
    else
    {分支2}

    这里写图片描述

    这是典型的分支结构,如果条件成立,执行分支1,否则执行分支2,分支1和分支2都可以是1条或若干条语句构成。如:求ax^2+bx+c=0的根

    分析:因为当b^2-4ac>=0时,方程有两个实根,否则(b^2-4ac<0)有两个共轭复根。其程序段如下:

    d=b*b-4*a*c; 
    if(d>=0) 
    {x1=(-b+sqrt(d))/2a; 
    x1=(-b-sqrt(d))/2a; 
    printf("x1=%8.4f,x2=%8.4f\n",x1,x2); 
    } 
    else 
    {r=-b/(2*a); 
    i =sqrt(-d)/(2*a); 
    printf("x1=%8.4f+%8.4fi\n"r, i); 
    printf("x2=%8.4f-%8.4fi\n"r,i); 
    } 

    ③嵌套分支语句:其语句格式为:
    if(条件1) {分支1};
    else if(条件2) {分支2}
    else if(条件3) {分支3}
    ……
    else if(条件n) {分支n}
    else {分支n+1}

    嵌套分支语句虽可解决多个入口和出口的问题,但超过3重嵌套后,语句结构变得非常复杂,对于程序的阅读和理解都极为不便,建议嵌套在3重以内,超过3重可以用下面的语句。

    ④switch开关语句:该语句也是多分支选择语句,到底执行哪一块,取决于开关设置,也就是表达式的值与常量表达式相匹配的那一路。

    它不同if…else 语句,它的所有分支都是并列的,程序执行时,由第一分支开始查找,如果相匹配,执行其后的块,接着执行第2分支,第3分支……的块,直到遇到break语句;如果不匹配,查找下一个分支是否匹配。

    这个语句在应用时要特别注意开关条件的合理设置以及break语句的合理应用。

    3、循环结构

    循环结构可以减少源程序重复书写的工作量,用来描述重复执行某段算法的问题,这是程序设计中最能发挥计算机特长的程序结构,C语言中提供四种循环,即goto循环、while循环、do –while循环和for循环。

    四种循环可以用来处理同一问题,一般情况下它们可以互相代替换,但一般不提倡用goto循环,因为强制改变程序的顺序经常会给程序的运行带来不可预料的错误,在学习中我们主要学习while、do…while、for三种循环。

    常用的三种循环结构学习的重点在于弄清它们相同与不同之处,以便在不同场合下使用,这就要清楚三种循环的格式和执行顺序,将每种循环的流程图理解透彻后就会明白如何替换使用。

    如把while循环的例题,用for语句重新编写一个程序,这样能更好地理解它们的作用。特别要注意在循环体内应包含趋于结束的语句(即循环变量值的改变),否则就可能成了一个死循环,这是初学者的一个常见错误。

    这里写图片描述

    在学完这三个循环后,应明确它们的异同点:用while和do…while循环时,循环变量的初始化的操作应在循环体之前,而for循环一般在语句1中进行的;

    while 循环和for循环都是先判断表达式,后执行循环体,而do…while循环是先执行循环体后判断表达式,也就是说do…while的循环体最少被执行一次,而while 循环和for就可能一次都不执行。

    另外还要注意的是这三种循环都可以用break语句跳出循环,用continue语句结束本次循环,而goto语句与if构成的循环,是不能用break和 continue语句进行控制的。

    顺序结构、分支结构和循环结构并不彼此孤立的,在循环中可以有分支、顺序结构,分支中也可以有循环、顺序结构,其实不管哪种结构,我们均可广义的把它们看成一个语句。

    这里写图片描述
    4、模块化程序结构

    C语言的模块化程序结构用函数来实现,即将复杂的C程序分为若干模块,每个模块都编写成一个C函数,然后通过主函数调用函数及函数调用函数来实现一大型问题的C程序编写。

    因此常说:C程序=主函数+子函数。 因此,对函数的定义、调用、值的返回等中要尤其注重理解和应用,并通过上机调试加以巩固。

    展开全文
  • 操作系统结构

    千次阅读 2018-07-13 12:58:45
    操作系统的内部的六种不同的结构设计:单体系统、层次系统、微内核、客户机-服务器系统、虚拟机和exokernels。 一、单体系统 二、层次式系统 三、微内核 四、客户机-服务器模式 五、虚拟机 六、外核...

    操作系统的内部的六种不同的结构设计:单体系统、层次系统、微内核、客户机-服务器系统、虚拟机和exokernels。

    一、单体系统

    在多数常见的组织形式的处理方式中,全部操作系统在内核态中以单一程序的方式运行。整个操作系统以过程集合的方式编写,链接成一个大型可执行二进制程序。
    在单体系统中,也可能有一些结构存在。可以将参数放置在良好定义的位置,通过这种方式,向操作系统请求所能提供的服务(系统调用),然后执行一个陷阱指令,这个指令将机器从用户态切换到内核态并把控制传递给操作系统,然后操作系统取出参数并且确定应该执行哪个系统调用,随后在一个表格中检索,在该表格的k槽中存放着指向执行系统调用k过程的指针。
    对于这类操作系统的基本结构:
    1. 需要一个主程序,用来处理服务过程请求。
    2. 需要一套服务过程,用来执行系统调用。
    3. 需要一套实用过程,用来辅助服务过程。
    在该模型中,每一个系统调用都通过一个服务过程为其工作并运行之。要有一组实用程序来完成一些服务过程所需要用到的功能。
    简单的单体系统结构图示:
    简单的单体系统结构

    二、层次式系统

    单体系统进一步通用化,就变成一个层次式结构的操作系统,它的上层软件都是在下一层软件的基础之上构建的。THE系统是按此模型构造的第一个操作系统。
    THE系统共分为六层。
    0. 处理器分配和多道程序设计
    处理器分配分配在第0层中进行,当中断发生或定时器到期时,由该层进行进程切换。在第0层之上,系统由一些连续的进程所组成,编写这些进程时不用再考虑在单处理器上多进程运行的细节。也就是说,在第0层中提供了基本的CPU多道程序功能。
    1. 存储器和磁鼓管理
    内存管理在第1层中进行,它分配进程的主存空间,当内存用完时则在一个512k字的磁鼓上保留进程的一部分(页面),在第1层上,进程不用考虑它是在磁鼓上还是在内存中进行。第1层软件保证一旦需要访问某一个页面时,该页面必定已在内存中。
    2. 操作员-进程通信
    第2层处理进程与操作员控制台(即用户)之间的通信。在这层的上不,可以认为每个进程都有自己的操作员控制台。
    3. 输入/输出管理
    第3层管理I/O设备和相关的信息流缓冲区。在第3层上,每个进程都与有良好特性的抽象I/O设备打交道,而不必考虑外部设备的物理细节。
    4. 用户程序
    第4层是用户程序层,用户程序不必考虑进程、内存、控制台或I/O设备管理细节。
    5. 操作员
    系统操作员进程位于第5层中。

    MULTICS系统

    该系统由许多的同心环构造而成,而不是采用层次化构造。内层环比外层环有更高的级别。当外环的过程欲调用内环的过程时,它必须执行一条等价于系统调用的TRAP指令。在执行该TRAP指令前,要进行严格的桉树合法性检查。

    三、微内核

    在微内核设计背后的思想是为了实现高可靠性,将操作系统划分成小的、良好定义的模块,只有其中一个模块–微内核–运行在内核态上,其余的模块,由于功能相对弱些,则作为普通用户进程运行。特别地由于把每个设备驱动和文件系统分别作为普通用户,这些模块中的错误虽然会使这些模块崩溃,但是不会使得整系统死机。
    微内核在实时、工业、航空以及军事应用中特别流行。
    MINIX3系统结构图示:
    微内核实例

    再生服务器

    检查其他服务器和驱动器的功能是否正确,一旦检查出一个错误,它自动取代之,无需任何用户的干预。这种方式使得系统具有自修复能力,并且获得了较高的可靠性。
    系统对每个进程的权限有着许多限制。一个与小内核相关联的思想是在内核中的机制与策略分离的原则。一个比较简单的调度算法是,对于每个进程赋予一个优先级,并让内核执行在具有最高优先级进程中可以运行的某个进程。这里机制(在内核中)就是寻找最高优先级的进程并运行之。而策略(赋予进程以优先级)可以由用户态中的进程完成。在这个方式中,机制和策略是分离的,从而使系统内核变得更小。

    四、客户机-服务器模式

    一个微内核思想的略微变体是将进程划分为:
    1. 服务器:每个服务器提供某种服务。
    2. 客户端:使用这些服务。
    这个模式就是所谓的客户机-服务器模式。通常在系统最底层是微内核,这个模式的本质是存在客户端进程和服务器进程。
    在客户端和服务器之间的通信是消息传递。普遍方式是客户端和服务器在不同的计算机上,通过局域网或广域网连接,由于客户端通过发送消息与服务端通信,客户端并不需要知道这些消息是在它们的本地机器上处理,还是通过网络被送到远程机器上处理。
    客户端-服务器模型实例:
    客户端-服务器

    五、虚拟机

    1. VM/370

    最初命名为CP/CMS,后来改名为VM/370,它的目的是为了将多道程序和获取更方便的裸机彻底隔离。它的核心称为虚拟机监控程序,它在裸机上运行并且具备了多道程序功能。该系统向上层提供了若干个虚拟机。它不同于其他操作系统:这些虚拟机不是那种具有文件等优良特征的扩展计算机。它们仅仅是裸机硬件的精确复制品,包含了内核态/用户态、I/O功能、中断及其他真是硬件所具备的全部内容。

    2. 虚拟机的再次发现

    虚拟化的优点:
    1. 一台物理机就可以运行许多虚拟机,每个虚拟机看起来都是一台完全的机器。这样可以节省费用。
    2. 可以同时运行两个或多个操作系统。

    3. Java虚拟机

    它是一种体系结构。Java编译器为JVM生成代码,这些代码以后可以由一个软件JVM解释器质性。这种处理方式的优点在于,JVM代码可以通过Internet传送到任何有JVM解释器的机器上,并在该机器执行。使用JVM的优点就是如果解释器正确地完成,并不意味着结束,还要对所输入的KVM进行安全性检测,然后在一直保护环境下执行,这样,这些程序就不能偷窃数据或进行其他任何有害的操作。

    六、外核

    与虚拟机克隆真实机器不同,另一种策略是对机器进行分区。给每个用户整个资源的一个子集。
    在底层中,一种称为外核的程序在内核态中运行。它的任务是为虚拟机分配资源,并检查试图使用这些资源的企图,以确保没有机器会使用他人资源。每个用户层的虚拟机可以运行自己的操作系统,但限制在只能使用已经申请并且获得分配的那部分资源。
    外核机制的优点是减少了映像层。在其他的设计中,每个虚拟机都认为它有的磁盘,其盘块号从0到最大编号,这样虚拟机监控程序必须维护一张表格用以重映像磁盘地址,有了外核这个重映像处理就不需要了,外核只需要记录已经分配各个虚拟机的有关资源即可。这个方法还有一个优点,它将多道程序(在外核内)与用户操作系统代码(在用户控件内)加以分离,而且相应负载并不重,这是因为外核所做的一切,只是保持多个虚拟机彼此不发生冲突。

    展开全文
  • 普林斯顿结构 VS 哈佛结构

    万次阅读 2012-08-01 17:39:10
    1. 冯·诺依曼结构  冯·诺依曼结构,又称为普林斯顿体系结构,是一种将程序指令存储器和数据存储器合并在一起的存储器结构。取指令和取操作数都在同一总线上,通过分时复用的方式进行;缺点是在高速运行时,不...

    1. 冯·诺依曼结构

        冯·诺依曼结构,又称为普林斯顿体系结构,是一种将程序指令存储器数据存储器合并在一起的存储器结构。取指令和取操作数都在同一总线上,通过分时复用的方式进行;缺点是在高速运行时,不能达到同时取指令和取操作数,从而形成了传输过程的瓶颈。由于程序指令存储地址和数据存储地址指向同一个存储器的不同物理位置,因此程序指令和数据的宽度相同,如英特尔公司的8086中央处理器的程序指令和数据都是16位宽。


        目前使用冯·诺依曼结构的CPU和微控制器有很多。其中包括英特尔公司的8086及其他CPU,TI的MSP430处理器,ARM公司的ARM7,MIPS公司的MIPS处理器。

     

    2. 哈佛结构

        哈佛结构是一种将程序指令存储和数据存储分开的存储器结构,它的主要特点是将程序和数据存储在不同的存储空间中,即程序存储器和数据存储器是两个独立的存储器,每个存储器独立编址、独立访问,目的是为了减轻程序运行时的访存瓶颈。


        如图,哈佛结构的计算机由CPU、程序存储器和数据存储器组成,程序存储器和数据存储器采用不同的总线,从而提供了较大的存储器带宽,使数据的移动和交换更加方便,尤其提供了较高的数字信号处理性能。

        目前使用哈佛结构的中央处理器和微控制器有很多,除了上面提到的Microchip公司的PIC系列芯片,还有摩托罗拉公司的MC68系列、Zilog公司的Z8系列、ATMEL公司的AVR系列和安谋公司的ARM9、ARM10和ARM11。

     

    3. 总结

        随着CPU设计的发展,流水线的增加,指令和数据的互斥读取影响CPU指令执行的scale程度。哈佛结构中数据存储器与程序代码存储器分开,各自有自己的数据总线与地址总线,取操作数与取指令能同时进行。但这是需要CPU提供大量的数据线,因而很少使用哈佛结构作为CPU外部构架来使用。对于CPU内部,通过使用不同的数据和指令cache,可以有效的提高指令执行的效率,因而目前大部分计算机体系都是在CPU内部的使用哈佛结构,在CPU外部使用冯·诺依曼结构。


    展开全文
  • 哈佛结构/冯诺依曼结构详细分析

    千次阅读 2019-06-16 17:33:56
    CISC与RISC的区别: CISC(复杂指令集):复杂指令集就是CPU在工作的时候需要有很多的汇编指令来完成,它可以用一个汇编指令来完成一件复杂的工作。例如:乘法,加法,乘加,乘减等处理的时候,他会每个处理方式用...
  • 图——基本的图算法(一)图的存储结构 1. 图的表示方法 对于图G=(V, E)来说,可以有两种标准的表示方法,这两种方法都可以表示有向图和无向图: (1)邻接矩阵 (2)邻接链表 ...
  • 数据结构:八大数据结构分类

    万次阅读 多人点赞 2018-09-05 18:23:28
    数据结构分类 数据结构是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成 。 常用的数据结构有:数组,栈,链表,队列,树,图,堆,散列表等,如图所示: 每一种数据结构都...
  • 评论样式结构优化

    热门讨论 2020-01-08 14:51:20
    评论样式结构优化评论样式结构优化评论样式结构优化评论样式结构优化评论样式结构优化评论样式结构优化评论样式结构优化评论样式结构优化评论样式结构优化评论样式结构优化评论样式结构优化评论样式结构优化评论样式...
  • 数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构...
  • 逻辑结构:数据的逻辑结构是对数据之间关系的描述,与存储结构无关,同一种逻辑结构可以有多多种存储结构。 逻辑结构主要分为两大类:线性存储结构和非线性存储结构 线性存储结构是数据元素有序集合,数据结构之间...
  • 数据结构和算法教程导航 数据结构和算法(DSA)教程 数据结构和算法概述 数据结构环境设置 数据结构算法基础 数据结构和算法分析 数据结构贪婪算法 数据结构分而治之 数据结构动态规划 数据...
  • 数据结构的逻辑结构和物理结构

    千次阅读 多人点赞 2017-10-25 14:57:58
    数据结构:指的是数据之间的相互关系,包含三个内容:逻辑结构,存储结构和数据的运算 数据的逻辑结构指数据元素之间的逻辑关系,分两种,线性结构和非线性结构。 常用的线性结构有:线性表,栈,队列,双队列,...
  • 数据结构 试卷1 1 数据结构 试卷2 3 数据结构 试卷3 5 数据结构 试卷4 7 数据结构 试卷5 9 数据结构 试卷6 10 数据结构 试卷7 12 数据结构 试卷8 14 数据结构 试卷9 16 数据结构 试卷10 18 数据结构 试卷...
  • 数据结构数据结构

    2020-07-29 14:20:23
    数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构数据结构
  • 数据结构专栏系列笔记目录 数据结构-基础篇 数据结构绪论 数据结构 时间空间复杂度计算 数据结构 线性表 数据结构 顺序表和链表 数据结构 栈 数据结构 队列 数据结构 字符串 数据结构 矩阵 数据结构 ...
  • joomla表结构介绍

    2020-07-28 23:33:29
    joomla表结构介绍 joomla表结构介绍 joomla表结构介绍 joomla表结构介绍 joomla表结构介绍 joomla表结构介绍 joomla表结构介绍 joomla表结构介绍 joomla表结构介绍 joomla表结构介绍 joomla表结构介绍 joomla表结构...
  • 2019独角兽企业重金招聘Python工程师标准>>> ...
  • 数据结构 严慰敏数据结构 严慰敏数据结构 严慰敏数据结构 严慰敏数据结构 严慰敏数据结构 严慰敏数据结构 严慰敏数据结构 严慰敏数据结构 严慰敏数据结构 严慰敏数据结构 严慰敏数据结构 严慰敏数据结构 严慰敏数据...
  • 1.数据结构 2.逻辑结构分为线性结构和非线性结构 (1)线性结构是有序的数据元素的集合,存在着一对一的关系。 线性结构:线性表、栈、队列、字符串、数组、广义表。 (2)非线性结构每个元素可能与零个或者多个...
  • 今天再看数据结构的书,想把自己理解的线性结构与非线性结构与大家分享一下。线性结构是什么呢?有什么特点呢?线性结构有唯一的首元素(第一个元素)线性结构有唯一的尾元素(最后一个元素)除首元素外,所有的元素...
1 2 3 4 5 ... 20
收藏数 5,195,520
精华内容 2,078,208
关键字:

结构