main.c

Go to the documentation of this file.
00001 
00018 #include "pm.h"
00019 #include <minix/keymap.h>
00020 #include <minix/callnr.h>
00021 #include <minix/com.h>
00022 #include <minix/endpoint.h>
00023 #include <signal.h>
00024 #include <stdlib.h>
00025 #include <fcntl.h>
00026 #include <sys/resource.h>
00027 #include <string.h>
00028 #include "mproc.h"
00029 #include "param.h"
00030 
00031 #include "../../kernel/const.h"
00032 #include "../../kernel/config.h"
00033 #include "../../kernel/type.h"
00034 #include "../../kernel/proc.h"
00035 
00036 FORWARD _PROTOTYPE( void get_work, (void)                               );
00037 FORWARD _PROTOTYPE( void pm_init, (void)                                );
00038 FORWARD _PROTOTYPE( int get_nice_value, (int queue)                     );
00039 FORWARD _PROTOTYPE( void get_mem_chunks, (struct memory *mem_chunks)    );
00040 FORWARD _PROTOTYPE( void patch_mem_chunks, (struct memory *mem_chunks, 
00041         struct mem_map *map_ptr)        );
00042 FORWARD _PROTOTYPE( void do_x86_vm, (struct memory mem_chunks[NR_MEMS]) );
00043 
00044 #define click_to_round_k(n) \
00045         ((unsigned) ((((unsigned long) (n) << CLICK_SHIFT) + 512) / 1024))
00046 
00047 /*===========================================================================*
00048  *                              main                                         *
00049  *===========================================================================*/
00050 PUBLIC int main()
00051 {
00052 /* Main routine of the process manager. */
00053   int result, s, proc_nr;
00054   struct mproc *rmp;
00055   sigset_t sigset;
00056 
00057   pm_init();                    /* initialize process manager tables */
00058 
00059   /* This is PM's main loop-  get work and do it, forever and forever. */
00060   while (TRUE) {
00061         get_work();             /* wait for an PM system call */
00062 
00063         /* Check for system notifications first. Special cases. */
00064         if (call_nr == SYN_ALARM) {
00065                 pm_expire_timers(m_in.NOTIFY_TIMESTAMP);
00066                 result = SUSPEND;               /* don't reply */
00067         } else if (call_nr == SYS_SIG) {        /* signals pending */
00068                 sigset = m_in.NOTIFY_ARG;
00069                 if (sigismember(&sigset, SIGKSIG))  {
00070                         (void) ksig_pending();
00071                 } 
00072                 result = SUSPEND;               /* don't reply */
00073         }
00074         /* Else, if the system call number is valid, perform the call. */
00075         else if ((unsigned) call_nr >= NCALLS) {
00076                 result = ENOSYS;
00077         } else {
00078                 result = (*call_vec[call_nr])();
00079         }
00080 
00081         /* Send the results back to the user to indicate completion. */
00082         if (result != SUSPEND) setreply(who_p, result);
00083 
00084         swap_in();              /* maybe a process can be swapped in? */
00085 
00086         /* Send out all pending reply messages, including the answer to
00087          * the call just made above.  The processes must not be swapped out.
00088          */
00089         for (proc_nr=0, rmp=mproc; proc_nr < NR_PROCS; proc_nr++, rmp++) {
00090                 /* In the meantime, the process may have been killed by a
00091                  * signal (e.g. if a lethal pending signal was unblocked)
00092                  * without the PM realizing it. If the slot is no longer in
00093                  * use or just a zombie, don't try to reply.
00094                  */
00095                 if ((rmp->mp_flags & (REPLY | ONSWAP | IN_USE | ZOMBIE)) ==
00096                    (REPLY | IN_USE)) {
00097                         if ((s=send(rmp->mp_endpoint, &rmp->mp_reply)) != OK) {
00098                                 printf("PM can't reply to %d (%s)\n",
00099                                         rmp->mp_endpoint, rmp->mp_name);
00100                                 panic(__FILE__, "PM can't reply", NO_NUM);
00101                         }
00102                         rmp->mp_flags &= ~REPLY;
00103                 }
00104         }
00105   }
00106   return(OK);
00107 }
00108 
00109 /*===========================================================================*
00110  *                              get_work                                     *
00111  *===========================================================================*/
00112 PRIVATE void get_work()
00113 {
00114 /* Wait for the next message and extract useful information from it. */
00115   if (receive(ANY, &m_in) != OK)
00116         panic(__FILE__,"PM receive error", NO_NUM);
00117   who_e = m_in.m_source;        /* who sent the message */
00118   if(pm_isokendpt(who_e, &who_p) != OK)
00119         panic(__FILE__, "PM got message from invalid endpoint", who_e);
00120   call_nr = m_in.m_type;        /* system call number */
00121 
00122   /* Process slot of caller. Misuse PM's own process slot if the kernel is
00123    * calling. This can happen in case of synchronous alarms (CLOCK) or or 
00124    * event like pending kernel signals (SYSTEM).
00125    */
00126   mp = &mproc[who_p < 0 ? PM_PROC_NR : who_p];
00127   if(who_p >= 0 && mp->mp_endpoint != who_e) {
00128         panic(__FILE__, "PM endpoint number out of sync with source",
00129                 mp->mp_endpoint);
00130   }
00131 }
00132 
00133 /*===========================================================================*
00134  *                              setreply                                     *
00135  *===========================================================================*/
00136 PUBLIC void setreply(proc_nr, result)
00137 int proc_nr;                    /* process to reply to */
00138 int result;                     /* result of call (usually OK or error #) */
00139 {
00140 /* Fill in a reply message to be sent later to a user process.  System calls
00141  * may occasionally fill in other fields, this is only for the main return
00142  * value, and for setting the "must send reply" flag.
00143  */
00144   register struct mproc *rmp = &mproc[proc_nr];
00145 
00146   if(proc_nr < 0 || proc_nr >= NR_PROCS)
00147       panic(__FILE__,"setreply arg out of range", proc_nr);
00148 
00149   rmp->mp_reply.reply_res = result;
00150   rmp->mp_flags |= REPLY;       /* reply pending */
00151 
00152   if (rmp->mp_flags & ONSWAP)
00153         swap_inqueue(rmp);      /* must swap this process back in */
00154 }
00155 
00156 /*===========================================================================*
00157  *                              pm_init                                      *
00158  *===========================================================================*/
00159 PRIVATE void pm_init()
00160 {
00161 /* Initialize the process manager. 
00162  * Memory use info is collected from the boot monitor, the kernel, and
00163  * all processes compiled into the system image. Initially this information
00164  * is put into an array mem_chunks. Elements of mem_chunks are struct memory,
00165  * and hold base, size pairs in units of clicks. This array is small, there
00166  * should be no more than 8 chunks. After the array of chunks has been built
00167  * the contents are used to initialize the hole list. Space for the hole list
00168  * is reserved as an array with twice as many elements as the maximum number
00169  * of processes allowed. It is managed as a linked list, and elements of the
00170  * array are struct hole, which, in addition to storage for a base and size in 
00171  * click units also contain space for a link, a pointer to another element.
00172 */
00173   int s;
00174   static struct boot_image image[NR_BOOT_PROCS];
00175   register struct boot_image *ip;
00176   static char core_sigs[] = { SIGQUIT, SIGILL, SIGTRAP, SIGABRT,
00177                         SIGEMT, SIGFPE, SIGUSR1, SIGSEGV, SIGUSR2 };
00178   static char ign_sigs[] = { SIGCHLD, SIGWINCH, SIGCONT };
00179   static char mess_sigs[] = { SIGTERM, SIGHUP, SIGABRT, SIGQUIT };
00180   register struct mproc *rmp;
00181   register int i;
00182   register char *sig_ptr;
00183   phys_clicks total_clicks, minix_clicks, free_clicks;
00184   message mess;
00185   struct mem_map mem_map[NR_LOCAL_SEGS];
00186   struct memory mem_chunks[NR_MEMS];
00187 
00188   /* Initialize process table, including timers. */
00189   for (rmp=&mproc[0]; rmp<&mproc[NR_PROCS]; rmp++) {
00190         tmr_inittimer(&rmp->mp_timer);
00191   }
00192 
00193   /* Build the set of signals which cause core dumps, and the set of signals
00194    * that are by default ignored.
00195    */
00196   sigemptyset(&core_sset);
00197   for (sig_ptr = core_sigs; sig_ptr < core_sigs+sizeof(core_sigs); sig_ptr++)
00198         sigaddset(&core_sset, *sig_ptr);
00199   sigemptyset(&ign_sset);
00200   for (sig_ptr = ign_sigs; sig_ptr < ign_sigs+sizeof(ign_sigs); sig_ptr++)
00201         sigaddset(&ign_sset, *sig_ptr);
00202 
00203   /* Obtain a copy of the boot monitor parameters and the kernel info struct.  
00204    * Parse the list of free memory chunks. This list is what the boot monitor 
00205    * reported, but it must be corrected for the kernel and system processes.
00206    */
00207   if ((s=sys_getmonparams(monitor_params, sizeof(monitor_params))) != OK)
00208       panic(__FILE__,"get monitor params failed",s);
00209   get_mem_chunks(mem_chunks);
00210   if ((s=sys_getkinfo(&kinfo)) != OK)
00211       panic(__FILE__,"get kernel info failed",s);
00212 
00213   /* Get the memory map of the kernel to see how much memory it uses. */
00214   if ((s=get_mem_map(SYSTASK, mem_map)) != OK)
00215         panic(__FILE__,"couldn't get memory map of SYSTASK",s);
00216   minix_clicks = (mem_map[S].mem_phys+mem_map[S].mem_len)-mem_map[T].mem_phys;
00217   patch_mem_chunks(mem_chunks, mem_map);
00218 
00219   /* Initialize PM's process table. Request a copy of the system image table 
00220    * that is defined at the kernel level to see which slots to fill in.
00221    */
00222   if (OK != (s=sys_getimage(image))) 
00223         panic(__FILE__,"couldn't get image table: %d\n", s);
00224   procs_in_use = 0;                             /* start populating table */
00225   printf("Building process table:");            /* show what's happening */
00226   for (ip = &image[0]; ip < &image[NR_BOOT_PROCS]; ip++) {              
00227         if (ip->proc_nr >= 0) {                 /* task have negative nrs */
00228                 procs_in_use += 1;              /* found user process */
00229 
00230                 /* Set process details found in the image table. */
00231                 rmp = &mproc[ip->proc_nr];      
00232                 strncpy(rmp->mp_name, ip->proc_name, PROC_NAME_LEN); 
00233                 rmp->mp_parent = RS_PROC_NR;
00234                 rmp->mp_nice = get_nice_value(ip->priority);
00235                 sigemptyset(&rmp->mp_sig2mess);
00236                 sigemptyset(&rmp->mp_ignore);   
00237                 sigemptyset(&rmp->mp_sigmask);
00238                 sigemptyset(&rmp->mp_catch);
00239                 if (ip->proc_nr == INIT_PROC_NR) {      /* user process */
00240                         rmp->mp_procgrp = rmp->mp_pid = INIT_PID;
00241                         rmp->mp_flags |= IN_USE; 
00242                 }
00243                 else {                                  /* system process */
00244                         rmp->mp_pid = get_free_pid();
00245                         rmp->mp_flags |= IN_USE | DONT_SWAP | PRIV_PROC; 
00246                         for (sig_ptr = mess_sigs; 
00247                                 sig_ptr < mess_sigs+sizeof(mess_sigs); 
00248                                 sig_ptr++)
00249                         sigaddset(&rmp->mp_sig2mess, *sig_ptr);
00250                 }
00251 
00252                 /* Get kernel endpoint identifier. */
00253                 rmp->mp_endpoint = ip->endpoint;
00254 
00255                 /* Get memory map for this process from the kernel. */
00256                 if ((s=get_mem_map(ip->proc_nr, rmp->mp_seg)) != OK)
00257                         panic(__FILE__,"couldn't get process entry",s);
00258                 if (rmp->mp_seg[T].mem_len != 0) rmp->mp_flags |= SEPARATE;
00259                 minix_clicks += rmp->mp_seg[S].mem_phys + 
00260                         rmp->mp_seg[S].mem_len - rmp->mp_seg[T].mem_phys;
00261                 patch_mem_chunks(mem_chunks, rmp->mp_seg);
00262 
00263                 /* Tell FS about this system process. */
00264                 mess.PR_SLOT = ip->proc_nr;
00265                 mess.PR_PID = rmp->mp_pid;
00266                 mess.PR_ENDPT = rmp->mp_endpoint;
00267                 if (OK != (s=send(FS_PROC_NR, &mess)))
00268                         panic(__FILE__,"can't sync up with FS", s);
00269                 printf(" %s", ip->proc_name);   /* display process name */
00270         }
00271   }
00272   printf(".\n");                                /* last process done */
00273 
00274   /* Override some details. INIT, PM, FS and RS are somewhat special. */
00275   mproc[PM_PROC_NR].mp_pid = PM_PID;            /* PM has magic pid */
00276   mproc[RS_PROC_NR].mp_parent = INIT_PROC_NR;   /* INIT is root */
00277   sigfillset(&mproc[PM_PROC_NR].mp_ignore);     /* guard against signals */
00278 
00279   /* Tell FS that no more system processes follow and synchronize. */
00280   mess.PR_ENDPT = NONE;
00281   if (sendrec(FS_PROC_NR, &mess) != OK || mess.m_type != OK)
00282         panic(__FILE__,"can't sync up with FS", NO_NUM);
00283 
00284 #if ENABLE_BOOTDEV
00285   /* Possibly we must correct the memory chunks for the boot device. */
00286   if (kinfo.bootdev_size > 0) {
00287       mem_map[T].mem_phys = kinfo.bootdev_base >> CLICK_SHIFT;
00288       mem_map[T].mem_len = 0;
00289       mem_map[D].mem_len = (kinfo.bootdev_size+CLICK_SIZE-1) >> CLICK_SHIFT;
00290       patch_mem_chunks(mem_chunks, mem_map);
00291   }
00292 #endif /* ENABLE_BOOTDEV */
00293 
00294   /* Withhold some memory from x86 VM */
00295   do_x86_vm(mem_chunks);
00296 
00297   /* Initialize tables to all physical memory and print memory information. */
00298   printf("Physical memory:");
00299   mem_init(mem_chunks, &free_clicks);
00300   total_clicks = minix_clicks + free_clicks;
00301   printf(" total %u KB,", click_to_round_k(total_clicks));
00302   printf(" system %u KB,", click_to_round_k(minix_clicks));
00303   printf(" free %u KB.\n", click_to_round_k(free_clicks));
00304 }
00305 
00306 /*===========================================================================*
00307  *                              get_nice_value                               *
00308  *===========================================================================*/
00309 PRIVATE int get_nice_value(queue)
00310 int queue;                              /* store mem chunks here */
00311 {
00312 /* Processes in the boot image have a priority assigned. The PM doesn't know
00313  * about priorities, but uses 'nice' values instead. The priority is between 
00314  * MIN_USER_Q and MAX_USER_Q. We have to scale between PRIO_MIN and PRIO_MAX.
00315  */ 
00316   int nice_val = (queue - USER_Q) * (PRIO_MAX-PRIO_MIN+1) / 
00317       (MIN_USER_Q-MAX_USER_Q+1);
00318   if (nice_val > PRIO_MAX) nice_val = PRIO_MAX; /* shouldn't happen */
00319   if (nice_val < PRIO_MIN) nice_val = PRIO_MIN; /* shouldn't happen */
00320   return nice_val;
00321 }
00322 
00323 #if _WORD_SIZE == 2
00324 /* In real mode only 1M can be addressed, and in 16-bit protected we can go
00325  * no further than we can count in clicks.  (The 286 is further limited by
00326  * its 24 bit address bus, but we can assume in that case that no more than
00327  * 16M memory is reported by the BIOS.)
00328  */
00329 #define MAX_REAL        0x00100000L
00330 #define MAX_16BIT       (0xFFF0L << CLICK_SHIFT)
00331 #endif
00332 
00333 /*===========================================================================*
00334  *                              get_mem_chunks                               *
00335  *===========================================================================*/
00336 PRIVATE void get_mem_chunks(mem_chunks)
00337 struct memory *mem_chunks;                      /* store mem chunks here */
00338 {
00339 /* Initialize the free memory list from the 'memory' boot variable.  Translate
00340  * the byte offsets and sizes in this list to clicks, properly truncated. Also
00341  * make sure that we don't exceed the maximum address space of the 286 or the
00342  * 8086, i.e. when running in 16-bit protected mode or real mode.
00343  */
00344   long base, size, limit;
00345   char *s, *end;                        /* use to parse boot variable */ 
00346   int i, done = 0;
00347   struct memory *memp;
00348 #if _WORD_SIZE == 2
00349   unsigned long max_address;
00350   struct machine machine;
00351   if (OK != (i=sys_getmachine(&machine)))
00352         panic(__FILE__, "sys_getmachine failed", i);
00353 #endif
00354 
00355   /* Initialize everything to zero. */
00356   for (i = 0; i < NR_MEMS; i++) {
00357         memp = &mem_chunks[i];          /* next mem chunk is stored here */
00358         memp->base = memp->size = 0;
00359   }
00360   
00361   /* The available memory is determined by MINIX' boot loader as a list of 
00362    * (base:size)-pairs in boothead.s. The 'memory' boot variable is set in
00363    * in boot.s.  The format is "b0:s0,b1:s1,b2:s2", where b0:s0 is low mem,
00364    * b1:s1 is mem between 1M and 16M, b2:s2 is mem above 16M. Pairs b1:s1 
00365    * and b2:s2 are combined if the memory is adjacent. 
00366    */
00367   s = find_param("memory");             /* get memory boot variable */
00368   for (i = 0; i < NR_MEMS && !done; i++) {
00369         memp = &mem_chunks[i];          /* next mem chunk is stored here */
00370         base = size = 0;                /* initialize next base:size pair */
00371         if (*s != 0) {                  /* get fresh data, unless at end */     
00372 
00373             /* Read fresh base and expect colon as next char. */ 
00374             base = strtoul(s, &end, 0x10);              /* get number */
00375             if (end != s && *end == ':') s = ++end;     /* skip ':' */ 
00376             else *s=0;                  /* terminate, should not happen */
00377 
00378             /* Read fresh size and expect comma or assume end. */ 
00379             size = strtoul(s, &end, 0x10);              /* get number */
00380             if (end != s && *end == ',') s = ++end;     /* skip ',' */
00381             else done = 1;
00382         }
00383         limit = base + size;    
00384 #if _WORD_SIZE == 2
00385         max_address = machine.protected ? MAX_16BIT : MAX_REAL;
00386         if (limit > max_address) limit = max_address;
00387 #endif
00388         base = (base + CLICK_SIZE-1) & ~(long)(CLICK_SIZE-1);
00389         limit &= ~(long)(CLICK_SIZE-1);
00390         if (limit <= base) continue;
00391         memp->base = base >> CLICK_SHIFT;
00392         memp->size = (limit - base) >> CLICK_SHIFT;
00393   }
00394 }
00395 
00396 /*===========================================================================*
00397  *                              patch_mem_chunks                             *
00398  *===========================================================================*/
00399 PRIVATE void patch_mem_chunks(mem_chunks, map_ptr)
00400 struct memory *mem_chunks;                      /* store mem chunks here */
00401 struct mem_map *map_ptr;                        /* memory to remove */
00402 {
00403 /* Remove server memory from the free memory list. The boot monitor
00404  * promises to put processes at the start of memory chunks. The 
00405  * tasks all use same base address, so only the first task changes
00406  * the memory lists. The servers and init have their own memory
00407  * spaces and their memory will be removed from the list. 
00408  */
00409   struct memory *memp;
00410   for (memp = mem_chunks; memp < &mem_chunks[NR_MEMS]; memp++) {
00411         if (memp->base == map_ptr[T].mem_phys) {
00412                 memp->base += map_ptr[T].mem_len + map_ptr[D].mem_len;
00413                 memp->size -= map_ptr[T].mem_len + map_ptr[D].mem_len;
00414         }
00415   }
00416 }
00417 
00418 #define PAGE_SIZE       4096
00419 #define PAGE_TABLE_COVER (1024*PAGE_SIZE)
00420 /*=========================================================================*
00421  *                              do_x86_vm                                  *
00422  *=========================================================================*/
00423 PRIVATE void do_x86_vm(mem_chunks)
00424 struct memory mem_chunks[NR_MEMS];
00425 {
00426         phys_bytes high, bytes;
00427         phys_clicks clicks, base_click;
00428         unsigned pages;
00429         int i, r;
00430 
00431         /* Compute the highest memory location */
00432         high= 0;
00433         for (i= 0; i<NR_MEMS; i++)
00434         {
00435                 if (mem_chunks[i].size == 0)
00436                         continue;
00437                 if (mem_chunks[i].base + mem_chunks[i].size > high)
00438                         high= mem_chunks[i].base + mem_chunks[i].size;
00439         }
00440 
00441         high <<= CLICK_SHIFT;
00442 #if VERBOSE_VM
00443         printf("do_x86_vm: found high 0x%x\n", high);
00444 #endif
00445 
00446         /* The number of pages we need is one for the page directory, enough
00447          * page tables to cover the memory, and one page for alignement.
00448          */
00449         pages= 1 + (high + PAGE_TABLE_COVER-1)/PAGE_TABLE_COVER + 1;
00450         bytes= pages*PAGE_SIZE;
00451         clicks= (bytes + CLICK_SIZE-1) >> CLICK_SHIFT;
00452 
00453 #if VERBOSE_VM
00454         printf("do_x86_vm: need %d pages\n", pages);
00455         printf("do_x86_vm: need %d bytes\n", bytes);
00456         printf("do_x86_vm: need %d clicks\n", clicks);
00457 #endif
00458 
00459         for (i= 0; i<NR_MEMS; i++)
00460         {
00461                 if (mem_chunks[i].size <= clicks)
00462                         continue;
00463                 break;
00464         }
00465         if (i >= NR_MEMS)
00466                 panic("PM", "not enough memory for VM page tables?", NO_NUM);
00467         base_click= mem_chunks[i].base;
00468         mem_chunks[i].base += clicks;
00469         mem_chunks[i].size -= clicks;
00470 
00471 #if VERBOSE_VM
00472         printf("do_x86_vm: using 0x%x clicks @ 0x%x\n", clicks, base_click);
00473 #endif
00474         r= sys_vm_setbuf(base_click << CLICK_SHIFT, clicks << CLICK_SHIFT,
00475                 high);
00476         if (r != 0)
00477                 printf("do_x86_vm: sys_vm_setbuf failed: %d\n", r);
00478 }

Generated on Fri Apr 14 22:56:41 2006 for minix by  doxygen 1.4.6