diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..4df567b --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module os-course-design + +go 1.25.1 diff --git a/main.go b/main.go new file mode 100644 index 0000000..22e9552 --- /dev/null +++ b/main.go @@ -0,0 +1,81 @@ +package main + +import ( + "encoding/json" + "fmt" + "net/http" + "os" + "strconv" +) + +type SimRequest struct { + Algorithm string `json:"algorithm"` + TimeSlice int `json:"timeSlice"` + Processes []ProcInput `json:"processes"` +} + +type ProcInput struct { + Name string `json:"name"` + Priority int `json:"priority"` + ArrivalTime int `json:"arrivalTime"` + NeedTime int `json:"needTime"` +} + +func main() { + port := "8080" + if len(os.Args) > 1 { + p, err := strconv.Atoi(os.Args[1]) + if err == nil && p > 0 && p < 65536 { + port = os.Args[1] + } + } + + fs := http.FileServer(http.Dir("./static")) + http.Handle("/", fs) + + http.HandleFunc("/api/simulate", handleSimulate) + + fmt.Printf("进程调度模拟器已启动: http://localhost:%s\n", port) + if err := http.ListenAndServe(":"+port, nil); err != nil { + fmt.Fprintf(os.Stderr, "服务器启动失败: %v\n", err) + os.Exit(1) + } +} + +func handleSimulate(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + var req SimRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + http.Error(w, "Invalid request: "+err.Error(), http.StatusBadRequest) + return + } + + procs := make([]*PCB, len(req.Processes)) + for i, p := range req.Processes { + procs[i] = NewPCB(p.Name, p.Priority, p.ArrivalTime, p.NeedTime) + } + + var result SimResult + if req.Algorithm == "mlfq" { + ts := [3]int{1, 2, 4} + if req.TimeSlice > 0 { + ts[0] = req.TimeSlice + ts[1] = req.TimeSlice * 2 + ts[2] = req.TimeSlice * 4 + } + result = SimulateMLFQ(procs, ts) + } else { + ts := 2 + if req.TimeSlice > 0 { + ts = req.TimeSlice + } + result = SimulatePriorityRR(procs, ts) + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(result) +} diff --git a/scheduler.go b/scheduler.go new file mode 100644 index 0000000..bd590db --- /dev/null +++ b/scheduler.go @@ -0,0 +1,506 @@ +package main + +import "sort" + +const ( + StateReady = "R" + StateExecute = "E" + StateWait = "W" + StateFinish = "F" +) + +type PCB struct { + Name string `json:"name"` + Priority int `json:"priority"` + InitPriority int `json:"initPriority"` + ArrivalTime int `json:"arrivalTime"` + NeedTime int `json:"needTime"` + UsedTime int `json:"usedTime"` + State string `json:"state"` + StartTime int `json:"startTime"` + FinishTime int `json:"finishTime"` + QueueLevel int `json:"queueLevel"` + WaitUntil int `json:"waitUntil"` + SliceUsedTime int `json:"sliceUsedTime"` +} + +func (p *PCB) Clone() PCB { + return *p +} + +func (p *PCB) Remaining() int { + return p.NeedTime - p.UsedTime +} + +type Snapshot struct { + Time int `json:"time"` + Processes []PCB `json:"processes"` + Running string `json:"running"` + Queues [][]string `json:"queues"` + Event string `json:"event"` +} + +type SimResult struct { + Snapshots []Snapshot `json:"snapshots"` + Algorithm string `json:"algorithm"` + AvgTurnaround float64 `json:"avgTurnaround"` + Deadlock bool `json:"deadlock"` +} + +func NewPCB(name string, priority, arrivalTime, needTime int) *PCB { + return &PCB{ + Name: name, + Priority: priority, + InitPriority: priority, + ArrivalTime: arrivalTime, + NeedTime: needTime, + UsedTime: 0, + State: StateReady, + StartTime: -1, + FinishTime: -1, + QueueLevel: 0, + WaitUntil: -1, + SliceUsedTime: 0, + } +} + +func allFinished(procs []*PCB) bool { + for _, p := range procs { + if p.State != StateFinish { + return false + } + } + return true +} + +func allWaitingOrFinished(procs []*PCB) bool { + for _, p := range procs { + if p.State != StateWait && p.State != StateFinish { + return false + } + } + return true +} + +func anyWillWake(procs []*PCB, time int) bool { + for _, p := range procs { + if p.State == StateWait && p.WaitUntil > time { + return true + } + } + return false +} + +func collectReady(procs []*PCB, time int) []*PCB { + var ready []*PCB + for _, p := range procs { + if p.State == StateReady && p.ArrivalTime <= time { + ready = append(ready, p) + } + } + return ready +} + +func findRunning(procs []*PCB) *PCB { + for _, p := range procs { + if p.State == StateExecute { + return p + } + } + return nil +} + +func calcAvgTurnaround(procs []*PCB) float64 { + total := 0 + count := 0 + for _, p := range procs { + if p.State == StateFinish && p.FinishTime > 0 { + total += p.FinishTime - p.ArrivalTime + count++ + } + } + if count == 0 { + return 0 + } + return float64(total) / float64(count) +} + +func makeSnap(time int, procs []*PCB, running string, queues [][]string, event string) Snapshot { + var copies []PCB + for _, p := range procs { + copies = append(copies, p.Clone()) + } + return Snapshot{ + Time: time, + Processes: copies, + Running: running, + Queues: queues, + Event: event, + } +} + +func SimulatePriorityRR(processes []*PCB, timeSlice int) SimResult { + procs := make([]*PCB, len(processes)) + for i, p := range processes { + c := *p + procs[i] = &c + } + + var snapshots []Snapshot + var readyQueue []*PCB + time := 0 + maxTime := 0 + for _, p := range procs { + end := p.ArrivalTime + p.NeedTime*2 + 10 + if end > maxTime { + maxTime = end + } + } + + for time <= maxTime { + for _, p := range procs { + if p.State == StateWait && p.WaitUntil <= time { + p.State = StateReady + p.WaitUntil = -1 + readyQueue = append(readyQueue, p) + } + if p.ArrivalTime == time && p.State == StateReady { + found := false + for _, q := range readyQueue { + if q == p { + found = true + break + } + } + if !found { + readyQueue = append(readyQueue, p) + } + } + } + + running := findRunning(procs) + + if running != nil { + for _, p := range readyQueue { + if p.Priority < running.Priority { + running.State = StateReady + running.SliceUsedTime = 0 + readyQueue = append(readyQueue, running) + running = nil + break + } + } + } + + if running == nil { + if len(readyQueue) == 0 { + if allFinished(procs) { + snapshots = append(snapshots, makeSnap(time, procs, "", nil, "✓ 所有进程执行完毕")) + break + } + if allWaitingOrFinished(procs) { + if !anyWillWake(procs, time) { + snapshots = append(snapshots, makeSnap(time, procs, "", nil, "✗ 死锁:所有未完成进程都在等待")) + return SimResult{Snapshots: snapshots, Algorithm: "基于优先级的时间片轮转", AvgTurnaround: calcAvgTurnaround(procs), Deadlock: true} + } + snapshots = append(snapshots, makeSnap(time, procs, "", nil, "无就绪进程,等待唤醒...")) + time++ + continue + } + snapshots = append(snapshots, makeSnap(time, procs, "", nil, "无就绪进程")) + time++ + continue + } + + sort.Slice(readyQueue, func(i, j int) bool { + if readyQueue[i].Priority != readyQueue[j].Priority { + return readyQueue[i].Priority < readyQueue[j].Priority + } + return readyQueue[i].ArrivalTime < readyQueue[j].ArrivalTime + }) + + selected := readyQueue[0] + readyQueue = readyQueue[1:] + selected.State = StateExecute + selected.SliceUsedTime = 0 + if selected.StartTime == -1 { + selected.StartTime = time + } + running = selected + } + + queueNames := buildQueueNames(readyQueue) + event := running.Name + " 执行 (优先级=" + itoa(running.Priority) + ", 剩余=" + itoa(running.Remaining()) + ")" + snapshots = append(snapshots, makeSnap(time, procs, running.Name, queueNames, event)) + + running.UsedTime++ + running.SliceUsedTime++ + + if running.UsedTime >= running.NeedTime { + running.State = StateFinish + running.FinishTime = time + 1 + snapshots = append(snapshots, makeSnap(time+1, procs, "", nil, running.Name+" 执行完毕 ✓")) + } else if running.SliceUsedTime >= timeSlice { + running.Priority += 3 + if running.UsedTime%3 == 0 && running.UsedTime < running.NeedTime { + running.State = StateWait + running.WaitUntil = time + 2 + running.SliceUsedTime = 0 + snapshots = append(snapshots, makeSnap(time+1, procs, "", nil, + running.Name+" 等待I/O (优先级→"+itoa(running.Priority)+", 唤醒于t="+itoa(running.WaitUntil)+")")) + } else { + running.State = StateReady + running.SliceUsedTime = 0 + readyQueue = append(readyQueue, running) + snapshots = append(snapshots, makeSnap(time+1, procs, "", buildQueueNames(readyQueue), + running.Name+" 时间片用完 (优先级→"+itoa(running.Priority)+")")) + } + } else { + queueNames2 := buildQueueNames(readyQueue) + snapshots = append(snapshots, makeSnap(time+1, procs, running.Name, queueNames2, running.Name+" 继续执行")) + } + + time++ + } + + return SimResult{ + Snapshots: snapshots, + Algorithm: "基于优先级的时间片轮转", + AvgTurnaround: calcAvgTurnaround(procs), + Deadlock: false, + } +} + +func SimulateMLFQ(processes []*PCB, queueTimeSlices [3]int) SimResult { + procs := make([]*PCB, len(processes)) + for i, p := range processes { + c := *p + c.QueueLevel = 0 + c.SliceUsedTime = 0 + procs[i] = &c + } + + var snapshots []Snapshot + queues := [3][]*PCB{} + time := 0 + maxTime := 0 + for _, p := range procs { + end := p.ArrivalTime + p.NeedTime*3 + 20 + if end > maxTime { + maxTime = end + } + } + + for time <= maxTime { + for _, p := range procs { + if p.State == StateWait && p.WaitUntil <= time { + p.State = StateReady + p.WaitUntil = -1 + queues[p.QueueLevel] = append(queues[p.QueueLevel], p) + } + if p.ArrivalTime == time && p.State == StateReady { + p.QueueLevel = 0 + p.SliceUsedTime = 0 + found := false + for _, q := range queues[0] { + if q == p { + found = true + break + } + } + if !found { + queues[0] = append(queues[0], p) + } + } + } + + running := findRunning(procs) + + if running != nil { + preempted := false + for q := 0; q < 3; q++ { + sort.Slice(queues[q], func(i, j int) bool { + return queues[q][i].Priority < queues[q][j].Priority + }) + if len(queues[q]) > 0 && (q < running.QueueLevel || (q == running.QueueLevel && queues[q][0].Priority < running.Priority)) { + running.State = StateReady + queues[running.QueueLevel] = insertSortedByPriority(queues[running.QueueLevel], running) + running = nil + preempted = true + break + } + } + _ = preempted + } + + if running == nil { + selected := selectFromMLFQ(queues) + if selected == nil { + if allFinished(procs) { + snapshots = append(snapshots, makeMLFQSnap(time, procs, "", queues, "✓ 所有进程执行完毕")) + break + } + if allWaitingOrFinished(procs) { + if !anyWillWake(procs, time) { + snapshots = append(snapshots, makeMLFQSnap(time, procs, "", queues, "✗ 死锁:所有未完成进程都在等待")) + return SimResult{Snapshots: snapshots, Algorithm: "多级反馈队列轮转", AvgTurnaround: calcAvgTurnaround(procs), Deadlock: true} + } + snapshots = append(snapshots, makeMLFQSnap(time, procs, "", queues, "无就绪进程,等待唤醒...")) + time++ + continue + } + snapshots = append(snapshots, makeMLFQSnap(time, procs, "", queues, "无就绪进程")) + time++ + continue + } + + removeFromQueue(&queues, selected) + selected.State = StateExecute + if selected.StartTime == -1 { + selected.StartTime = time + } + running = selected + } + + event := running.Name + " 从Q" + itoa(running.QueueLevel) + "执行 (片内已用=" + itoa(running.SliceUsedTime) + "/" + itoa(queueTimeSlices[running.QueueLevel]) + ")" + snapshots = append(snapshots, makeMLFQSnap(time, procs, running.Name, queues, event)) + + running.UsedTime++ + running.SliceUsedTime++ + qLevel := running.QueueLevel + ts := queueTimeSlices[qLevel] + + if running.UsedTime >= running.NeedTime { + running.State = StateFinish + running.FinishTime = time + 1 + snapshots = append(snapshots, makeMLFQSnap(time+1, procs, "", queues, running.Name+" 执行完毕 ✓")) + } else if running.SliceUsedTime >= ts { + shouldWait := running.UsedTime%3 == 0 && running.UsedTime < running.NeedTime + if shouldWait { + running.State = StateWait + running.WaitUntil = time + 2 + running.SliceUsedTime = 0 + snapshots = append(snapshots, makeMLFQSnap(time+1, procs, "", queues, + running.Name+" 等待I/O (唤醒于t="+itoa(running.WaitUntil)+")")) + } else if qLevel < 2 { + running.QueueLevel = qLevel + 1 + running.State = StateReady + running.SliceUsedTime = 0 + queues[qLevel+1] = append(queues[qLevel+1], running) + snapshots = append(snapshots, makeMLFQSnap(time+1, procs, "", queues, + running.Name+" 降级→Q"+itoa(qLevel+1))) + } else { + running.State = StateReady + running.SliceUsedTime = 0 + queues[2] = append(queues[2], running) + snapshots = append(snapshots, makeMLFQSnap(time+1, procs, "", queues, + running.Name+" 重新加入Q2")) + } + } else { + snapshots = append(snapshots, makeMLFQSnap(time+1, procs, running.Name, queues, running.Name+" 继续执行")) + } + + time++ + } + + return SimResult{ + Snapshots: snapshots, + Algorithm: "多级反馈队列轮转", + AvgTurnaround: calcAvgTurnaround(procs), + Deadlock: false, + } +} + +func selectFromMLFQ(queues [3][]*PCB) *PCB { + for q := 0; q < 3; q++ { + sort.Slice(queues[q], func(i, j int) bool { + if queues[q][i].Priority != queues[q][j].Priority { + return queues[q][i].Priority < queues[q][j].Priority + } + return queues[q][i].ArrivalTime < queues[q][j].ArrivalTime + }) + if len(queues[q]) > 0 { + return queues[q][0] + } + } + return nil +} + +func removeFromQueue(queues *[3][]*PCB, target *PCB) { + for q := 0; q < 3; q++ { + for i, p := range queues[q] { + if p == target { + queues[q] = append(queues[q][:i], queues[q][i+1:]...) + return + } + } + } +} + +func insertSortedByPriority(queue []*PCB, p *PCB) []*PCB { + queue = append(queue, p) + sort.Slice(queue, func(i, j int) bool { + if queue[i].Priority != queue[j].Priority { + return queue[i].Priority < queue[j].Priority + } + return queue[i].ArrivalTime < queue[j].ArrivalTime + }) + return queue +} + +func buildQueueNames(queue []*PCB) [][]string { + var names []string + for _, p := range queue { + names = append(names, p.Name) + } + if names == nil { + names = []string{} + } + return [][]string{names} +} + +func buildMLFQQueueNames(queues [3][]*PCB) [][]string { + var qs [][]string + for q := 0; q < 3; q++ { + var names []string + for _, p := range queues[q] { + names = append(names, p.Name) + } + if names == nil { + names = []string{} + } + qs = append(qs, names) + } + return qs +} + +func makeMLFQSnap(time int, procs []*PCB, running string, queues [3][]*PCB, event string) Snapshot { + return makeSnap(time, procs, running, buildMLFQQueueNames(queues), event) +} + +func itoa(n int) string { + if n == 0 { + return "0" + } + neg := false + if n < 0 { + neg = true + n = -n + } + s := "" + for n > 0 { + s = string(rune('0'+n%10)) + s + n /= 10 + } + if neg { + s = "-" + s + } + return s +} + +func minInt(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/static/index.html b/static/index.html new file mode 100644 index 0000000..165102e --- /dev/null +++ b/static/index.html @@ -0,0 +1,604 @@ + + + + + +进程调度模拟器 + + + +
+

进程调度模拟器

+ +
+
参数设置
+
优先级时间片轮转
+
多级反馈队列轮转
+
算法对比
+
+ +
+
+

进程参数

+
+
+ +
+
+
+ +
+
+
+

进程信息表

+
+ +
进程名优先级(1-100)到达时间运行时间
+
+
+ + +
+
+
+ +
+
+

优先级时间片轮转 - 动态调度过程

+
+ + + + +
5
+ 步骤 0/0 +
+
等待模拟开始...
+
+
+
+
+

当前执行

+
-
+
+
+

就绪队列

+
-
+
+
+
+

进程状态表

+ +
进程优先级状态已用剩余进度
+
+
+
+

甘特图

+
+
+
+

调度结果

+
+
+
+ +
+
+

多级反馈队列轮转 - 动态调度过程

+
+ + + + +
5
+ 步骤 0/0 +
+
等待模拟开始...
+
+
+
+
+

当前执行

+
-
+
+
+

就绪队列 (Q0 / Q1 / Q2)

+
+

Q0 (最高优先)

-
+

Q1

-
+

Q2 (最低优先)

-
+
+
+
+
+

进程状态表

+ +
进程优先级队列状态已用剩余进度
+
+
+
+

甘特图

+
+
+
+

调度结果

+
+
+
+ +
+
+

算法性能对比

+
请先运行模拟(选择"两种算法对比")
+
+
+
+ + + +