操作系统实验零——操作系统实验环境准备

前言

最近在学习清华大学操作系统课程,同时在实验楼做实验,打算把实验过程记录下来。算上环境搭建的实验共九个,也就是说这系列共有9篇文章。

贴几张图片:
设计思路
在这里插入图片描述

1、了解汇编

运行 gcc -S -m32 lab0_ex1.c ,生成S汇编语言文件。

  • -S表示仅仅编译,不进行链接或汇编
  • -m32表示生成32位机器的汇编代码

得到lab_ex1.S文件,下面对比理解C文件和S文件(不会):

int count=1;
int value=1;
int buf[10];//三个变量
void main()			
{				
   asm(	
	"cld \n\t"//将标志寄存器Flag的方向标志位DF清零。
        "rep \n\t"//重复前缀指令
        "stosl"//将EAX中的值保存到ES:EDI指向的地址中
	:
	: "c" (count), "a" (value) , "D" (buf[0])
	:
      );
}
	.file	"lab0_ex1.c"
	.globl	count
	.data
	.align 4
	.type	count, @object
	.size	count, 4
count:
	.long	1
	.globl	value
	.align 4
	.type	value, @object
	.size	value, 4
value:
	.long	1
	.comm	buf,40,32
	.text
	.globl	main
	.type	main, @function
main:
.LFB0:
	.cfi_startproc
	pushl	%ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	movl	%esp, %ebp
	.cfi_def_cfa_register 5
	pushl	%edi
	pushl	%ebx
	.cfi_offset 7, -12
	.cfi_offset 3, -16
	movl	count, %edx
	movl	value, %eax
	movl	buf, %ebx
	movl	%edx, %ecx
	movl	%ebx, %edi
#APP
# 6 "lab0_ex1.c" 1
	cld 
	rep 
	stosl
# 0 "" 2
#NO_APP
	popl	%ebx
	.cfi_restore 3
	popl	%edi
	.cfi_restore 7
	popl	%ebp
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.ident	"GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
	.section	.note.GNU-stack,"",@progbits

2、用gdb调试

gcc -g -m32 lab0_ex2.c // -g表示调试
gdb a.out

先用 gdb l 查看代码,发现是helloworld:

1	#include <stdio.h>
2	int
3	main(void)
4	{
5	    printf("Hello, world!\n");
6	    return 0;
7	}(gdb) 

给每一行都加入断点,输入 info breakpoints 查看,有虽然每行都有断点,但从地址上看实际上只有三处有断点:

gdb) info breakpoints
Num     Type           Disp Enb Address    What
1       breakpoint     keep y   0x08048426 in main at lab0_ex2.c:1
	breakpoint already hit 1 time
2       breakpoint     keep y   0x08048426 in main at lab0_ex2.c:2
3       breakpoint     keep y   0x08048426 in main at lab0_ex2.c:3
4       breakpoint     keep y   0x08048426 in main at lab0_ex2.c:4
5       breakpoint     keep y   0x08048426 in main at lab0_ex2.c:5
6       breakpoint     keep y   0x08048432 in main at lab0_ex2.c:6
7       breakpoint     keep y   0x08048437 in main at lab0_ex2.c:7

r (run)从头运行,停止在第一个断点。这里:

  • c (continue)继续运行,直到遇见下一个断点
  • s (step)表示执行下一语句,会进入函数内部,类似VS的 F11
  • n (next)表示执行下一语句,但不会进入函数内部,类似VS的 F10

按下 s 后屏幕输出 hello,world! 停止在了断点6,再按 s 停止在断点7反花括号那里,最后一步程序结束运行。按 q退出。

3、掌握指针和类型转换相关的C编程

实验给出了一个C文件,让debug,我的注释如下:

#include <stdio.h>

#define STS_IG32        0xE            // 32-bit Interrupt Gate
#define STS_TG32        0xF            // 32-bit Trap Gate

typedef unsigned uint32_t;

#define SETGATE(gate, istrap, sel, off, dpl) {            \
    (gate).gd_off_15_0 = (uint32_t)(off) & 0xffff;        \
    (gate).gd_ss = (sel);                                \
    (gate).gd_args = 0;                                    \
    (gate).gd_rsv1 = 0;                                    \
    (gate).gd_type = (istrap) ? STS_TG32 : STS_IG32;    \
    (gate).gd_s = 0;                                    \
    (gate).gd_dpl = (dpl);                                \
    (gate).gd_p = 1;                                    \
    (gate).gd_off_31_16 = (uint32_t)(off) >> 16;        \
}

 /* Gate descriptors for interrupts and traps */
 struct gatedesc {
    unsigned gd_off_15_0 : 16;        // low 16 bits of offset in segment
    unsigned gd_ss : 16;            // segment selector
    unsigned gd_args : 5;            // # args, 0 for interrupt/trap gates
    unsigned gd_rsv1 : 3;            // reserved(should be zero I guess)
    unsigned gd_type : 4;            // type(STS_{TG,IG32,TG32})
    unsigned gd_s : 1;                // must be 0 (system)
    unsigned gd_dpl : 2;            // descriptor(meaning new) privilege level
    unsigned gd_p : 1;                // Present
    unsigned gd_off_31_16 : 16;        // high bits of offset in segment
 }; // 共 64 位
 
