操作系统1-计算机系统概述

最近开始系统学习操作系统相关的知识,为了及时作总结,这里会记录整个学习的笔记。学习基于的教材是《深入理解计算机系统第三版》,同时参考南京大学袁春风老师的《计算机系统基础》的视频课。

  1. C语言程序举例
    举一些令人觉得反常的例子。

案例1:建立main.c文件,内容如下

#include 
int main()
{
    /*case 1*/
    printf("%d\n", -2147483648 < 2147483647);

    /*case 2*/
    int i = -2147483648;
    printf("%d\n", i < 2147483647);

    /*case 3*/
    printf("%d\n", -2147483647 - 1 < 2147483647);
    
    return 0;
}

ISO C90标准下,在32位系统上,上面三个C表达式的结果是什么?
我的机器是linux64位的,我现在把main.c编译链接成一个32位的基于C90的可执行文件,输入:

gcc -m32 -std=c90 main.c

编译的时候会有告警:

main.c: In function ‘main’:
main.c:5:5: warning: this decimal constant is unsigned only in ISO C90
     printf("%d\n", -2147483648 < 2147483647);
     ^
main.c:8:5: warning: this decimal constant is unsigned only in ISO C90
     int i = -2147483648;
     ^

运行后结果为:

0
1
1

实际上,上面结果有问题的原因就是告警提示的,要具体理解该问题需要知道:
编译器如何处理字面量;
高级语言中运算规则;
高级语言与指令之间的对应;
机器指令的执行过程;
机器级数据的表示和运算。

案例2:修改main.c文件,内容如下

#include 
int sum (int a[], unsigned len)
{
    int i, sum =0;
    for (i =0;i<=len-1;i++) {
        sum += a[i];
    }
    return sum;
}

int main()
{
        int array[]={1,2,3};
        sum(array,0); 
        return 0;
}

编译运行后结果为:

aitian@aitian-CW65S:~/at/OS/week1$ gcc main.c 
aitian@aitian-CW65S:~/at/OS/week1$ ./a.out 
段错误 (核心已转储)

当参数len为0时,返回值应该是0,但是在机器上执行时,却发生访存异常。但当len为int型时则正常。理解该问题需要知道:
高级语言中运算规则;
机器指令的含义和执行;
计算机内部的运算电路;
异常的检测和处理;
虚拟地址空间。

案例3:修改main.c文件,内容如下

#include

int main()
{
    // case 1
    int x = 65535;
    int y = x*x; 
    printf("y=%d\n", y);
    
    // case 2
    int z = -2147483648;
    int m = -2147483649;
    printf("%d,%d\n",z > m,-z < -m);
    return 0;
}

编译运行后结果为:

aitian@aitian-CW65S:~/at/OS/week1/exam2$ gcc main.c 
main.c: In function ‘main’:
main.c:12:13: warning: overflow in implicit constant conversion [-Woverflow]
     int m = -2147483649;
             ^
aitian@aitian-CW65S:~/at/OS/week1/exam2$ ./a.out 
y=-131071
0,1

若x和y为int型, 当x=65535时, y=x*x; y的值为多少?y= -131071。
现实世界中,x的平方≥0,但在计算机世界并不一定成立。

对于任何int型变量x和y,(x>y) == (-x<-y) 总成立吗?当x=-2147483648,y任意(除-2147483648外)时不成立。
Why?

在现实世界中成立,但在计算机世界中并不一定成立。
理解该问题需要知道:
机器级数据的表示;
机器指令的执行;
计算机内部的运算电路。

案例4:建立建立文件,分别是main.c和p1.c

aitian@aitian-CW65S:~/at/OS/week1/exam3$ cat main.c 
#include 
int d = 100;
int x = 200;
int main()
{
    p1();
    printf("d=%d,x=%d\n",d,x);
    return 0;
}
double d;
void p1()
{
    d=1.0;
}

编译运行后结果为:

aitian@aitian-CW65S:~/at/OS/week1/exam3$ gcc main.c p1.c -o main
main.c: In function ‘main’:
main.c:6:5: warning: implicit declaration of function ‘p1’ [-Wimplicit-function-declaration]
     p1();
     ^
