fix: add dummy process to keep scheduler busy
[lunaix-os.git] / lunaix-os / kernel / process / sched.c
1 #include <arch/x86/interrupts.h>
2 #include <arch/x86/tss.h>
3
4 #include <hal/apic.h>
5 #include <hal/cpu.h>
6
7 #include <lunaix/fs/taskfs.h>
8 #include <lunaix/mm/cake.h>
9 #include <lunaix/mm/kalloc.h>
10 #include <lunaix/mm/pmm.h>
11 #include <lunaix/mm/valloc.h>
12 #include <lunaix/mm/vmm.h>
13 #include <lunaix/process.h>
14 #include <lunaix/sched.h>
15 #include <lunaix/signal.h>
16 #include <lunaix/spike.h>
17 #include <lunaix/status.h>
18 #include <lunaix/syscall.h>
19 #include <lunaix/syslog.h>
20
21 volatile struct proc_info* __current;
22
23 static struct proc_info dummy_proc;
24
25 struct proc_info dummy;
26
27 struct scheduler sched_ctx;
28
29 struct cake_pile* proc_pile;
30
31 LOG_MODULE("SCHED")
32
33 void
34 sched_init_dummy();
35
36 void
37 sched_init()
38 {
39     proc_pile = cake_new_pile("proc", sizeof(struct proc_info), 1, 0);
40     cake_set_constructor(proc_pile, cake_ctor_zeroing);
41
42     sched_ctx = (struct scheduler){ ._procs = vzalloc(PROC_TABLE_SIZE),
43                                     .ptable_len = 0,
44                                     .procs_index = 0 };
45
46     // TODO initialize dummy_proc
47     sched_init_dummy();
48 }
49
50 void
51 sched_init_dummy()
52 {
53     // This surely need to be simplified or encapsulated!
54     // It is a living nightmare!
55
56     extern void my_dummy();
57     static char dummy_stack[1024] __attribute__((aligned(16)));
58
59     // memset to 0
60     dummy_proc = (struct proc_info){};
61     dummy_proc.intr_ctx =
62       (isr_param){ .registers = { .ds = KDATA_SEG,
63                                   .es = KDATA_SEG,
64                                   .fs = KDATA_SEG,
65                                   .gs = KDATA_SEG,
66                                   .esp = (void*)dummy_stack + 1004 },
67                    .cs = KCODE_SEG,
68                    .eip = (void*)my_dummy,
69                    .ss = KDATA_SEG,
70                    .eflags = cpu_reflags() | 0x0200 };
71
72     *(u32_t*)(&dummy_stack[1020]) = dummy_proc.intr_ctx.eflags;
73     *(u32_t*)(&dummy_stack[1016]) = KCODE_SEG;
74     *(u32_t*)(&dummy_stack[1012]) = dummy_proc.intr_ctx.eip;
75
76     dummy_proc.page_table = cpu_rcr3();
77     dummy_proc.state = PS_READY;
78     dummy_proc.parent = &dummy_proc;
79
80     __current = &dummy_proc;
81 }
82
83 void
84 run(struct proc_info* proc)
85 {
86     proc->state = PS_RUNNING;
87
88     /*
89         将tss.esp0设置为上次调度前的esp值。
90         当处理信号时,上下文信息是不会恢复的,而是保存在用户栈中,然后直接跳转进位于用户空间的sig_wrapper进行
91           信号的处理。当用户自定义的信号处理函数返回时,sigreturn的系统调用才开始进行上下文的恢复(或者说是进行
92           另一次调度。
93         由于这中间没有进行地址空间的交换,所以第二次跳转使用的是同一个内核栈,而之前默认tss.esp0的值是永远指向最顶部
94         这样一来就有可能会覆盖更早的上下文信息(比如嵌套的信号捕获函数)
95     */
96     tss_update_esp(proc->intr_ctx.registers.esp);
97
98     apic_done_servicing();
99
100     asm volatile("pushl %0\n"
101                  "jmp switch_to\n" ::"r"(proc)
102                  : "memory"); // kernel/asm/x86/interrupt.S
103 }
104
105 int
106 can_schedule(struct proc_info* proc)
107 {
108     if (__SIGTEST(proc->sig_pending, _SIGCONT)) {
109         __SIGCLEAR(proc->sig_pending, _SIGSTOP);
110     } else if (__SIGTEST(proc->sig_pending, _SIGSTOP)) {
111         // 如果进程受到SIGSTOP,则该进程不给予调度。
112         return 0;
113     }
114
115     return 1;
116 }
117
118 void
119 check_sleepers()
120 {
121     struct proc_info* leader = sched_ctx._procs[0];
122     struct proc_info *pos, *n;
123     time_t now = clock_systime();
124     llist_for_each(pos, n, &leader->sleep.sleepers, sleep.sleepers)
125     {
126         if (PROC_TERMINATED(pos->state)) {
127             goto del;
128         }
129
130         time_t wtime = pos->sleep.wakeup_time;
131         time_t atime = pos->sleep.alarm_time;
132
133         if (wtime && now >= wtime) {
134             pos->sleep.wakeup_time = 0;
135             pos->state = PS_READY;
136         }
137
138         if (atime && now >= atime) {
139             pos->sleep.alarm_time = 0;
140             __SIGSET(pos->sig_pending, _SIGALRM);
141         }
142
143         if (!wtime && !atime) {
144         del:
145             llist_delete(&pos->sleep.sleepers);
146         }
147     }
148 }
149
150 void
151 schedule()
152 {
153     if (!sched_ctx.ptable_len) {
154         return;
155     }
156
157     // 上下文切换相当的敏感!我们不希望任何的中断打乱栈的顺序……
158     cpu_disable_interrupt();
159     struct proc_info* next;
160     int prev_ptr = sched_ctx.procs_index;
161     int ptr = prev_ptr;
162
163     if (!(__current->state & ~PS_RUNNING)) {
164         __current->state = PS_READY;
165     }
166
167     check_sleepers();
168
169     // round-robin scheduler
170 redo:
171     do {
172         ptr = (ptr + 1) % sched_ctx.ptable_len;
173         next = sched_ctx._procs[ptr];
174     } while (!next || (next->state != PS_READY && ptr != prev_ptr));
175
176     sched_ctx.procs_index = ptr;
177
178     if (next->state != PS_READY) {
179         // schedule the dummy process if we're out of choice
180         next = &dummy_proc;
181         goto done;
182     }
183
184     if (!can_schedule(next)) {
185         // 如果该进程不给予调度,则尝试重新选择
186         goto redo;
187     }
188
189 done:
190     run(next);
191 }
192
193 void
194 sched_yieldk()
195 {
196     cpu_enable_interrupt();
197     cpu_int(LUNAIX_SCHED);
198 }
199
200 __DEFINE_LXSYSCALL1(unsigned int, sleep, unsigned int, seconds)
201 {
202     if (!seconds) {
203         return 0;
204     }
205
206     if (__current->sleep.wakeup_time) {
207         return (__current->sleep.wakeup_time - clock_systime()) / 1000U;
208     }
209
210     struct proc_info* root_proc = sched_ctx._procs[0];
211     __current->sleep.wakeup_time = clock_systime() + seconds * 1000;
212     llist_append(&root_proc->sleep.sleepers, &__current->sleep.sleepers);
213
214     __current->intr_ctx.registers.eax = seconds;
215
216     block_current();
217     schedule();
218 }
219
220 __DEFINE_LXSYSCALL1(unsigned int, alarm, unsigned int, seconds)
221 {
222     time_t prev_ddl = __current->sleep.alarm_time;
223     time_t now = clock_systime();
224
225     __current->sleep.alarm_time = seconds ? now + seconds * 1000 : 0;
226
227     struct proc_info* root_proc = sched_ctx._procs[0];
228     if (llist_empty(&__current->sleep.sleepers)) {
229         llist_append(&root_proc->sleep.sleepers, &__current->sleep.sleepers);
230     }
231
232     return prev_ddl ? (prev_ddl - now) / 1000 : 0;
233 }
234
235 __DEFINE_LXSYSCALL1(void, exit, int, status)
236 {
237     terminate_proc(status);
238     schedule();
239 }
240
241 __DEFINE_LXSYSCALL(void, yield)
242 {
243     schedule();
244 }
245
246 pid_t
247 _wait(pid_t wpid, int* status, int options);
248
249 __DEFINE_LXSYSCALL1(pid_t, wait, int*, status)
250 {
251     return _wait(-1, status, 0);
252 }
253
254 __DEFINE_LXSYSCALL3(pid_t, waitpid, pid_t, pid, int*, status, int, options)
255 {
256     return _wait(pid, status, options);
257 }
258
259 __DEFINE_LXSYSCALL(int, geterrno)
260 {
261     return __current->k_status;
262 }
263
264 pid_t
265 _wait(pid_t wpid, int* status, int options)
266 {
267     pid_t cur = __current->pid;
268     int status_flags = 0;
269     struct proc_info *proc, *n;
270     if (llist_empty(&__current->children)) {
271         return -1;
272     }
273
274     wpid = wpid ? wpid : -__current->pgid;
275 repeat:
276     llist_for_each(proc, n, &__current->children, siblings)
277     {
278         if (!~wpid || proc->pid == wpid || proc->pgid == -wpid) {
279             if (proc->state == PS_TERMNAT && !options) {
280                 status_flags |= PEXITTERM;
281                 goto done;
282             }
283             if (proc->state == PS_READY && (options & WUNTRACED)) {
284                 status_flags |= PEXITSTOP;
285                 goto done;
286             }
287         }
288     }
289     if ((options & WNOHANG)) {
290         return 0;
291     }
292     // 放弃当前的运行机会
293     sched_yieldk();
294     goto repeat;
295
296 done:
297     status_flags |= PEXITSIG * (proc->sig_inprogress != 0);
298     if (status) {
299         *status = proc->exit_code | status_flags;
300     }
301     return destroy_process(proc->pid);
302 }
303
304 struct proc_info*
305 alloc_process()
306 {
307     pid_t i = 0;
308     for (; i < sched_ctx.ptable_len && sched_ctx._procs[i]; i++)
309         ;
310
311     if (i == MAX_PROCESS) {
312         panick("Panic in Ponyville shimmer!");
313     }
314
315     if (i == sched_ctx.ptable_len) {
316         sched_ctx.ptable_len++;
317     }
318
319     struct proc_info* proc = cake_grab(proc_pile);
320
321     proc->state = PS_CREATED;
322     proc->pid = i;
323     proc->created = clock_systime();
324     proc->pgid = proc->pid;
325     proc->fdtable = vzalloc(sizeof(struct v_fdtable));
326     proc->fxstate =
327       vzalloc_dma(512); // FXSAVE需要十六位对齐地址,使用DMA块(128位对齐)
328
329     llist_init_head(&proc->mm.regions.head);
330     llist_init_head(&proc->tasks);
331     llist_init_head(&proc->children);
332     llist_init_head(&proc->grp_member);
333     llist_init_head(&proc->sleep.sleepers);
334     waitq_init(&proc->waitqueue);
335
336     sched_ctx._procs[i] = proc;
337
338     return proc;
339 }
340
341 void
342 commit_process(struct proc_info* process)
343 {
344     assert(process == sched_ctx._procs[process->pid]);
345
346     if (process->state != PS_CREATED) {
347         __current->k_status = EINVAL;
348         return;
349     }
350
351     // every process is the child of first process (pid=1)
352     if (!process->parent) {
353         process->parent = sched_ctx._procs[1];
354     }
355
356     llist_append(&process->parent->children, &process->siblings);
357     llist_append(&sched_ctx._procs[0]->tasks, &process->tasks);
358
359     process->state = PS_READY;
360 }
361
362 // from <kernel/process.c>
363 extern void
364 __del_pagetable(pid_t pid, uintptr_t mount_point);
365
366 pid_t
367 destroy_process(pid_t pid)
368 {
369     int index = pid;
370     if (index <= 0 || index > sched_ctx.ptable_len) {
371         __current->k_status = EINVAL;
372         return;
373     }
374     struct proc_info* proc = sched_ctx._procs[index];
375     sched_ctx._procs[index] = 0;
376
377     llist_delete(&proc->siblings);
378     llist_delete(&proc->grp_member);
379     llist_delete(&proc->tasks);
380     llist_delete(&proc->sleep.sleepers);
381
382     taskfs_invalidate(pid);
383
384     if (proc->cwd) {
385         vfs_unref_dnode(proc->cwd);
386     }
387
388     for (size_t i = 0; i < VFS_MAX_FD; i++) {
389         struct v_fd* fd = proc->fdtable->fds[i];
390         if (fd) {
391             vfs_pclose(fd->file, pid);
392             vfs_free_fd(fd);
393         }
394     }
395
396     vfree(proc->fdtable);
397     vfree_dma(proc->fxstate);
398
399     struct mm_region *pos, *n;
400     llist_for_each(pos, n, &proc->mm.regions.head, head)
401     {
402         vfree(pos);
403     }
404
405     vmm_mount_pd(PD_MOUNT_1, proc->page_table);
406
407     __del_pagetable(pid, PD_MOUNT_1);
408
409     vmm_unmount_pd(PD_MOUNT_1);
410
411     cake_release(proc_pile, proc);
412
413     return pid;
414 }
415
416 void
417 terminate_proc(int exit_code)
418 {
419     __current->state = PS_TERMNAT;
420     __current->exit_code = exit_code;
421
422     __SIGSET(__current->parent->sig_pending, _SIGCHLD);
423 }
424
425 struct proc_info*
426 get_process(pid_t pid)
427 {
428     int index = pid;
429     if (index < 0 || index > sched_ctx.ptable_len) {
430         return NULL;
431     }
432     return sched_ctx._procs[index];
433 }
434
435 int
436 orphaned_proc(pid_t pid)
437 {
438     if (!pid)
439         return 0;
440     if (pid >= sched_ctx.ptable_len)
441         return 0;
442     struct proc_info* proc = sched_ctx._procs[pid];
443     struct proc_info* parent = proc->parent;
444
445     // 如果其父进程的状态是terminated 或 destroy中的一种
446     // 或者其父进程是在该进程之后创建的,那么该进程为孤儿进程
447     return PROC_TERMINATED(parent->state) || parent->created > proc->created;
448 }