Hash-by-pid logic (used, e.g., for signal delivery) is all gathered in pid.c . The two main structures are procdir[] and pidhash[]. The underlying idea, as far as I understand it, is as follows: in order to look things up (specifically, proc_t by its PID) trough a hashtable, one needs to keep the collision list pointers somewhere. The lookup keys being simply integers, some kind of a struct is needed to keep both the integer PID and the collision linked list. Hence "struct pid". pidhash[] entries point to pid structs, which contain the collision list pointer pid_link, and the "slot" index of the corresponding proc_t pointer in procdir[], pid_prslot. This pid_prslot provides a "back-pointer" from struct pid to the corresponding proc_t . procdir[] and pidhash[] are kept in sync by proc_t-creating and freeing functions (described below). procdir[] doubles as a freelist of proc_t's (pointed to by procentfree). Note that most of these are "static", i.e., are explicitly internal to this file's scope; pid_prslot is also not findable from elsewhere. However, notice in proc.h the macro proc.h:402 #define p_slot p_pidp->pid_prslot and its use in prsubr.c, prcontrol.c, proc.c, and sign.c [same deal with the macro proc.h:418 #define p_lock p_lockp->pl_lock ] ---- Pid-hash structures are created in pid_init() in pid.c startup() -> kern_setup1() -> pid_init() http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/intel/ia32/os/sundep.c#231 Observe the initialization of p0 (proc_t for the first process, "sched") and its related struct pid pid0 pid0 is statically defined in pid.c, but p0 is in param.c , for the reason explained in http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/conf/param.c#71 --- http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/os/main.c#proc_sched /* well known processes */ 82 proc_t *proc_sched; /* memory scheduler */ 83 proc_t *proc_init; /* init */ 84 proc_t *proc_pageout; /* pageout daemon */ 85 proc_t *proc_fsflush; /* fsflush daemon */ 86 87 pgcnt_t maxmem; /* Maximum available memory in pages. */ 88 pgcnt_t freemem; /* Current available memory in pages. */ 89 int audit_active; 90 int interrupts_unleashed; /* set when we do the first spl0() */ 91 92 kmem_cache_t *process_cache; /* kmem cache for proc structures */ --- Read through pid_init() to see the pid-hash structures created. The so-called "hash" is in fact, that's just zeroing out the higher bits of the integer PID: static int pid_hashlen = 4; /* desired average hash chain length */ static int pid_hashsz; /* number of buckets in the hash table */ /* 4096 on my system, got set by pid_hashsz = 1 << highbit(v.v_proc / pid_hashlen); */ #define HASHPID(pid) (pidhash[((pid)&(pid_hashsz-1))]) Note that HASHPID(pid) stands for the pidhash[] element, and is used as an lvalue . --- The structure of the hash table made out of pidhash[] and links in struct pid is made obvious in the following functions: pid_lookup: Locate the "struct pid" by the integer PID by walking the collision list. pid_allocate: See new pid struct being allocated and linked into pidhash and "point" back to procdir's "prslot" where the associated proc_t goes. For one point where this function is called, see getproc() in fork.c, which creates a new proc_t: http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/os/fork.c#951 BTW, on line 984 we see nproc++ (the number of processes on the system), and then the newly created proc_t linked at the head of the activie process list. More of fork()-ed child's setup follows. Note: If you are confused as to the use of ->p_lock off of proc_t* because the proc_t struct does not contain any member named p_lock, you are right. Notice the following macro in proc.h: l.418 #define p_lock p_lockp->pl_lock --- Observe the permutation induced by pid_getlockslot on procdir integer slot integers: PLOCK_SHIFT 3 static int pid_getlockslot(int prslot) { int even = (v.v_proc >> PLOCK_SHIFT) << PLOCK_SHIFT; int perlap = even >> PLOCK_SHIFT; if (prslot >= even) return (prslot); return (((prslot % perlap) << PLOCK_SHIFT) + (prslot / perlap)); } 0 8 16 24 32 40 48 56 64 72 80 88 96 104 112 120 128 136 144 152 160 168 176 184 192 200 208 216 224 232 240 248 256 264 272 280 288 296 304 312 320 328 336 344 352 360 368 376 384 392 400 408 416 424 432 440 ... ... 9728 9736 9744 9752 9760 1 9 17 25 33 41 49 57 65 73 81 89 97 105 113 121 129 137 145 ... This is done to prevent spin lock "lock"-ed and cached bytes for sequentially created processes from being in the same cache line. See explanations of the Linux spinlock in the previous lecture, and Chapter 17 in the book. ---- Unlinking and freeing processes: proc_entry_free(): [called from pid_ext(), which after that frees the proc_t] Observe the proc_t structure being put on the head of the freelist: procentree now points to it. procdir[pidp->pid_prslot].pe_next = procentfree; procentfree = &procdir[pidp->pid_prslot]; The structure is being referred to through it slot number in procdir, contained in pid: pidp->pid_prslot. ---- A use case: pid_entry(): called in pr_readdir_procdir(), not much used otherwise Gets the proc_t entry from the slot number in procdir. The if-clause at 550 looks confusing (well, it confused me on a first reading). Its meaning: If the pointer in the procdir slot is pointing *inside* the procdir itself, this means the slot is free and not associated with an active process's proc_t. So return NULL. Otherwise there is an active process associated with that slot. One more check on line 553: perhaps it is being set up? (prp->p_stat == SIDL, see http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/sys/proc.h#SIDL for process states) If not, we've found an active process. ---- Next time we look into the finer points of adaptive locks.