/usr/bin/ld: Warning: alignment 4 of symbol `d' in /tmp/ccMboWJv.o is smaller than 8 in /tmp/ccJppd2b.o
aitian@aitian-CW65S:~/at/OS/week1/exam3$ ./main 
d=0,x=1072693248

理解该问题需要知道:
机器级数据的表示;
数据的大端 变量的存储空间分配;
小端存储方式;
链接器的符号解析规则。

案例5:建立main.c如下

aitian@aitian-CW65S:~/at/OS/week1$ cat main.c 
#include
#include

int copy_array(int * array,int count)
{
    int i;
    printf ("%d\n", count*sizeof(int));
    int*myarray=(int*)malloc(count*sizeof(int));
    if (myarray == NULL) {
        return -1;
    }
    for (i = 0;i< count ;i++) {
        printf ("index = %d\n",i);
        myarray[i] = array[i];
    }
    return count;
}

int main()
{
    int array[4400]={0};
    copy_array(array, 1073741824+1);
    return 0;
}

编译后运行结果为:

aitian@aitian-CW65S:~/at/OS/week1$ gcc main.c 
main.c: In function ‘copy_array’:
main.c:7:13: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long unsigned int’ [-Wformat=]
     printf ("%d\n", count*sizeof(int));
             ^
aitian@aitian-CW65S:~/at/OS/week1$ ./a.out 
4
1
2
3
4
...(省略一些输出)
index = 6854
index = 6855
index = 6856
index = 6857
index = 6858
index = 6859
index = 6860
段错误 (核心已转储)

当参数count很大时,则countsizeof(int)会溢出。如count=2的30次方 +1时,countsizeof(int)=4。从而会导致 堆(heap)中大量数据被破坏!
理解该问题需要知道:
乘法运算及溢出;
虚拟地址空间;
存储空间映射。

案例6:新建main.c如下

#include 

int main()
{
    {
        int a = 0x80000000;
        int b = a/-1;
        printf("result = %d\n", b);
    }

    {
        int a = 0x80000000;
        int b = -1;
        int c = a/b;
        printf("result = %d\n", c);
    }
    return 0;
}

运行编译后结果为:

aitian@aitian-CW65S:~/at/OS/week1/exam5$ gcc -g main.c 
aitian@aitian-CW65S:~/at/OS/week1/exam5$ ./a.out 
result = -2147483648
浮点数例外 (核心已转储)

运行结果为“Floating poin为什么两者结果不同!
后者显示浮点数的异常,显然CPU检测到了溢出异常
objdump反汇编代码,负指令neg,得知除以 被优化成取-1故未发生除法溢出.
a/b用除法指令IDIV实现,但它不生成OF标志,那么如何判断溢出异常的呢?
实际上是“除法错”异常#DE(类型0)Linux中,对#DE类型发SIGFPE信号。

输入objdump -S a.out


333333.png

理解该问题需要知道:
编译器如何优化
机器指令的含义和执行
机器级数据的表示
计算机内部的运算电路
除法错异常的处理

案例7:建立main.c

#include 

int main()
{
    double a = 10;
    printf("a = %d\n", a);
    return 0;
}

先编译成32位机器上运行的程序,然后编译成64位机器上运行的程序

aitian@aitian-CW65S:~/at/OS/week1/exam6$ gcc -m32 main.c 
main.c: In function ‘main’:
main.c:6:12: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘double’ [-Wformat=]
     printf("a = %d\n", a);
            ^
aitian@aitian-CW65S:~/at/OS/week1/exam6$ ./a.out 
a = 0
aitian@aitian-CW65S:~/at/OS/week1/exam6$ gcc main.c 
main.c: In function ‘main’:
main.c:6:12: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘double’ [-Wformat=]
     printf("a = %d\n", a);
            ^
aitian@aitian-CW65S:~/at/OS/week1/exam6$ ./a.out 
a = 1774780312
aitian@aitian-CW65S:~/at/OS/week1/exam6$ ./a.out 
a = -150387448

在IA-32上运行时,打印结果为a=0
在x86-64上运行时,打印出来的a是一个不确定值
IEEE 理解该问题需要知道:
754 的表示;
X87 FPU的体系结构;
IA-32和x86-64中过程;
调用的参数传递;
计算机内部的运算电路。

案例8:建立main.c

#include 

double fun(int i)
{
    volatile double d[1]={3.14};
    volatile long int a[2];
    a[i] = 1073741824;// Possibly out of bounds
    return d[0];
}

int main()
{
    for (int i =0;i<=5;i++) {
        printf("fun(%d)->%lf\n", i, fun(i));
    }
    return 0;
}

编译运行后:

aitian@aitian-CW65S:~/at/OS/week1/exam7$ gcc main.c 
aitian@aitian-CW65S:~/at/OS/week1/exam7$ ./a.out 
fun(0)->3.140000
fun(1)->3.140000
fun(2)->3.140000
*** stack smashing detected ***: ./a.out terminated
已放弃 (核心已转储)

理解该问题需要知道:
机器级数据的表示;
过程调用机制;
栈帧中数据的布局。

案例9:建立main.c

#include
#include 
#define NUM 5000 
#define COUNT 20

void copyij (int src[NUM][NUM],int dst[NUM][NUM])
{
        int i,j;
        for (i = 0; i < NUM; i++)
                for ( j = 0; j < NUM; j++)
                        dst[i][j] = src[i][j];
}

void copyji (int src[NUM][NUM],int dst[NUM][NUM])
{
        int i,j;
        for ( j = 0; j < NUM; j++)
                for (i = 0; i < NUM; i++)
                        dst[i][j] = src[i][j];
}

int main()
{
    int* src = (int*)malloc(NUM*sizeof(int));
    for (int i = 0;i < NUM;i++) {
        src[i] = (int*)malloc(NUM*sizeof(int));
    }

    int* dst = (int*)malloc(NUM*sizeof(int));
    for (int i = 0;i < NUM;i++) {
        dst[i] = (int*)malloc(NUM*sizeof(int));
    }

    time_t t1 = time(NULL);
    for (int i =0;i < COUNT;i++) {
        copyij(src,dst);
    }
    time_t t2 = time(NULL); 
    printf("copyij cost %lds\n",t2-t1);

   time_t t3 = time(NULL); 
   for (int i =0;i < COUNT;i++) {
       copyji(src,dst);
   }
   time_t t4 = time(NULL); 
   printf("copyji cost %lds\n",t4-t3);
    return 0;
}

编译运行后:

aitian@aitian-CW65S:~/at/OS/week1/exam8$ gcc main.c 
aitian@aitian-CW65S:~/at/OS/week1/exam8$ ./a.out 
copyij cost 1s
copyji cost 12s

以上两个程序功能完全一样,算法完全一样,因此,时间和空间复杂度完全一样,执行时间一样吗?
空间复杂度一样,但是第一个子函数比第二个子函数快很多倍。

理解该问题需要知道:
数组的存放方式
Cache机制
访问局部性

案例10:


333.png

这个要用老的gcc去编译,我没有下载对应的版本。

案例11:建立main.c

#include 
void main()
{
    int a = 10;
    double *p=(double*)&a;
    printf("%lf\n", *p);
    printf("%lf\n", (double)a);
}

运行后结果为:

aitian@aitian-CW65S:~/at/OS/week1/exam9$ ./a.out 
-0.000000
10.000000

理解该问题需要知道:
数据的表示
编译(程序的转换)
局部变量在栈中的位置

使用gdb进入a.out,然后查看汇编

aitian@aitian-CW65S:~/at/OS/week1/exam9$ gdb a.out 
(gdb) disassemble /m main
Dump of assembler code for function main:
3   {
   0x0000000000400596 <+0>: push   %rbp
   0x0000000000400597 <+1>: mov    %rsp,%rbp
   0x000000000040059a <+4>: sub    $0x30,%rsp
=> 0x000000000040059e <+8>: mov    %fs:0x28,%rax
   0x00000000004005a7 <+17>:    mov    %rax,-0x8(%rbp)
   0x00000000004005ab <+21>:    xor    %eax,%eax

4       int a = 10;
   0x00000000004005ad <+23>:    movl   $0xa,-0x14(%rbp)

5       double *p=(double*)&a;
   0x00000000004005b4 <+30>:    lea    -0x14(%rbp),%rax
   0x00000000004005b8 <+34>:    mov    %rax,-0x10(%rbp)

6       printf("%lf\n", *p);
   0x00000000004005bc <+38>:    mov    -0x10(%rbp),%rax
   0x00000000004005c0 <+42>:    mov    (%rax),%rax
   0x00000000004005c3 <+45>:    mov    %rax,-0x28(%rbp)
   0x00000000004005c7 <+49>:    movsd  -0x28(%rbp),%xmm0
   0x00000000004005cc <+54>:    mov    $0x400694,%edi
   0x00000000004005d1 <+59>:    mov    $0x1,%eax
   0x00000000004005d6 <+64>:    callq  0x400470 

7       printf("%lf\n", (double)a);
   0x00000000004005db <+69>:    mov    -0x14(%rbp),%eax
   0x00000000004005de <+72>:    pxor   %xmm0,%xmm0
   0x00000000004005e2 <+76>:    cvtsi2sd %eax,%xmm0
   0x00000000004005e6 <+80>:    mov    $0x400694,%edi
   0x00000000004005eb <+85>:    mov    $0x1,%eax
   0x00000000004005f0 <+90>:    callq  0x400470 
...(后面的省略) 

关键在于命令movsd和命令cvtsi2sd的区别。
movsd:传送一个双字
cvtsi2sd命令把源存储器1个双字有符号整数变成1个双精度浮点数,结果送入目的寄存器的低64位,高64位不变.

  1. 什么是计算机系统


    333.png
11111.png
  1. 计算机系统组成与基本功能
    3.1 现代计算机的原型:冯·诺依曼结构


    222.png
444.png
555.png
5555555555.png

3.2 现代计算机结构模型


77777.png
888.png

3.3 计算机是如何工作的


999.png
111111.png
1.png

4.程序开发和执行过程简介

4.1 程序开发语言


1.png
1.png
1.png
1.png
1.png

4.2 程序转换


1.png
1.png
1.png
1.png
  1. 编程语言和计算机体系层次


    1.png
1.png
1.png
1.png
1.png
1.png

你可能感兴趣的:(操作系统1-计算机系统概述)