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