feat: added ability to identify process vm regions
[lunaix-os.git] / lunaix-os / kernel / sched.c
1 #include <lunaix/process.h>
2 #include <lunaix/sched.h>
3 #include <lunaix/mm/vmm.h>
4 #include <hal/cpu.h>
5 #include <arch/x86/interrupts.h>
6 #include <hal/apic.h>
7
8 #include <lunaix/spike.h>
9 #include <lunaix/status.h>
10 #include <lunaix/syslog.h>
11 #include <lunaix/syscall.h>
12
13 #define MAX_PROCESS 512
14
15 struct proc_info* __current;
16 struct proc_info dummy;
17
18 extern void __proc_table;
19
20 struct scheduler sched_ctx;
21
22 LOG_MODULE("SCHED")
23
24 void sched_init() {
25     size_t pg_size = ROUNDUP(sizeof(struct proc_info) * MAX_PROCESS, 0x1000);
26     assert_msg(
27         vmm_alloc_pages(KERNEL_PID, &__proc_table, pg_size, PG_PREM_RW, PP_FGPERSIST), 
28         "Fail to allocate proc table"
29     );
30     
31     sched_ctx = (struct scheduler) {
32         ._procs = (struct proc_info*) &__proc_table,
33         .ptable_len = 0,
34         .procs_index = 0
35     };
36 }
37
38 void run(struct proc_info* proc) {
39     if (!(__current->state & ~PROC_RUNNING)) {
40         __current->state = PROC_STOPPED;
41     }
42     proc->state = PROC_RUNNING;
43     
44     __current = proc;
45
46     cpu_lcr3(__current->page_table);
47
48     apic_done_servicing();
49
50     asm volatile (
51         "pushl %0\n"
52         "jmp soft_iret\n"::"r"(&__current->intr_ctx): "memory");
53 }
54
55 void schedule() {
56     if (!sched_ctx.ptable_len) {
57         return;
58     }
59
60     struct proc_info* next;
61     int prev_ptr = sched_ctx.procs_index;
62     int ptr = prev_ptr;
63     // round-robin scheduler
64     do {
65         ptr = (ptr + 1) % sched_ctx.ptable_len;
66         next = &sched_ctx._procs[ptr];
67     } while(next->state != PROC_STOPPED && ptr != prev_ptr);
68     
69     sched_ctx.procs_index = ptr;
70     
71     run(next);
72 }
73
74 static void proc_timer_callback(struct proc_info* proc) {
75     proc->timer = NULL;
76     proc->state = PROC_STOPPED;
77 }
78
79 __DEFINE_LXSYSCALL1(unsigned int, sleep, unsigned int, seconds) {
80     if (!seconds) {
81         return 0;
82     }
83     if (__current->timer) {
84         return __current->timer->counter / timer_context()->running_frequency;
85     }
86
87     struct lx_timer* timer = timer_run_second(seconds, proc_timer_callback, __current, 0);
88     __current->timer = timer;
89     __current->intr_ctx.registers.eax = seconds;
90     __current->state = PROC_BLOCKED;
91     schedule();
92 }
93
94 __DEFINE_LXSYSCALL1(void, exit, int, status) {
95     terminate_proc(status);
96 }
97
98 __DEFINE_LXSYSCALL(void, yield) {
99     schedule();
100 }
101
102 pid_t alloc_pid() {
103     pid_t i = 0;
104     for (; i < sched_ctx.ptable_len && sched_ctx._procs[i].state != PROC_DESTROY; i++);
105
106     if (i == MAX_PROCESS) {
107         __current->k_status = LXPROCFULL;
108         return -1;
109     }
110     return i + 1;
111 }
112
113 void push_process(struct proc_info* process) {
114     int index = process->pid - 1;
115     if (index < 0 || index > sched_ctx.ptable_len) {
116         __current->k_status = LXINVLDPID;
117         return;
118     }
119     
120     if (index == sched_ctx.ptable_len) {
121         sched_ctx.ptable_len++;
122     }
123     
124     // every process is the parent of first process (pid=1)
125     process->parent = process->parent ? process->parent : &sched_ctx._procs;
126     process->state = PROC_STOPPED;
127
128     sched_ctx._procs[index] = *process;
129 }
130
131 void destroy_process(pid_t pid) {
132     int index = pid - 1;
133     if (index <= 0 || index > sched_ctx.ptable_len) {
134         __current->k_status = LXINVLDPID;
135         return;
136     }
137
138     sched_ctx._procs[index].state = PROC_DESTROY;
139
140     // TODO: recycle the physical pages used by page tables
141 }
142
143 void terminate_proc(int exit_code) {
144     __current->state = exit_code < 0 ? PROC_SPOILED : PROC_TERMNAT;
145     __current->exit_code = exit_code;
146
147     schedule();
148 }
149
150 struct proc_info* get_process(pid_t pid) {
151     int index = pid - 1;
152     if (index < 0 || index > sched_ctx.ptable_len) {
153         return NULL;
154     }
155     return &sched_ctx._procs[index];
156 }
157
158 int orphaned_proc(pid_t pid) {
159     if(!pid) return 0;
160     if(pid >= sched_ctx.ptable_len) return 0;
161     struct proc_info* proc = &sched_ctx._procs[pid-1];
162     struct proc_info* parent = proc->parent;
163     
164     // 如果其父进程的状态是terminated, spoiled 或 destroy中的一种
165     // 或者其父进程是在该进程之后创建的,那么该进程为孤儿进程
166     return (parent->state & 0xe) || parent->created > proc->created;
167 }