最近在写操作系统的进程同步实验,现在随便写点什么。
实验基于川和秀实的《30天自制操作系统》的day24f代码,也就是进行代码分文件夹归类的最后一版。

要求分析:

  1. 实现系统内核态的共享变量竞争条件
  2. 实现系统内核态的共享变量竞争条件的解决方案,并进一步实现某一个同步场景
  3. 实现从用户态启动前两条内容
  4. 基于30天实现展示用户程序中逻辑地址到物理地址的转换

第一个。30天OS中的任务调度是以TASK为单位的,若要实现竞争条件,可以建立两个及以上数量的TASK,对同一个变量进行进行自加操作,检测自加前后的差值,如果大于1,那么就说明变量中间被别的TASK访问过。

在实现的时候,我发现要出现竞争条件很难,所以我在自加之后加了一个长度为5的循环,这样就很容易出现竞争条件了。

第二个。我重新整理了一下代码,做了一个sync_utils库。里面提供了一个快速创建任务的函数,以及原子的test_and_set、Swap、sem_wait、sem_signal。注意任何while的阻塞循环都应当io_hlt(),否则会一直占用CPU。利用这些函数,实现了一个生产者消费者的进程同步模型。

剩下两个还没整。

附一下sync_utils

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
/* sync_utils.c */
/* 记得修改Makefile和bootpack.h */
#include "bootpack.h"
void avoid_sleep() {
struct TASK *now_task;
now_task = task_now();
now_task->flags = 2;
}

int test_and_set(int *target) {
io_cli();
int tmp = *target;
*target = 0xff;
io_sti();
return tmp;
}
void Swap(int *a, int *b) {
io_cli();
int tmp = *a;
*a = *b;
*b = tmp;
io_sti();
}
void sem_signal(int *x) {
io_cli();
(*x)++;
io_sti();
}
void sem_wait(int *x) {
while ((*x) <= 0)
io_hlt();
io_cli();
(*x)--;
io_sti();
}

struct TASK *create_task(struct MEMMAN *man, void (*f)(void *), void *args) {
struct TASK *tst = task_alloc();
tst->cons_stack = memman_alloc_4k(man, 64 * 1024);
tst->tss.esp = tst->cons_stack + 64 * 1024 - 8;
tst->tss.eip = (int)f;
tst->tss.es = 1 * 8;
tst->tss.cs = 2 * 8;
tst->tss.ss = 1 * 8;
tst->tss.ds = 1 * 8;
tst->tss.fs = 1 * 8;
tst->tss.gs = 1 * 8;
*((int *)(tst->tss.esp + 4)) = (int)args;
return tst;
}

用法示例

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
struct BBParg { // 参数类型
int *empty, *mutex, *full, id;
int *con_mutex;
struct FIFO32 *fifo;
struct TASK *task;
};
void producer(struct BBParg *args) {
avoid_sleep();
int *empty = args->empty,
*mutex = args->mutex,
*full = args->full,
id = args->id;
struct FIFO32 *fifo = args->fifo;
struct TASK *task = args->task;
int cnt = 2, i;
char tag;
char str[128];
while (1) {
tag = 0;
while (!tag) {
tag = 1;
for (i = 2; i * i <= cnt; i++) {
if (cnt % i == 0) {
tag = 0;
break;
}
}
if (!tag) cnt++;
if (cnt > 0x3f3f3f) cnt = 2;
}
sem_wait(empty);
sem_wait(mutex);
fifo32_put(fifo, cnt++);
sem_signal(mutex);
sem_signal(full);
}
}
void consumer(struct BBParg *args) {
avoid_sleep();
int *empty = args->empty,
*mutex = args->mutex,
*full = args->full,
id = args->id,
*con_mutex = args->con_mutex;
struct FIFO32 *fifo = args->fifo;
struct TASK *task = args->task;
int tmp;
char str[128];
while (1) {
while (task->cons == 0)
io_hlt();
sem_wait(full);
sem_wait(mutex);
tmp = fifo32_get(fifo);
sem_signal(mutex);
sem_signal(empty);
sprintf(str, "cproc %d fetched %d\n", id, tmp);
sem_wait(con_mutex);
cons_putstr0(task->cons, str);
sem_signal(con_mutex);
}
}

/* in HariMain */
struct SHEET *sout;
sout = open_console(shtctl, memtotal);
sheet_slide(sout, 64, 4);
sheet_updown(sout, shtctl->top);
/* 进程创建参数 */
struct BBParg agp, agc1, agc2;
int buf[128];
struct FIFO32 bbpfifo;
fifo32_init(&bbpfifo, 8, buf, 0);
struct TASK *tskp, *tskc1, *tskc2;
int empty = 8, mutex = 1, full = 0;
int con_mutex = 1;
agp.empty = &empty;
agp.full = &full;
agp.mutex = &mutex;
agp.fifo = &bbpfifo;
agp.con_mutex = &con_mutex;
agc1 = agc2 = agp;
agc1.id = 1;
agc2.id = 2;
agc1.task = key_win->task; // 第一个consumer将在第一个console中进行输出
agc2.task = sout->task; // 第二个consumer将在第二个console中进行输出
/* 创建进程 */
tskp = create_task(memman, &producer, (void*)&agp);
tskc1 = create_task(memman, &consumer, (void*)&agc1);
tskc2 = create_task(memman, &consumer, (void*)&agc2);
/* 运行进程 */
task_run(tskp, 2, 0);
task_run(tskc1, 2, 0);
task_run(tskc2, 2, 0);
image_88.png