int main(void)
{
    unsigned before;
    unsigned intr;
    unsigned after;// unsigned 是 32 位
    struct gatedesc gintr;
    
    intr=8;
    before=after=0;

    gintr=*((struct gatedesc *)&intr);
    /*先取 intr 地址,将其类型转换为指向结构体的地址类型,再引用就获得了一个结构体gintr。
    现在 gintr 的值为:
    gintr = {gd_off_15_0 = 8, gd_ss = 0, gd_args = 0, gd_rsv1 = 0, gd_type = 0, 
  	gd_s = 0, gd_dpl = 0, gd_p = 0, gd_off_31_16 = 0}
    */
    SETGATE(gintr, 0,1,2,3);
    /*改变 gintr 这个结构体的成员变量,现在 gintr 的值为 :
    gintr = {gd_off_15_0 = 2, gd_ss = 1, gd_args = 0, gd_rsv1 = 0, 
  	gd_type = 14, gd_s = 0, gd_dpl = 3, gd_p = 1, gd_off_31_16 = 0}
    */
    intr=*(unsigned *)&(gintr);
    /*
	intr 是 32位 u int 型,gintr 是 64位 结构体类型
	现在 gintr 的后32位 被转换为了 u int型
	由上一步结果知 gintr 的后32位二进制为 0000 0000 0000 0010  0000 0000 0000 0001 ……
	转换为16进制为 0x00010002 转换为10进制为 65538
	*/
    printf("intr is 0x%x\n",intr);
    /*
    这里输出结果为 0x10002 display变量intr的结果也为 65538 验证了我们上一步的推理
    */
    printf("gintr is 0x%llx\n",*(long long unsigned*)&(gintr));//我改后的代码
    // 输出 0xee0000010002
    //printf("gintr is 0x%llx\n",gintr); //题目原来代码,这里有错
    return 0;
}

首先编译报错为:
在这里插入图片描述
题目的意思应该是输出更改后的gintr这个结构体变量的内容 ,参照上面处理方法我把它强制类型转换成llu型: * ( long long unsigned *) & gintr 就OK了。
解释:首先是取gintr这个变量的地址,强制转换为指向llu型变量的指针,再引用这个地址就得到了llu型的变量。
输出结果为:

intr is 0x10002
gintr is 0xee0000010002

这个程序目的应该是用setgate函数修改结构体内容,不知道我这样改对不对。。。
另外注意到 gintr 调试输出的高位为0x00020001 ,高位放在了低地址,说明该机器是大端模式

4、掌握通用链表结构相关的C编程

用在related_info/lab0/list.h中定义的结构和函数来实现一个小应用程序完成一个基于此链表的数据对象的访问操作。 可参考related_info/lab0/lab0_ex4.c

查看lab0_ex4.c代码:

struct list_entry {
    struct list_entry *prev, *next;
};

typedef struct list_entry list_entry_t;
struct entry {
    list_entry_t node;
    int num;
};
int main() {
    struct entry head;
    list_entry_t* p = &head.node;
    list_init(p);
    //p->prev = p-> next = p
    head.num = 0;
    int i;
    for (i = 1; i != 10; i ++) {
        struct entry * e = (struct entry *)malloc(sizeof(struct entry));
        e->num = i;
        list_add(p, &(e->node));
        /* list_add_after ( p , &(e -> node) )  =>>
        	__list_add (&(e -> node ) , p , p -> next) =>>
        	{
				 p->next = p->next->prev = &(e -> node );
   				 &(e -> node )->next = p->next;
    			 &(e -> node )->prev = p;
			}
        */
        p = list_next(p);//p = p->next
    }
    //reverse list all node
    while ((p = list_prev(p)) != &head.node)
        printf("%d\n", ((struct entry *)p)->num);
        //从后往前打印节点的值: 10 9 8 ……
    return 0;
}

分析:在进入循环体前,p是指向自己的头指针。p的结构如下:
在这里插入图片描述
进入到循环体的第一次循环后,新建了一个节点,链接到p。结构如下:
在这里插入图片描述

第一次循环的最后,p = p -> next 也就是 p = e1。第二次循环后结构如图:
在这里插入图片描述
然后 p = e2 结束第二层循环,依此循环10次就完成了双向链表节点的插入。最后从后往前打印p的num的值。
在实验楼的终端上运行报错,就没调试。

总结

没学过x86汇编,但是学过mips,所以第一题不会。指针和数据结构仍有些吃力,继续加油。

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值