操作系统实验零——操作系统实验环境准备
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])
:
);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
.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
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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)
1
2
3
4
5
6
7
给每一行都加入断点,输入 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
1
2
3
4
5
6
7
8
9
10
11
按 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;
}
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
首先编译报错为:
题目的意思应该是输出更改后的gintr这个结构体变量的内容 ,参照上面处理方法我把它强制类型转换成llu型: * ( long long unsigned *) & gintr 就OK了。
解释:首先是取gintr这个变量的地址,强制转换为指向llu型变量的指针,再引用这个地址就得到了llu型的变量。
输出结果为:
intr is 0x10002
gintr is 0xee0000010002
1
2
这个程序目的应该是用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;
}
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
分析:在进入循环体前,p是指向自己的头指针。p的结构如下:
进入到循环体的第一次循环后,新建了一个节点,链接到p。结构如下:
第一次循环的最后,p = p -> next 也就是 p = e1。第二次循环后结构如图:
然后 p = e2 结束第二层循环,依此循环10次就完成了双向链表节点的插入。最后从后往前打印p的num的值。
在实验楼的终端上运行报错,就没调试。
---------------------