New developer version 0.6.8; added select () function; added demonstrating example...
[ZeXOS.git] / kernel / core / proc.c
blob0c1bf913ac2ce933f1cd5b01bd7eb1d5b6f184df
1 /*
2 * ZeX/OS
3 * Copyright (C) 2007 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
4 * Copyright (C) 2008 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
5 * Copyright (C) 2009 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
6 * Copyright (C) 2010 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include <system.h>
24 #include <string.h>
25 #include <proc.h>
26 #include <tty.h>
27 #include <task.h>
28 #include <signal.h>
29 #include <build.h>
30 #include <paging.h>
31 #include <vfs.h>
32 #include <net/socket.h>
33 #include <fd.h>
35 static void *thread_arg;
36 static void *(*thread_entry) (void *);
38 static pid_t newpid = 0;
40 proc_t proc_list;
41 proc_t proc_kernel;
43 extern task_t task_list;
44 extern task_t *_curr_task;
45 extern unsigned long timer_ticks;
47 pid_t fork ()
49 proc_t *p = proc_find (_curr_task);
51 if (!p)
52 return 0;
54 #ifdef ARCH_i386
55 return proc_create (p->tty, p->name, p->task->state[0].JMPBUF_IP)->pid;
56 #endif
59 void proc_display ()
61 printf ("PID\tCMD\tMEM\n");
63 proc_t *proc;
64 for (proc = proc_list.next; proc != &proc_list; proc = proc->next)
65 printf ("%d\t%s\t%dkB\n", proc->pid, proc->name, umem_used_proc (proc) / 1024);
68 void proc_top ()
70 tty_t *tty = currtty;
72 unsigned long ltime = 0;
74 for (;;) {
75 if (tty != currtty) {
76 schedule ();
77 continue;
80 tty_cls (tty);
82 printf ("TID\tTASK\t\tCPU Usage\n");
84 task_t *task;
85 for (task = task_list.next; task != &task_list; task = task->next) {
86 unsigned usage = 100 - ((task->lasttick - ltime) / 10);
87 char name[17];
88 memset (name, ' ', 16);
89 memcpy (name, task->name, strlen (task->name) > 16 ? 16 : strlen (task->name));
90 name[16] = '\0';
92 printf ("%d\t%s%d%%\n", task->id, name, usage > 100 ? 100 : usage);
93 schedule ();
97 proc_t *proc;
98 for (proc = proc_list.next; proc != &proc_list; proc = proc->next) {
99 printf ("\nPID\tCMD\t\tMEM\tCPU Usage\n");
100 break;
103 for (proc = proc_list.next; proc != &proc_list; proc = proc->next) {
104 schedule ();
105 unsigned usage = 100 - ((proc->task->lasttick - ltime) / 10);
106 char name[17];
107 memset (name, ' ', 16);
108 memcpy (name, proc->task->name, strlen (proc->task->name) > 16 ? 16 : strlen (proc->task->name));
109 name[16] = '\0';
110 printf ("%d\t%s%dkB\t%d%%\n", proc->pid, name, umem_used_proc (proc) / 1024, usage > 100 ? 100 : usage);
113 unsigned long stime = timer_ticks;
115 ltime = timer_ticks;
117 /* timeout for 1s */
118 while ((stime+1000) >= timer_ticks) {
119 if (getkey () == 'q')
120 return;
122 schedule ();
127 proc_t *proc_find (task_t *task)
129 proc_t *proc;
130 for (proc = proc_list.next; proc != &proc_list; proc = proc->next) {
131 if (!proc->task)
132 continue;
134 if (proc->task->id == task->id)
135 return proc;
138 return 0;
141 proc_t *proc_findbypid (pid_t pid)
143 proc_t *proc;
144 for (proc = proc_list.next; proc != &proc_list; proc = proc->next) {
145 if (proc->pid == pid)
146 return proc;
149 return 0;
152 unsigned proc_fd_close (proc_t *proc)
154 fd_t *f;
156 unsigned i;
158 for (;;) {
159 i = 0;
160 for (f = fd_list.next; f != &fd_list; f = f->next) {
161 if (f->proc == proc) {
162 if (f->flags & FD_SOCK) // socket close
163 sclose (f->id);
165 fd_delete (f);
167 i ++;
168 break;
172 if (!i)
173 return 1;
176 return 0;
179 proc_t *proc_create (tty_t *tty, char *name, unsigned entry)
181 proc_t *proc = NULL;
182 task_t *task = (task_t *) task_create (name, entry, PROC_USER_PRIORITY);
184 if (!task) {
185 DPRINT (DBG_PROC, "proc_create () - !task");
186 return 0;
189 /* alloc and init context */
190 proc = (proc_t *) kmalloc (sizeof (proc_t));
192 if (!proc) {
193 DPRINT (DBG_PROC, "proc_create () - !proc");
194 return 0;
197 /* remember task, because we need it for scheduler work */
198 proc->task = task;
200 /* remember new pid of process */
201 proc->pid = ++ newpid;
203 /* remember tty, where we start this process */
204 proc->tty = tty;
206 /* setup process name */
207 strcpy (proc->name, name);
209 /* clear process info */
210 proc->start = 0;
211 proc->code = 0;
212 proc->data = 0;
213 proc->data_off = 0;
214 proc->bss = 0;
215 proc->end = 0;
217 proc->argc = 0;
218 proc->argv = 0;
220 proc->flags = 0;
222 /* add structure into process list */
223 proc->next = &proc_list;
224 proc->prev = proc_list.prev;
225 proc->prev->next = proc;
226 proc->next->prev = proc;
228 /* create virtual file in /proc with name of new process */
229 vfs_list_add (name, VFS_FILEATTR_FILE | VFS_FILEATTR_SYSTEM, "/proc/");
231 DPRINT (DBG_PROC, "proc -> %s, pid %d, entry 0x%x", name, newpid, entry);
233 return proc;
236 extern bool task_done (task_t *task);
237 bool proc_done (proc_t *proc)
239 if (proc) {
240 /* verify task structure */
241 if (!proc->task)
242 return 0;
243 #ifdef ARCH_i386
244 /* unmap memory pages */
245 proc_vmem_unmap (proc);
246 #endif
247 /* delete all tasks from process context */
248 unsigned short id = proc->task->id;
250 for (;;) {
251 task_t *task = task_find (id);
253 if (!task)
254 break;
256 if (!task_done (task))
257 return 0;
260 DPRINT (DBG_PROC, "proc -> pid %d done", proc->pid);
262 /* close all un-closed file descriptors */
263 proc_fd_close (proc);
265 /* delete process from context */
266 proc->next->prev = proc->prev;
267 proc->prev->next = proc->next;
269 proc->task = 0;
271 /* free memory from process parameters */
272 proc_arg_free (proc);
274 /* free all memory blocks allocated by process */
275 ufree_proc (proc);
277 /* free process structure */
278 kfree (proc);
280 return 1;
283 return 0;
286 bool proc_signal (proc_t *proc, unsigned signal)
288 #ifdef ARCH_i386
289 if (!proc)
290 return 0;
292 /* check for daemon */
293 if (proc->flags & PROC_FLAG_DAEMON)
294 return 0;
296 task_t *task = proc->tty->task;
298 /* this unlock tty, enable itrs, etc (see command_exec ())*/
299 proc->pid = 0;
301 /* disable interrupts */
302 if (!int_disable ())
303 return 0;
305 /* jump back from app code to kernel code (task) */
306 longjmp (task->state, 1);
307 #else
308 DPRINT (DBG_PROC, "proc_signal: Not implemented for this platform !");
309 #endif
310 return 1;
313 sighandler_t signal (int signum, sighandler_t handler)
315 /* try to search current process */
316 proc_t *proc = proc_find (_curr_task);
318 if (!proc)
319 return 0;
321 if (proc->task != _curr_task)
322 return 0;
324 if (proc->tty != currtty)
325 return 0;
327 /* send signal to current process */
328 proc_signal (proc, signum);
330 return handler;
333 bool proc_arg_set (proc_t *proc, char *arg, unsigned argl)
335 if (!proc)
336 return 0;
338 unsigned i = 0;
339 unsigned y = 0;
340 unsigned x = 0;
342 if (proc->argc)
343 return 0;
345 /* First calculate length of array */
346 if (argl)
347 while (i < (argl+1)) {
348 if (arg[i] == ' ' || arg[i] == '\0')
349 x ++;
351 i ++;
354 proc->argv = (char **) kmalloc (sizeof (unsigned) * (x+2));
356 if (!proc->argv)
357 return 0;
359 proc->argc = x+1; /* +1 because argv[0] is name of process so it is one entry */
361 /* setup program name entry */
362 i = strlen (proc->name);
364 proc->argv[0] = (char *) kmalloc (sizeof (char) * (i+1));
365 memcpy (proc->argv[0], proc->name, i);
366 proc->argv[0][i] = '\0';
368 x = 1;
369 i = 0;
371 /* assign values to array */
372 while (i < (argl+1)) {
373 if (arg[i] == ' ' || arg[i] == '\0' || i == argl) {
374 if (!y) {
375 proc->argv[x] = (char *) kmalloc (sizeof (char) * (i+1));
377 if (!proc->argv[x])
378 return 0;
380 memcpy (proc->argv[x], arg, i);
381 proc->argv[x][i] = '\0';
382 } else {
383 proc->argv[x] = (char *) kmalloc (sizeof (char) * (i+1-y));
385 if (!proc->argv[x])
386 return 0;
388 memcpy (proc->argv[x], arg+y+1, i-y-1);
389 proc->argv[x][i-y-1] = '\0';
392 y = i;
393 x ++;
396 i ++;
399 return 1;
402 bool proc_arg_free (proc_t *proc)
404 if (!proc)
405 return 0;
407 if (!proc->argv || !proc->argc)
408 return 0;
410 unsigned i = 0;
412 while (i < proc->argc) {
413 kfree (proc->argv[i]);
414 i ++;
417 kfree (proc->argv);
419 proc->argc = 0;
421 return 1;
424 bool proc_arg_get (proc_t *proc, unsigned *argc, char **argv)
426 if (!proc)
427 return 0;
429 if (!proc->argv || !proc->argc || !argc)
430 return 0;
432 argv = proc->argv;
433 *argc = proc->argc;
435 return 1;
438 bool proc_flag_set (proc_t *proc, unsigned short flag)
440 if (!proc)
441 return 0;
443 if (proc->flags & flag)
444 return 0;
446 proc->flags |= flag;
448 return 1;
451 unsigned proc_page_fault ()
453 proc_t *proc = proc_find (_curr_task);
455 if (!proc)
456 return 0;
458 video_color (15, 0);
459 printf ("%s: signal SIGSEGV\n", proc->name);
461 /* send SIGSEGV signal to running process */
462 proc_signal (proc, SIGSEGV);
464 return 1;
467 unsigned proc_vmem_map (proc_t *proc)
469 #ifdef ARCH_i386
470 /* create new page_cover "shield" for process */
471 proc->task->page_cover = (page_ent_t *) page_cover_create ();
473 /* map kernel space */
474 page_mmap (proc->task->page_cover, (void *) 0x0, (void *) 0x400000, 1, 0);
476 /* map VESA double buffer memory - enough for 16bit 800x600 - 1MB for framebuffer */
477 page_mmap (proc->task->page_cover, (void *) 0x400000, (void *) proc->start, 1, 0);
479 /* calculate size of process image (binary file) */
480 unsigned p = (unsigned) palign ((void *) (proc->end - proc->start));
482 /* map process image - app should think, that it is placed at 8MB in ram */
483 page_mmap (proc->task->page_cover, (void *) 0x800000, (void *) 0x800000+p, 0, 1);
485 /* map end of kernel space */
486 page_mmap (proc->task->page_cover, (void *) proc->start+p, (void *) 0x800000, 1, 0);
488 proc->heap = 0x800000;
489 #endif
490 return 1;
493 unsigned proc_vmem_unmap (proc_t *proc)
495 if (!proc)
496 return 0;
498 if (!proc->task)
499 return 0;
500 #ifdef ARCH_i386
501 if (!proc->task->page_cover)
502 return 0;
504 page_ent_t *page_cover = proc->task->page_cover;
506 /* switch to kernel page directory */
507 page_dir_switch (task_list.next->page_cover->page_dir);
509 /* unmap kernel area */
510 page_unmmap (page_cover, (void *) 0x0, (void *) 0x400000);
512 /* unmap framebuffer area */
513 //page_unmmap (page_cover, (void *) 0x400000, (void *) 0x500000);
515 /* unmap process image */
516 page_unmmap (page_cover, (void *) 0x400000, (void *) proc->start);
518 unsigned p = (unsigned) palign ((void *) (proc->end - proc->start));
520 /* unmap end of user-space */
521 page_unmmap (page_cover, (void *) 0x800000, (void *) 0x800000+p);
523 /* unmap the rest */
524 page_unmmap (page_cover, (void *) proc->start+p, (void *) 0x800000);
526 /* unmap the allocated memory */
527 page_unmmap (page_cover, (void *) 0x800000+p, (void *) proc->heap+p);
529 if (!page_cover_delete (page_cover)) {
530 DPRINT (DBG_PROC, "ERROR -> proc_vmem_unmap () - proc: %s", proc->name);
531 return 0;
534 proc->task->page_cover = 0;
535 #endif
536 return 1;
539 void proc_thread_context ()
541 thread_entry (thread_arg);
542 #ifdef ARCH_i386
543 sys_threadclose (0);
544 #endif
547 unsigned proc_thread_create (void *entry, void *arg)
549 proc_t *proc = proc_find (_curr_task);
551 if (!proc)
552 return 0;
554 char *name = proc->name;
556 unsigned p = (unsigned) entry;
558 thread_entry = (void *) p;
559 thread_arg = arg;
561 /* create new process thread */
562 task_t *task = (task_t *) task_create (name, (unsigned) &proc_thread_context, PROC_USER_PRIORITY);
564 if (!task)
565 return 0;
567 /* HACK: set task id same as current task (our process) */
568 if (proc->task) {
569 task->id = proc->task->id;
570 #ifdef ARCH_i386
571 /* use same page_cover too for correctly paging */
572 if (proc->task->page_cover)
573 task->page_cover = proc->task->page_cover;
574 #endif
577 DPRINT (DBG_PROC, "process: new thread '%s' : 0x%x", name, p);
579 /* NOTE: this is pretty needed, because thread argument and entry point is then same as previous */
580 schedule ();
582 return 1;
585 unsigned proc_thread_destroy (proc_t *proc, task_t *task)
587 /* we won't close main process thread */
588 if (proc->task == task)
589 return 0;
591 if (!task_done (task))
592 return 0;
594 DPRINT (DBG_PROC, "process: close thread '%s'", task->name);
596 return 1;
599 unsigned int init_proc (void)
601 proc_list.next = &proc_list;
602 proc_list.prev = &proc_list;
604 proc_kernel.task = task_find (0);
605 #ifdef ARCH_i386
606 proc_kernel.heap = PAGE_MEM_HIGH;
607 #endif
608 proc_kernel.start = 0;
609 proc_kernel.end = 0;
611 return 1;