A thread is a struct proc that shares three counted objects with others of
its group: the address space (struct mm: page tables, heap top, VMAs and a
lock for changing them), the descriptor table, and the signal dispositions.
clone() shares what its flags say and fork() copies.

- clone(): pthread_create's shape (VM, FILES, SIGHAND, THREAD, SETTLS,
  PARENT_SETTID, CHILD_CLEARTID) and posix_spawn's (VM, VFORK, a stack).
- futex: WAIT, WAIT_BITSET with timeouts, WAKE, REQUEUE, CMP_REQUEUE, keyed
  by the word's kernel address so shared pages meet. set_tid_address keeps
  the word that exit zeroes and wakes, for pthread_join.
- exit ends a thread; exit_group, a fatal signal, or the leader exiting ends
  them all. reapd frees exited threads; the leader is reaped by its parent
  once the rest are gone. getpid is the group, gettid the thread.
- Stops and resumes reach every thread; other process signals the leader.
- exec kills the other threads and unshares the dispositions.
- No TLB shootdown, so pages unmapped from a shared address space are freed
  only once every CPU has reloaded CR3 or is idle.
- procfs: /proc lists processes, task/ lists threads, comm, Threads:, Tgid.
- The console honours VMIN 0 / VTIME (btop's input drain hung on it).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
Nexoniarz 2026-10-25 03:03:50 +01:00
commit f4d7f9100d
19 changed files with 1300 additions and 248 deletions

View file

@ -62,6 +62,31 @@ void pciuser_release_all(struct proc*);
int pciuser_owner_pid(int idx); int pciuser_owner_pid(int idx);
int proc_reincarnate(int status); int proc_reincarnate(int status);
int sig_default_stops(int); int sig_default_stops(int);
struct mm* mm_alloc(void);
void mm_get(struct mm*);
void mm_put(struct mm*);
int mm_shared(struct mm*);
void mm_lock(struct mm*);
void mm_unlock(struct mm*);
struct files* files_alloc(void);
void files_get(struct files*);
void files_put(struct files*);
void files_lock(void);
void files_unlock(void);
struct sighand* sighand_alloc(void);
void sighand_get(struct sighand*);
void sighand_put(struct sighand*);
struct proc* proc_leader(struct proc*);
int proc_thread_count(int);
void thread_exit(void) __attribute__((noreturn));
int futex_wake_key(void*, int);
int futex_op(addr_t, int, int, addr_t, addr_t, int);
int clone_proc(uint64, uint64, uint64, uint64, uint64);
void proc_exec_threads(void);
void proc_exec_done(void);
void thread_reaper_start(void);
void vm_defer_tick(void);
void mm_release_shared(struct mm*);
void proc_stop(int); void proc_stop(int);
int sig_attention(struct proc*); int sig_attention(struct proc*);
int sig_blocking_check(void); int sig_blocking_check(void);
@ -774,7 +799,7 @@ uint64 memfd_size(int id);
int memfd_truncate(int id, uint64 len); int memfd_truncate(int id, uint64 len);
int memfd_rw(int id, int user, uint64 buf, uint64 off, int n, int writing); int memfd_rw(int id, int user, uint64 buf, uint64 off, int n, int writing);
void shm_detach(int id); void shm_detach(int id);
void proc_release_shared(struct proc *);
int copy_vma_range(pml4e_t *dstpgdir, pml4e_t *srcpgdir, uint64 va, uint64 length, int is_fb); int copy_vma_range(pml4e_t *dstpgdir, pml4e_t *srcpgdir, uint64 va, uint64 length, int is_fb);
void clearpteu(pml4e_t *pgdir, char *uva); void clearpteu(pml4e_t *pgdir, char *uva);

View file

@ -31,6 +31,40 @@ struct vma {
int shmid; // shared segment this came from, or -1 int shmid; // shared segment this came from, or -1
}; };
// --- What threads share ---------------------------------------------------
//
// A thread is a struct proc like any other, scheduled like any other. What
// makes several of them one program is that they point at the same three
// objects below, each counted, each freed with its last user: clone() with
// CLONE_VM, CLONE_FILES and CLONE_SIGHAND shares them, fork() copies them.
// The address space. pgdir is mirrored in every struct proc that uses it,
// because the scheduler, trapasm and half the kernel read it from there; it
// only changes at exec, which makes a new mm.
struct mm {
int ref;
int locked; // mm_lock(): one mmap/munmap/brk at a time
uint64 sz; // top of the heap; p->sz mirrors it
uint64 mmap_next; // next free mmap VA (bump allocator, see sys_mmap)
pml4e_t *pgdir;
struct vma vma[NVMA]; // VMA tracking for mmap
};
// The descriptor table, and FD_CLOEXEC beside it.
struct files {
int ref;
struct file *ofile[NOFILE];
char flags[NOFILE];
};
// Signal dispositions: what sigaction() sets.
struct sighand {
int ref;
uint64 handler[32]; // 0 SIG_DFL, 1 SIG_IGN, else an address
uint64 restorer[32];
uint64 flags[32]; // sa_flags; SA_RESTART is the one read
};
// Per-CPU state // Per-CPU state
struct cpu { struct cpu {
uchar id; uchar id;
@ -176,6 +210,8 @@ struct procinfo {
char exe[64]; // full path, for /proc/<pid>/exe char exe[64]; // full path, for /proc/<pid>/exe
int kthread; // a kernel thread, not a program int kthread; // a kernel thread, not a program
int stopped; // stopped by a signal (ps shows T) int stopped; // stopped by a signal (ps shows T)
int tgid; // the process this thread belongs to
int nthreads; // threads in that process
}; };
struct proc { struct proc {
@ -191,7 +227,7 @@ struct proc {
void *chan; // [+0x40] void *chan; // [+0x40]
int killed; // [+0x48] int killed; // [+0x48]
int _pad; // [+0x4c] int _pad; // [+0x4c]
struct file *ofile[NOFILE]; // Open files [+0x50] struct files *files; // Open files, shared by threads [+0x50]
struct inode *cwd; // follows struct inode *cwd; // follows
char name[16]; // char name[16]; //
// The path exec() was given. `name` is only the last component and only 15 // The path exec() was given. `name` is only the last component and only 15
@ -238,7 +274,6 @@ struct proc {
// alarmd, in kernel/proc/proc.c, is what posts it. // alarmd, in kernel/proc/proc.c, is what posts it.
uint alarm_at; uint alarm_at;
uint alarm_every; uint alarm_every;
char ofile_flags[NOFILE];
// Real signal delivery (added for SIGCHLD — see kernel/sys/syscall.c's // Real signal delivery (added for SIGCHLD — see kernel/sys/syscall.c's
// deliver_signals() and kernel/proc/proc.c's exit()). sig_handler[sig]/ // deliver_signals() and kernel/proc/proc.c's exit()). sig_handler[sig]/
@ -247,9 +282,7 @@ struct proc {
// the pre-signal trapframe so sys_rt_sigreturn() can restore it; in_signal // the pre-signal trapframe so sys_rt_sigreturn() can restore it; in_signal
// prevents re-entering a handler while one is already running (we don't // prevents re-entering a handler while one is already running (we don't
// support nested signal delivery). // support nested signal delivery).
uint64 sig_handler[32]; struct sighand *sighand; // dispositions, shared by threads
uint64 sig_restorer[32];
uint64 sig_flags[32]; // sa_flags; SA_RESTART is the one read
struct trapframe saved_sigtf; struct trapframe saved_sigtf;
int in_signal; int in_signal;
@ -260,8 +293,7 @@ struct proc {
uint64 sigsusp_oldmask; uint64 sigsusp_oldmask;
int sigsusp_active; int sigsusp_active;
struct vma vma[NVMA]; // VMA tracking for mmap struct mm *mm; // address space, shared by threads
uint64 mmap_next; // next free mmap VA (bump allocator, see sys_mmap)
// ABI translation (Linux compat syscall number map) // ABI translation (Linux compat syscall number map)
int *syscall_map; int *syscall_map;
@ -339,6 +371,19 @@ struct proc {
int stopsig; int stopsig;
int stop_report; int stop_report;
int cont_report; int cont_report;
// Threads. tgid is the pid of the thread group's leader -- what getpid()
// returns -- and equal to pid for every ordinary process.
int tgid;
uint64 clear_child_tid; // CLONE_CHILD_CLEARTID: zeroed and woken at exit
void *futex_key; // the futex word this thread waits on, or 0
int futex_woken;
int autoreap; // a thread: reapd frees it, nobody waits for it
struct proc *vfork_parent; // CLONE_VFORK: released at exec or exit
int vfork_wait; // the vfork child this process is waiting on
int thread_dying; // killed with its group: exits as a thread
int group_status; // a thread's exit_group status, for the leader
int group_status_set;
}; };
#define FD_CLOEXEC 1 #define FD_CLOEXEC 1

View file

@ -39,6 +39,7 @@
#define ENAMETOOLONG 36 // File name too long #define ENAMETOOLONG 36 // File name too long
#define ENOLCK 37 // No record locks available #define ENOLCK 37 // No record locks available
#define ENOSYS 38 // Function not implemented #define ENOSYS 38 // Function not implemented
#define ETIMEDOUT 110 // Connection timed out (and a futex wait that ran out)
#define ENOTEMPTY 39 // Directory not empty #define ENOTEMPTY 39 // Directory not empty
#define ELOOP 40 // Too many symbolic links encountered #define ELOOP 40 // Too many symbolic links encountered

View file

@ -636,6 +636,11 @@ oom:
return 0; return 0;
} }
// CR3 loads per processor, and what each has loaded; see "Pages another
// processor may still have in its TLB" below.
static volatile uint64 cr3_loads[NCPU];
static pml4e_t *volatile cpu_pgdir[NCPU];
void void
switchuvm(struct proc *p) switchuvm(struct proc *p)
{ {
@ -650,6 +655,8 @@ switchuvm(struct proc *p)
// every process that is not hosting a driver. // every process that is not hosting a driver.
ioperm_switch(p); ioperm_switch(p);
cr3_loads[cpunum()]++;
cpu_pgdir[cpunum()] = p->pgdir;
lcr3(v2p(p->pgdir)); lcr3(v2p(p->pgdir));
popcli(); popcli();
} }
@ -695,9 +702,109 @@ walkpgdir(pml4e_t *pml4, const void *va, int alloc)
return (pte_t*)&pgtab[PTX(va)]; return (pte_t*)&pgtab[PTX(va)];
} }
// --- Pages another processor may still have in its TLB -----------------
//
// Threads put one address space on several processors at once, and there is
// no shootdown IPI: unmapping a page here invalidates it in this TLB only.
// Freed at once, it could be handed to someone else while a thread on another
// CPU still reaches it through a stale entry -- a write into a stranger's
// memory with no fault to show for it.
//
// So a page unmapped from a shared address space waits. Every CR3 load is
// counted per processor, and a batch of pages is freed once every processor
// has reloaded CR3 since the batch was closed, or is not running a user
// address space at all. A processor reloads at every switch, so a batch waits
// a scheduling slice or two.
#define DEFER_MAX 2048
struct defer_batch {
int n;
uint64 snap[NCPU];
uint64 pa[DEFER_MAX];
};
static struct defer_batch defer_q[2];
static int defer_open; // which of the two is filling
static int defer_closed_valid; // the other one is waiting to be freed
static struct spinlock deferlock;
static int defer_init;
static int
defer_ready(struct defer_batch *b)
{
for (int c = 0; c < ncpu; c++)
if (cr3_loads[c] == b->snap[c] && cpu_pgdir[c] != 0)
return 0;
return 1;
}
// With deferlock. Frees the closed batch if it can, then closes the open one.
static void
defer_reclaim(void)
{
struct defer_batch *closed = &defer_q[defer_open ^ 1];
if (defer_closed_valid && defer_ready(closed)) {
for (int i = 0; i < closed->n; i++)
kfree((char*)P2V(closed->pa[i]));
closed->n = 0;
defer_closed_valid = 0;
}
struct defer_batch *open = &defer_q[defer_open];
if (!defer_closed_valid && open->n > 0) {
for (int c = 0; c < ncpu; c++)
open->snap[c] = cr3_loads[c];
defer_closed_valid = 1;
defer_open ^= 1;
}
}
void
vm_defer_tick(void)
{
if (!defer_init)
return;
acquire(&deferlock);
defer_reclaim();
release(&deferlock);
}
static void
defer_free(uint64 pa)
{
if (!defer_init) {
initlock(&deferlock, "vmdefer");
defer_init = 1;
}
for (;;) {
acquire(&deferlock);
struct defer_batch *open = &defer_q[defer_open];
if (open->n == DEFER_MAX)
defer_reclaim();
open = &defer_q[defer_open];
if (open->n < DEFER_MAX) {
open->pa[open->n++] = pa;
release(&deferlock);
return;
}
release(&deferlock);
yield(); // both batches full: let the CPUs switch
}
}
// Free a user page just unmapped from pgdir: now, or once no TLB can hold it.
static void
user_page_free(pml4e_t *pgdir, uint64 pa)
{
struct proc *p = myproc();
if (p && p->pgdir == pgdir && mm_shared(p->mm))
defer_free(pa);
else
kfree((char*)P2V(pa));
}
void void
switchkvm(void) switchkvm(void)
{ {
cr3_loads[cpunum()]++;
cpu_pgdir[cpunum()] = 0;
lcr3(v2p(kpml4)); lcr3(v2p(kpml4));
} }
@ -733,9 +840,9 @@ vm_unmap_range(pml4e_t *pgdir, uint64 va, uint64 length)
pte_t *pte = walkpgdir(pgdir, (void*)a, 0); pte_t *pte = walkpgdir(pgdir, (void*)a, 0);
if (pte && (*pte & PTE_P)) { if (pte && (*pte & PTE_P)) {
uint64 pa = PTE_ADDR(*pte); uint64 pa = PTE_ADDR(*pte);
kfree((char*)P2V(pa));
*pte = 0; *pte = 0;
invlpg((void*)a); invlpg((void*)a);
user_page_free(pgdir, pa);
} }
} }
} }
@ -883,11 +990,10 @@ deallocuvm(pml4e_t *pgdir, uint64 oldsz, uint64 newsz)
pa = PTE_ADDR(*pte); pa = PTE_ADDR(*pte);
if(pa == 0) if(pa == 0)
panic("kfree"); panic("kfree");
if(pa < PHYSTOP) {
char *v = P2V(pa);
kfree(v);
}
*pte = 0; *pte = 0;
invlpg((void*)a);
if(pa < PHYSTOP)
user_page_free(pgdir, pa);
} }
} }
return newsz; return newsz;

View file

@ -711,9 +711,21 @@ consoleread(struct inode *ip, uint64 off, char *dst, int n)
iunlock(ip); iunlock(ip);
target = n; target = n;
uint t0 = ticks;
acquire(&input_lock); acquire(&input_lock);
while(n > 0){ while(n > 0){
while(v->in.r == v->in.w){ while(v->in.r == v->in.w){
// Raw mode with VMIN 0 is a poll: VTIME tenths of a second at most, and
// none at all when that is 0 too. It waited for a key regardless, and
// btop -- which drains its input with exactly such reads -- hung on the
// read after the last key, so `q` never quit it.
if (!(v->tio.c_lflag & ICANON) && v->tio.c_cc[VMIN] == 0 &&
(target - n > 0 ||
(uint)(ticks - t0) >= (uint)v->tio.c_cc[VTIME] * (HZ / 10))) {
release(&input_lock);
ilock(ip);
return target - n;
}
if (v->in.eof) { if (v->in.eof) {
v->in.eof = 0; v->in.eof = 0;
release(&input_lock); release(&input_lock);
@ -752,7 +764,11 @@ consoleread(struct inode *ip, uint64 off, char *dst, int n)
acquire(&input_lock); acquire(&input_lock);
continue; continue;
} }
sleep(&v->in.r, &input_lock); // A VTIME wait has to see the clock move as well as a key arrive.
if (!(v->tio.c_lflag & ICANON) && v->tio.c_cc[VMIN] == 0)
sleep(&ticks, &input_lock);
else
sleep(&v->in.r, &input_lock);
} }
c = v->in.buf[v->in.r++ % VT_INPUT_BUF]; c = v->in.buf[v->in.r++ % VT_INPUT_BUF];
*dst++ = c; *dst++ = c;

View file

@ -407,6 +407,7 @@ kmain(uint64 fb_addr)
netuser_init(); netuser_init();
dbg_thread_start(); dbg_thread_start();
alarm_thread_start(); alarm_thread_start();
thread_reaper_start();
if (boot_verbose) cprintf("verbose boot: dbg thread started\n"); if (boot_verbose) cprintf("verbose boot: dbg thread started\n");
// The greeting is not printed here. // The greeting is not printed here.

View file

@ -664,7 +664,7 @@ do_dma(uint64 argp)
struct vma *v = 0; struct vma *v = 0;
for (int i = 0; i < NVMA; i++) for (int i = 0; i < NVMA; i++)
if (!p->vma[i].used) { v = &p->vma[i]; break; } if (!p->mm->vma[i].used) { v = &p->mm->vma[i]; break; }
if (!v) { if (!v) {
blk->proc = 0; blk->proc = 0;
return -ENOMEM; return -ENOMEM;
@ -672,9 +672,9 @@ do_dma(uint64 argp)
// Its own mapping in the caller, taken from the same bump allocator mmap // Its own mapping in the caller, taken from the same bump allocator mmap
// uses so the two cannot collide. // uses so the two cannot collide.
if (p->mmap_next == 0) if (p->mm->mmap_next == 0)
p->mmap_next = 0x40000000; p->mm->mmap_next = 0x40000000;
uint64 va = p->mmap_next; uint64 va = p->mm->mmap_next;
if (va + size < va || va + size > KERNBASE) { if (va + size < va || va + size > KERNBASE) {
blk->proc = 0; blk->proc = 0;
return -ENOMEM; return -ENOMEM;
@ -685,7 +685,7 @@ do_dma(uint64 argp)
blk->proc = 0; blk->proc = 0;
return -ENOMEM; return -ENOMEM;
} }
p->mmap_next += size; p->mm->mmap_next += size;
uint64 phys = dma_phys(kva); uint64 phys = dma_phys(kva);
if (mappages(p->pgdir, (void *)va, size, phys, PTE_W | PTE_U) < 0) { if (mappages(p->pgdir, (void *)va, size, phys, PTE_W | PTE_U) < 0) {

View file

@ -369,7 +369,8 @@ exec_elf(char *path, struct argsrc *av, struct argsrc *ev)
uint64 sz, sp; uint64 sz, sp;
struct elfhdr elf; struct elfhdr elf;
struct inode *ip; struct inode *ip;
pml4e_t *pgdir, *oldpgdir; pml4e_t *pgdir;
struct mm *oldmm, *newmm;
// The arguments are measured before anything is loaded, so that too many // The arguments are measured before anything is loaded, so that too many
// of them is E2BIG with nothing to undo -- and so the stack can be sized to // of them is E2BIG with nothing to undo -- and so the stack can be sized to
@ -540,10 +541,10 @@ exec_elf(char *path, struct argsrc *av, struct argsrc *ev)
safestrcpy(myproc()->exe, path, sizeof(myproc()->exe)); safestrcpy(myproc()->exe, path, sizeof(myproc()->exe));
for(int i = 0; i < NOFILE; i++){ for(int i = 0; i < NOFILE; i++){
if(myproc()->ofile[i] && (myproc()->ofile_flags[i] & FD_CLOEXEC)){ if(myproc()->files->ofile[i] && (myproc()->files->flags[i] & FD_CLOEXEC)){
fileclose(myproc()->ofile[i]); fileclose(myproc()->files->ofile[i]);
myproc()->ofile[i] = 0; myproc()->files->ofile[i] = 0;
myproc()->ofile_flags[i] = 0; myproc()->files->flags[i] = 0;
} }
} }
@ -557,18 +558,19 @@ exec_elf(char *path, struct argsrc *av, struct argsrc *ev)
myproc()->map_size = 0; myproc()->map_size = 0;
} }
// Detach shared segments before anything forgets which mappings they were. // A new image is a new address space: a fresh mm, and the old one dropped
// // at the end. mm_put() detaches shared segments before freeing anything,
// This has to happen while myproc()->pgdir and myproc()->vma[] still describe the // and does nothing at all while a vfork parent is still using it.
// *old* image: the memset below erases the bookkeeping, and freevm() at the if ((newmm = mm_alloc()) == 0)
// end would then free a shared memory segment's pages as though they were goto bad;
// ordinary heap. Doing it here rather than there is the difference between proc_exec_threads();
// detaching a segment and corrupting whoever else has it mapped. newmm->pgdir = pgdir;
proc_release_shared(myproc()); newmm->sz = sz;
oldmm = myproc()->mm;
oldpgdir = myproc()->pgdir; myproc()->mm = newmm;
myproc()->pgdir = pgdir; myproc()->pgdir = pgdir;
myproc()->sz = sz; myproc()->sz = sz;
pgdir = 0; // newmm owns it now; not bad:'s to free
// New address space: reset all per-process mmap/signal/TLS state. // New address space: reset all per-process mmap/signal/TLS state.
// sig_handler/sig_restorer point into the *old* image — POSIX resets // sig_handler/sig_restorer point into the *old* image — POSIX resets
@ -579,26 +581,25 @@ exec_elf(char *path, struct argsrc *av, struct argsrc *ev)
// Ignored signals stay ignored, and the mask is inherited (execve(2)): // Ignored signals stay ignored, and the mask is inherited (execve(2)):
// neither points into the old image. `nohup` and every program that ignores // neither points into the old image. `nohup` and every program that ignores
// SIGPIPE before running a helper depend on it. // SIGPIPE before running a helper depend on it.
memset(myproc()->vma, 0, sizeof(myproc()->vma));
for (int i = 0; i < 32; i++) for (int i = 0; i < 32; i++)
if (myproc()->sig_handler[i] != 1) if (myproc()->sighand->handler[i] != 1)
myproc()->sig_handler[i] = 0; myproc()->sighand->handler[i] = 0;
memset(myproc()->sig_restorer, 0, sizeof(myproc()->sig_restorer)); memset(myproc()->sighand->restorer, 0, sizeof(myproc()->sighand->restorer));
myproc()->fs_base = 0; myproc()->fs_base = 0;
myproc()->pending_signals = 0; myproc()->pending_signals = 0;
myproc()->in_signal = 0; myproc()->in_signal = 0;
myproc()->sigsusp_active = 0; myproc()->sigsusp_active = 0;
myproc()->mmap_next = 0; myproc()->mm->mmap_next = 0;
// The interpreter is ordinary memory to everything after this: fork copies // The interpreter is ordinary memory to everything after this: fork copies
// it, exit frees it. mmap carries on above it. // it, exit frees it. mmap carries on above it.
if (interp[0]) { if (interp[0]) {
myproc()->vma[0].addr = INTERP_BASE; myproc()->mm->vma[0].addr = INTERP_BASE;
myproc()->vma[0].length = PGROUNDUP(interp_end); myproc()->mm->vma[0].length = PGROUNDUP(interp_end);
myproc()->vma[0].used = 1; myproc()->mm->vma[0].used = 1;
myproc()->vma[0].no_free = 0; myproc()->mm->vma[0].no_free = 0;
myproc()->vma[0].shmid = -1; myproc()->mm->vma[0].shmid = -1;
myproc()->mmap_next = INTERP_BASE + PGROUNDUP(interp_end); myproc()->mm->mmap_next = INTERP_BASE + PGROUNDUP(interp_end);
} }
myproc()->tf->rax = 0; myproc()->tf->rax = 0;
@ -637,7 +638,8 @@ exec_elf(char *path, struct argsrc *av, struct argsrc *ev)
// old one's registers. // old one's registers.
memmove(myproc()->fpu, fpu_default, sizeof(myproc()->fpu)); memmove(myproc()->fpu, fpu_default, sizeof(myproc()->fpu));
fxrstor(myproc()->fpu); fxrstor(myproc()->fpu);
freevm(oldpgdir); mm_put(oldmm);
proc_exec_done(); // a vfork parent may have its memory back
kfree((char*)scratch); kfree((char*)scratch);
kfree(bounce); kfree(bounce);
fsstat.exec_cycles += rdtsc() - _t0; fsstat.exec_cycles += rdtsc() - _t0;

View file

@ -70,7 +70,8 @@
#define PF_SCHEDSTAT 6 #define PF_SCHEDSTAT 6
#define PF_FD 7 // the directory /proc/<pid>/fd #define PF_FD 7 // the directory /proc/<pid>/fd
#define PF_TASK 8 // the directory /proc/<pid>/task #define PF_TASK 8 // the directory /proc/<pid>/task
#define PF_MAX 8 #define PF_COMM 9
#define PF_MAX 9
#define PF_FDENT 0x80 // | fd: one link in it #define PF_FDENT 0x80 // | fd: one link in it
#define INO_PID(ino) ((int)((ino) >> 8)) #define INO_PID(ino) ((int)((ino) >> 8))
@ -137,11 +138,13 @@ static const struct global_file pidfiles[] = {
// One symlink per open descriptor, named by its number and pointing at the // One symlink per open descriptor, named by its number and pointing at the
// path it was opened by, or at "socket:[ino]" and the like. // path it was opened by, or at "socket:[ino]" and the like.
{ "fd", PF_FD }, { "fd", PF_FD },
// The process's threads, one directory each, named by thread id. A process // The process's threads, one directory each, named by thread id; each is
// here is one thread, so task/ holds a single entry, the pid, and it *is* // /proc/<tid> -- the same inode. htop reads every process through it and
// /proc/<pid> -- the same inode. htop reads every process through it and
// shows nothing at all without it. // shows nothing at all without it.
{ "task", PF_TASK }, { "task", PF_TASK },
// The name alone, a line of it. btop reads this and nothing else for the
// Program column.
{ "comm", PF_COMM },
}; };
#define NPIDFILES ((int)(sizeof(pidfiles) / sizeof(pidfiles[0]))) #define NPIDFILES ((int)(sizeof(pidfiles) / sizeof(pidfiles[0])))
@ -236,7 +239,9 @@ gen_pid_stat(struct sb *s, const struct procinfo *pi)
sbputc(s, ' '); sbputi(s, (int64)pi->cutime); sbputc(s, ' '); sbputi(s, (int64)pi->cutime);
sbputc(s, ' '); sbputi(s, (int64)pi->cstime); sbputc(s, ' '); sbputi(s, (int64)pi->cstime);
sbputs(s, " 20 0 1 0 0 "); // priority nice num_threads itrealvalue starttime sbputs(s, " 20 0 "); // priority nice
sbputi(s, pi->nthreads); // 20 num_threads
sbputs(s, " 0 0 "); // itrealvalue starttime
sbputi(s, (int64)pi->sz); // 23 vsize, in bytes sbputi(s, (int64)pi->sz); // 23 vsize, in bytes
sbputc(s, ' '); sbputc(s, ' ');
sbputi(s, (int64)(pi->sz / PGSIZE)); // 24 rss, in pages sbputi(s, (int64)(pi->sz / PGSIZE)); // 24 rss, in pages
@ -248,7 +253,7 @@ gen_pid_status(struct sb *s, const struct procinfo *pi)
{ {
sbputs(s, "Name:\t"); sbputs(s, pi->name); sbputc(s, '\n'); sbputs(s, "Name:\t"); sbputs(s, pi->name); sbputc(s, '\n');
sbputs(s, "State:\t"); sbputc(s, pi->stopped ? 'T' : state_char(pi->state)); sbputs(s, "State:\t"); sbputc(s, pi->stopped ? 'T' : state_char(pi->state));
sbputs(s, "\nTgid:\t"); sbputi(s, pi->pid); sbputs(s, "\nTgid:\t"); sbputi(s, pi->tgid);
sbputs(s, "\nPid:\t"); sbputi(s, pi->pid); sbputs(s, "\nPid:\t"); sbputi(s, pi->pid);
sbputs(s, "\nPPid:\t"); sbputi(s, pi->ppid); sbputs(s, "\nPPid:\t"); sbputi(s, pi->ppid);
sbputs(s, "\nUid:\t"); sbputi(s, pi->uid); sbputs(s, "\nUid:\t"); sbputi(s, pi->uid);
@ -256,7 +261,7 @@ gen_pid_status(struct sb *s, const struct procinfo *pi)
sbputc(s, '\t'); sbputi(s, pi->uid); sbputc(s, '\t'); sbputi(s, pi->uid);
sbputc(s, '\t'); sbputi(s, pi->uid); sbputc(s, '\t'); sbputi(s, pi->uid);
sbputs(s, "\nVmSize:\t"); sbputi(s, (int64)(pi->sz / 1024)); sbputs(s, "\nVmSize:\t"); sbputi(s, (int64)(pi->sz / 1024));
sbputs(s, " kB\nThreads:\t1\n"); sbputs(s, " kB\nThreads:\t"); sbputi(s, pi->nthreads); sbputc(s, '\n');
// Where Linux keeps them, under the names Linux uses. The two are worth // Where Linux keeps them, under the names Linux uses. The two are worth
// telling apart: a process with a high voluntary count is waiting on // telling apart: a process with a high voluntary count is waiting on
@ -315,6 +320,7 @@ gen_file(uint64 ino, char *buf, int cap)
case PF_CMDLINE: gen_pid_cmdline(&s, &pi); break; case PF_CMDLINE: gen_pid_cmdline(&s, &pi); break;
case PF_STATM: gen_pid_statm(&s, &pi); break; case PF_STATM: gen_pid_statm(&s, &pi); break;
case PF_EXE: sbputs(&s, pi.exe[0] ? pi.exe : pi.name); break; case PF_EXE: sbputs(&s, pi.exe[0] ? pi.exe : pi.name); break;
case PF_COMM: sbputs(&s, pi.name); sbputc(&s, '\n'); break;
case PF_SCHEDSTAT: case PF_SCHEDSTAT:
sbputi(&s, (int64)pi.vruntime); sbputc(&s, ' '); sbputi(&s, (int64)pi.vruntime); sbputc(&s, ' ');
sbputi(&s, (int64)pi.nice); sbputc(&s, ' '); sbputi(&s, (int64)pi.nice); sbputc(&s, ' ');
@ -521,11 +527,13 @@ procfs_dirlookup(struct inode *dp, char *name, uint *poff)
INO_FILE(dp->inum) == PF_TASK ? (dp->inum & ~0xFFULL) INO_FILE(dp->inum) == PF_TASK ? (dp->inum & ~0xFFULL)
: PROCFS_ROOT); : PROCFS_ROOT);
// Inside /proc/<pid>/task/: the one thread, which is the process. // Inside /proc/<pid>/task/: a thread of this process, which is /proc/<tid>.
if (dp->inum != PROCFS_ROOT && INO_FILE(dp->inum) == PF_TASK) { if (dp->inum != PROCFS_ROOT && INO_FILE(dp->inum) == PF_TASK) {
if (name_to_pid(name) != INO_PID(dp->inum)) int tid = name_to_pid(name);
if (tid <= 0 || proc_snapshot_pid(tid, &pi) < 0 ||
pi.tgid != (int)INO_PID(dp->inum))
return 0; return 0;
return iget(dp->dev, (dp->inum & ~0xFFULL) | PF_DIR); return iget(dp->dev, ((uint64)tid << 8) | PF_DIR);
} }
// Inside /proc/<pid>/fd/: a number that is an open descriptor. // Inside /proc/<pid>/fd/: a number that is an open descriptor.
@ -647,7 +655,11 @@ procfs_getdents(struct inode *dp, char *out, int cap, uint64 *off)
EMIT(PROCFS_SELF, DT_LNK_, "self"); EMIT(PROCFS_SELF, DT_LNK_, "self");
for (i = 0; i < NGLOBALS; i++) for (i = 0; i < NGLOBALS; i++)
EMIT(PROCFS_GLOBAL | globals[i].id, DT_REG_, globals[i].name); EMIT(PROCFS_GLOBAL | globals[i].id, DT_REG_, globals[i].name);
// Processes only. A thread is reachable as /proc/<tid>, as on Linux,
// but listed under its process's task/ rather than here.
for (i = 0; proc_snapshot(i, &pi) == 0; i++) { for (i = 0; proc_snapshot(i, &pi) == 0; i++) {
if (pi.tgid != pi.pid)
continue;
utoa((uint)pi.pid, name); utoa((uint)pi.pid, name);
EMIT(((uint64)pi.pid << 8) | PF_DIR, DT_DIR_, name); EMIT(((uint64)pi.pid << 8) | PF_DIR, DT_DIR_, name);
} }
@ -664,8 +676,12 @@ procfs_getdents(struct inode *dp, char *out, int cap, uint64 *off)
} else if (INO_FILE(dp->inum) == PF_TASK) { } else if (INO_FILE(dp->inum) == PF_TASK) {
EMIT(dp->inum, DT_DIR_, "."); EMIT(dp->inum, DT_DIR_, ".");
EMIT(dp->inum & ~0xFFULL, DT_DIR_, ".."); EMIT(dp->inum & ~0xFFULL, DT_DIR_, "..");
utoa((uint)INO_PID(dp->inum), name); for (i = 0; proc_snapshot(i, &pi) == 0; i++) {
EMIT((dp->inum & ~0xFFULL) | PF_DIR, DT_DIR_, name); if (pi.tgid != (int)INO_PID(dp->inum))
continue;
utoa((uint)pi.pid, name);
EMIT(((uint64)pi.pid << 8) | PF_DIR, DT_DIR_, name);
}
} else { } else {
EMIT(dp->inum, DT_DIR_, "."); EMIT(dp->inum, DT_DIR_, ".");
EMIT(PROCFS_ROOT, DT_DIR_, ".."); EMIT(PROCFS_ROOT, DT_DIR_, "..");

View file

@ -169,7 +169,7 @@ net_interrupted(void)
return 1; return 1;
uint64 live = p->pending_signals & ~p->signal_mask; uint64 live = p->pending_signals & ~p->signal_mask;
for (int sig = 1; sig < 32; sig++) for (int sig = 1; sig < 32; sig++)
if ((live & (1ULL << sig)) && p->sig_handler[sig] > 1) // not DFL/IGN if ((live & (1ULL << sig)) && p->sighand->handler[sig] > 1) // not DFL/IGN
return 1; return 1;
return 0; return 0;
} }

File diff suppressed because it is too large Load diff

View file

@ -32,10 +32,12 @@ sys_enable_linux_abi(void)
addr_t addr_t
sys_set_tid_address(void) sys_set_tid_address(void)
{ {
// The word zeroed and woken when this thread exits -- what pthread_join
// waits on. It used to be *written*, with the tid, which is CHILD_SETTID's
// job and not this one's.
addr_t tidp; addr_t tidp;
argaddr(0, &tidp); argaddr(0, &tidp);
if (tidp) myproc()->clear_child_tid = tidp;
put_user(myproc()->pid, tidp);
return myproc()->pid; return myproc()->pid;
} }
@ -46,12 +48,15 @@ addr_t sys_set_robust_list(void) { return 0; }
addr_t addr_t
sys_futex(void) sys_futex(void)
{ {
int op; addr_t uaddr, utime, uaddr2;
int op, val, val3;
argaddr(0, &uaddr);
argint(1, &op); argint(1, &op);
op &= ~(FUTEX_WAIT_PRIVATE - FUTEX_WAIT); argint(2, &val);
if (op == FUTEX_WAKE || op == FUTEX_WAKE_PRIVATE) argaddr(3, &utime);
return 0; argaddr(4, &uaddr2);
return -EAGAIN; // no blocking, musl retries argint(5, &val3);
return futex_op(uaddr, op, val, utime, uaddr2, val3);
} }
// arch_prctl: save FS_BASE for TLS. // arch_prctl: save FS_BASE for TLS.

View file

@ -143,7 +143,7 @@ ev_open(struct evobj *o, int flags, int readable, int writable)
return -EMFILE; return -EMFILE;
} }
if (flags & O_CLOEXEC) if (flags & O_CLOEXEC)
myproc()->ofile_flags[fd] |= FD_CLOEXEC; myproc()->files->flags[fd] |= FD_CLOEXEC;
return fd; return fd;
} }
@ -163,7 +163,7 @@ ev_arg(int n, int kind, struct file **pf)
{ {
int fd; int fd;
struct file *f; struct file *f;
if (argint(n, &fd) < 0 || fd < 0 || fd >= NOFILE || !(f = myproc()->ofile[fd])) if (argint(n, &fd) < 0 || fd < 0 || fd >= NOFILE || !(f = myproc()->files->ofile[fd]))
return 0; return 0;
if (f->type != FD_EVENT || f->ev->kind != kind) if (f->type != FD_EVENT || f->ev->kind != kind)
return 0; return 0;
@ -497,7 +497,7 @@ signalfd_common(int fd, addr_t umask, uint64 size, int flags)
// An existing signalfd: change its mask, keep the descriptor. // An existing signalfd: change its mask, keep the descriptor.
if (fd != -1) { if (fd != -1) {
struct file *f; struct file *f;
if (fd < 0 || fd >= NOFILE || !(f = myproc()->ofile[fd]) || if (fd < 0 || fd >= NOFILE || !(f = myproc()->files->ofile[fd]) ||
f->type != FD_EVENT || f->ev->kind != EV_SIGNALFD) f->type != FD_EVENT || f->ev->kind != EV_SIGNALFD)
return -EINVAL; return -EINVAL;
acquiresleep(&f->ev->lock); acquiresleep(&f->ev->lock);
@ -606,9 +606,9 @@ sys_epoll_ctl(void)
return -EBADF; return -EBADF;
if (argint(1, &op) < 0 || argint(2, &fd) < 0 || argaddr(3, &uev) < 0) if (argint(1, &op) < 0 || argint(2, &fd) < 0 || argaddr(3, &uev) < 0)
return -EINVAL; return -EINVAL;
if (fd < 0 || fd >= NOFILE || !myproc()->ofile[fd]) if (fd < 0 || fd >= NOFILE || !myproc()->files->ofile[fd])
return -EBADF; return -EBADF;
struct file *tf = myproc()->ofile[fd]; struct file *tf = myproc()->files->ofile[fd];
// A set cannot watch itself. (Nor contain a set that contains it; that // A set cannot watch itself. (Nor contain a set that contains it; that
// check is the deep one Linux does, and nothing here builds such loops.) // check is the deep one Linux does, and nothing here builds such loops.)
if (tf == ef) if (tf == ef)
@ -662,7 +662,7 @@ ep_ready(struct evobj *o, struct epoll_event *out, int max)
acquiresleep(&o->lock); acquiresleep(&o->lock);
for (int i = 0; i < o->ep.n && got < max; ) { for (int i = 0; i < o->ep.n && got < max; ) {
struct epitem *it = &o->ep.items[i]; struct epitem *it = &o->ep.items[i];
struct file *now = (it->fd >= 0 && it->fd < NOFILE) ? myproc()->ofile[it->fd] : 0; struct file *now = (it->fd >= 0 && it->fd < NOFILE) ? myproc()->files->ofile[it->fd] : 0;
if (now != it->f) { if (now != it->f) {
ep_drop(o, i); // closed: it leaves the set ep_drop(o, i); // closed: it leaves the set
continue; continue;

View file

@ -143,7 +143,7 @@ argfd(int n, int *pfd, struct file **pf)
struct file *f; struct file *f;
if (argint(n, &fd) < 0) if (argint(n, &fd) < 0)
return -1; return -1;
if (fd < 0 || fd >= NOFILE || !(f = myproc()->ofile[fd])) if (fd < 0 || fd >= NOFILE || !(f = myproc()->files->ofile[fd]))
return -1; return -1;
if (pfd) *pfd = fd; if (pfd) *pfd = fd;
if (pf) *pf = f; if (pf) *pf = f;
@ -155,13 +155,18 @@ argfd(int n, int *pfd, struct file **pf)
int int
fdalloc(struct file *f) fdalloc(struct file *f)
{ {
// Locked: threads share the table, and two picking the same empty slot
// would each believe they owned it.
files_lock();
for (int fd = 0; fd < NOFILE; fd++) { for (int fd = 0; fd < NOFILE; fd++) {
if (!myproc()->ofile[fd]) { if (!myproc()->files->ofile[fd]) {
myproc()->ofile[fd] = f; myproc()->files->ofile[fd] = f;
myproc()->ofile_flags[fd] = 0; myproc()->files->flags[fd] = 0;
files_unlock();
return fd; return fd;
} }
} }
files_unlock();
return -1; return -1;
} }
@ -178,7 +183,7 @@ file_setpath(struct file *f, int dirfd, const char *path)
struct file *d; struct file *d;
if (dirfd == AT_FDCWD) if (dirfd == AT_FDCWD)
base = myproc()->cwd_path; base = myproc()->cwd_path;
else if (dirfd >= 0 && dirfd < NOFILE && (d = myproc()->ofile[dirfd]) && d->path) else if (dirfd >= 0 && dirfd < NOFILE && (d = myproc()->files->ofile[dirfd]) && d->path)
base = d->path; base = d->path;
else else
return; return;
@ -218,8 +223,8 @@ static struct inode *
at_dir(int dirfd) at_dir(int dirfd)
{ {
if (dirfd == AT_FDCWD) return myproc()->cwd; if (dirfd == AT_FDCWD) return myproc()->cwd;
if (dirfd < 0 || dirfd >= NOFILE || !myproc()->ofile[dirfd]) return 0; if (dirfd < 0 || dirfd >= NOFILE || !myproc()->files->ofile[dirfd]) return 0;
struct file *f = myproc()->ofile[dirfd]; struct file *f = myproc()->files->ofile[dirfd];
if (f->type != FD_INODE) return 0; if (f->type != FD_INODE) return 0;
return f->ip; return f->ip;
} }
@ -393,12 +398,12 @@ addr_t sys_dup2(void)
int oldfd, newfd; int oldfd, newfd;
struct file *f; struct file *f;
if (argint(0, &oldfd) < 0 || argint(1, &newfd) < 0) return -1; if (argint(0, &oldfd) < 0 || argint(1, &newfd) < 0) return -1;
if (oldfd < 0 || oldfd >= NOFILE || !(f = myproc()->ofile[oldfd])) return -EBADF; if (oldfd < 0 || oldfd >= NOFILE || !(f = myproc()->files->ofile[oldfd])) return -EBADF;
if (newfd < 0 || newfd >= NOFILE) return -EBADF; if (newfd < 0 || newfd >= NOFILE) return -EBADF;
if (oldfd == newfd) return newfd; if (oldfd == newfd) return newfd;
if (myproc()->ofile[newfd]) fileclose(myproc()->ofile[newfd]); if (myproc()->files->ofile[newfd]) fileclose(myproc()->files->ofile[newfd]);
myproc()->ofile[newfd] = filedup(f); myproc()->files->ofile[newfd] = filedup(f);
myproc()->ofile_flags[newfd] = 0; // dup2 never carries FD_CLOEXEC over myproc()->files->flags[newfd] = 0; // dup2 never carries FD_CLOEXEC over
return newfd; return newfd;
} }
@ -412,9 +417,9 @@ addr_t sys_dup3(void)
// Unlike dup2, equal fds are an error (dup3(2)). Without this check we // Unlike dup2, equal fds are an error (dup3(2)). Without this check we
// would fileclose(f) and then filedup() the file we just released. // would fileclose(f) and then filedup() the file we just released.
if (oldfd == newfd) return -EINVAL; if (oldfd == newfd) return -EINVAL;
if (myproc()->ofile[newfd]) fileclose(myproc()->ofile[newfd]); if (myproc()->files->ofile[newfd]) fileclose(myproc()->files->ofile[newfd]);
myproc()->ofile[newfd] = filedup(f); myproc()->files->ofile[newfd] = filedup(f);
myproc()->ofile_flags[newfd] = 0; myproc()->files->flags[newfd] = 0;
return newfd; return newfd;
} }
@ -423,8 +428,8 @@ addr_t sys_close(void)
int fd; int fd;
struct file *f; struct file *f;
if (argfd(0, &fd, &f) < 0) return -EBADF; if (argfd(0, &fd, &f) < 0) return -EBADF;
myproc()->ofile[fd] = 0; myproc()->files->ofile[fd] = 0;
myproc()->ofile_flags[fd] = 0; myproc()->files->flags[fd] = 0;
lock_close_fd(f); lock_close_fd(f);
fileclose(f); fileclose(f);
return 0; return 0;
@ -444,23 +449,23 @@ sys_fcntl(void)
{ {
int fd, cmd, arg; int fd, cmd, arg;
if (argint(0, &fd) < 0 || argint(1, &cmd) < 0 || argint(2, &arg) < 0) return -1; if (argint(0, &fd) < 0 || argint(1, &cmd) < 0 || argint(2, &arg) < 0) return -1;
if (fd < 0 || fd >= NOFILE || !myproc()->ofile[fd]) return -1; if (fd < 0 || fd >= NOFILE || !myproc()->files->ofile[fd]) return -1;
struct file *f = myproc()->ofile[fd]; struct file *f = myproc()->files->ofile[fd];
switch (cmd) { switch (cmd) {
case F_DUPFD: case F_DUPFD:
case F_DUPFD_CLOEXEC: { case F_DUPFD_CLOEXEC: {
if (arg < 0) return -EINVAL; if (arg < 0) return -EINVAL;
for (int nfd = arg; nfd < NOFILE; nfd++) { for (int nfd = arg; nfd < NOFILE; nfd++) {
if (!myproc()->ofile[nfd]) { if (!myproc()->files->ofile[nfd]) {
myproc()->ofile[nfd] = filedup(f); myproc()->files->ofile[nfd] = filedup(f);
myproc()->ofile_flags[nfd] = (cmd == F_DUPFD_CLOEXEC) ? FD_CLOEXEC : 0; myproc()->files->flags[nfd] = (cmd == F_DUPFD_CLOEXEC) ? FD_CLOEXEC : 0;
return nfd; return nfd;
} }
} }
return -EMFILE; return -EMFILE;
} }
case F_GETFD: return myproc()->ofile_flags[fd]; case F_GETFD: return myproc()->files->flags[fd];
case F_SETFD: myproc()->ofile_flags[fd] = arg; return 0; case F_SETFD: myproc()->files->flags[fd] = arg; return 0;
case F_GETFL: { case F_GETFL: {
int flags = 0; int flags = 0;
if (f->readable && f->writable) flags = O_RDWR; if (f->readable && f->writable) flags = O_RDWR;
@ -592,7 +597,7 @@ addr_t sys_pipe(void)
int fd0 = -1, fd1; int fd0 = -1, fd1;
if (argptr(0, (void*)&fd, 2*sizeof(fd[0])) < 0 || pipealloc(&rf, &wf) < 0) return -1; if (argptr(0, (void*)&fd, 2*sizeof(fd[0])) < 0 || pipealloc(&rf, &wf) < 0) return -1;
if ((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0) { if ((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0) {
if (fd0 >= 0) myproc()->ofile[fd0] = 0; if (fd0 >= 0) myproc()->files->ofile[fd0] = 0;
fileclose(rf); fileclose(wf); fileclose(rf); fileclose(wf);
return -1; return -1;
} }
@ -614,14 +619,14 @@ addr_t sys_pipe2(void)
if (!fds || pipealloc(&rf, &wf) < 0) return -1; if (!fds || pipealloc(&rf, &wf) < 0) return -1;
fd0 = fd1 = -1; fd0 = fd1 = -1;
if ((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0) { if ((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0) {
if (fd0 >= 0) myproc()->ofile[fd0] = 0; if (fd0 >= 0) myproc()->files->ofile[fd0] = 0;
fileclose(rf); fileclose(wf); fileclose(rf); fileclose(wf);
return -1; return -1;
} }
rf->nonblock = wf->nonblock = (flags & O_NONBLOCK) != 0; rf->nonblock = wf->nonblock = (flags & O_NONBLOCK) != 0;
if (flags & O_CLOEXEC) { if (flags & O_CLOEXEC) {
myproc()->ofile_flags[fd0] |= FD_CLOEXEC; myproc()->files->flags[fd0] |= FD_CLOEXEC;
myproc()->ofile_flags[fd1] |= FD_CLOEXEC; myproc()->files->flags[fd1] |= FD_CLOEXEC;
} }
fds[0] = fd0; fds[1] = fd1; fds[0] = fd0; fds[1] = fd1;
return 0; return 0;
@ -1518,7 +1523,7 @@ addr_t sys_ioctl(void)
if (f->type != FD_INODE || !f->ip || !f->writable) if (f->type != FD_INODE || !f->ip || !f->writable)
return -EBADF; return -EBADF;
if (sfd < 0 || sfd >= NOFILE || (sf = pr->ofile[sfd]) == 0) if (sfd < 0 || sfd >= NOFILE || (sf = pr->files->ofile[sfd]) == 0)
return -EBADF; return -EBADF;
if (sf->type != FD_INODE || !sf->ip || !sf->readable) if (sf->type != FD_INODE || !sf->ip || !sf->readable)
return -EBADF; return -EBADF;
@ -2344,8 +2349,8 @@ addr_t sys_fstatat(void)
// AT_EMPTY_PATH: if path is empty, fstat the dirfd itself. // AT_EMPTY_PATH: if path is empty, fstat the dirfd itself.
if ((flags & AT_EMPTY_PATH) && path[0] == '\0') { if ((flags & AT_EMPTY_PATH) && path[0] == '\0') {
if (dirfd < 0 || dirfd >= NOFILE || !myproc()->ofile[dirfd]) return -1; if (dirfd < 0 || dirfd >= NOFILE || !myproc()->files->ofile[dirfd]) return -1;
struct file *f = myproc()->ofile[dirfd]; struct file *f = myproc()->files->ofile[dirfd];
if (f->type != FD_INODE) return -1; if (f->type != FD_INODE) return -1;
ilock(f->ip); ilock(f->ip);
if (myproc()->syscall_map) stati_linux(f->ip, (struct linux_stat*)st); if (myproc()->syscall_map) stati_linux(f->ip, (struct linux_stat*)st);

View file

@ -45,8 +45,8 @@ static struct vma *
vma_free_slot(struct proc *p) vma_free_slot(struct proc *p)
{ {
for (int i = 0; i < NVMA; i++) for (int i = 0; i < NVMA; i++)
if (!p->vma[i].used) if (!p->mm->vma[i].used)
return &p->vma[i]; return &p->mm->vma[i];
return 0; return 0;
} }
@ -62,7 +62,7 @@ static int
vma_note_anon(struct proc *p, uint64 va, uint64 len) vma_note_anon(struct proc *p, uint64 va, uint64 len)
{ {
for (int i = 0; i < NVMA; i++) { for (int i = 0; i < NVMA; i++) {
struct vma *v = &p->vma[i]; struct vma *v = &p->mm->vma[i];
if (vma_anon(v) && v->addr + v->length == va) { if (vma_anon(v) && v->addr + v->length == va) {
v->length += len; v->length += len;
return 0; return 0;
@ -81,7 +81,7 @@ static int
range_free(struct proc *p, uint64 va, uint64 len) range_free(struct proc *p, uint64 va, uint64 len)
{ {
for (int i = 0; i < NVMA; i++) { for (int i = 0; i < NVMA; i++) {
struct vma *v = &p->vma[i]; struct vma *v = &p->mm->vma[i];
if (v->used && va < v->addr + v->length && v->addr < va + len) if (v->used && va < v->addr + v->length && v->addr < va + len)
return 0; return 0;
} }
@ -104,21 +104,21 @@ range_free(struct proc *p, uint64 va, uint64 len)
static uint64 static uint64
mmap_va(struct proc *p, uint64 len, int mergeable) mmap_va(struct proc *p, uint64 len, int mergeable)
{ {
if (p->mmap_next == 0) if (p->mm->mmap_next == 0)
p->mmap_next = 0x40000000; p->mm->mmap_next = 0x40000000;
if (mergeable) { if (mergeable) {
for (int i = 0; i < NVMA; i++) { for (int i = 0; i < NVMA; i++) {
struct vma *v = &p->vma[i]; struct vma *v = &p->mm->vma[i];
uint64 e = v->addr + v->length; uint64 e = v->addr + v->length;
if (vma_anon(v) && e >= 0x40000000 && e + len <= p->mmap_next && if (vma_anon(v) && e >= 0x40000000 && e + len <= p->mm->mmap_next &&
range_free(p, e, len)) range_free(p, e, len))
return e; return e;
} }
} }
uint64 va = p->mmap_next; uint64 va = p->mm->mmap_next;
if (bad_user_range(va, len)) if (bad_user_range(va, len))
return 0; return 0;
p->mmap_next += len; p->mm->mmap_next += len;
return va; return va;
} }
@ -138,7 +138,7 @@ vma_cover_anon(struct proc *p, uint64 va, uint64 len)
uint64 next = end; uint64 next = end;
int inside = -1; int inside = -1;
for (int i = 0; i < NVMA; i++) { for (int i = 0; i < NVMA; i++) {
struct vma *v = &p->vma[i]; struct vma *v = &p->mm->vma[i];
if (!v->used) if (!v->used)
continue; continue;
if (v->addr <= cur && cur < v->addr + v->length) if (v->addr <= cur && cur < v->addr + v->length)
@ -147,7 +147,7 @@ vma_cover_anon(struct proc *p, uint64 va, uint64 len)
next = v->addr; next = v->addr;
} }
if (inside >= 0) { if (inside >= 0) {
cur = p->vma[inside].addr + p->vma[inside].length; cur = p->mm->vma[inside].addr + p->mm->vma[inside].length;
continue; continue;
} }
if (vma_note_anon(p, cur, next - cur) < 0) if (vma_note_anon(p, cur, next - cur) < 0)
@ -180,8 +180,8 @@ sys_brk(void)
return myproc()->sz; return myproc()->sz;
} }
addr_t static addr_t
sys_mmap(void) mmap_locked(void)
{ {
uint64 addr, length, offset; uint64 addr, length, offset;
int prot, flags, fd; int prot, flags, fd;
@ -193,7 +193,7 @@ sys_mmap(void)
return -1; return -1;
if (fd >= 0 && fd < NOFILE) if (fd >= 0 && fd < NOFILE)
f = myproc()->ofile[fd]; f = myproc()->files->ofile[fd];
length = PGROUNDUP(length); length = PGROUNDUP(length);
uint64 va = addr; uint64 va = addr;
@ -425,8 +425,8 @@ sys_mmap(void)
return va; return va;
} }
addr_t static addr_t
sys_munmap(void) munmap_locked(void)
{ {
uint64 addr, length; uint64 addr, length;
struct proc *p = myproc(); struct proc *p = myproc();
@ -441,7 +441,7 @@ sys_munmap(void)
// Part of one is refused: unmapping it the anonymous way below would kfree // Part of one is refused: unmapping it the anonymous way below would kfree
// pages that are the framebuffer's or another process's. // pages that are the framebuffer's or another process's.
for (int i = 0; i < NVMA; i++) { for (int i = 0; i < NVMA; i++) {
struct vma *v = &p->vma[i]; struct vma *v = &p->mm->vma[i];
if (!v->used || vma_anon(v)) if (!v->used || vma_anon(v))
continue; continue;
if (v->addr == addr) { if (v->addr == addr) {
@ -466,7 +466,7 @@ sys_munmap(void)
// are not there, and the bump allocator never hands the addresses out // are not there, and the bump allocator never hands the addresses out
// again. // again.
for (int i = 0; i < NVMA; i++) { for (int i = 0; i < NVMA; i++) {
struct vma *v = &p->vma[i]; struct vma *v = &p->mm->vma[i];
if (!vma_anon(v)) if (!vma_anon(v))
continue; continue;
uint64 vs = v->addr, ve = v->addr + v->length; uint64 vs = v->addr, ve = v->addr + v->length;
@ -835,8 +835,8 @@ sys_shmget(void)
return -ENOSPC; return -ENOSPC;
} }
addr_t static addr_t
sys_shmat(void) shmat_locked(void)
{ {
int id, flags; int id, flags;
addr_t addr; addr_t addr;
@ -859,15 +859,15 @@ sys_shmat(void)
struct vma *v = 0; struct vma *v = 0;
for (int i = 0; i < NVMA; i++) for (int i = 0; i < NVMA; i++)
if (!myproc()->vma[i].used) { v = &myproc()->vma[i]; break; } if (!myproc()->mm->vma[i].used) { v = &myproc()->mm->vma[i]; break; }
if (!v) { shm_detach(id); return -ENOMEM; } if (!v) { shm_detach(id); return -ENOMEM; }
// Ignore the hint and take a fresh range, the same way mmap does. // Ignore the hint and take a fresh range, the same way mmap does.
if (myproc()->mmap_next == 0) if (myproc()->mm->mmap_next == 0)
myproc()->mmap_next = 0x40000000; myproc()->mm->mmap_next = 0x40000000;
uint64 va = myproc()->mmap_next; uint64 va = myproc()->mm->mmap_next;
if (bad_user_range(va, len)) { shm_detach(id); return -ENOMEM; } if (bad_user_range(va, len)) { shm_detach(id); return -ENOMEM; }
myproc()->mmap_next += len; myproc()->mm->mmap_next += len;
for (int pg = 0; pg < npages; pg++) { for (int pg = 0; pg < npages; pg++) {
if (mappages(myproc()->pgdir, (void*)(va + (uint64)pg * PGSIZE), PGSIZE, if (mappages(myproc()->pgdir, (void*)(va + (uint64)pg * PGSIZE), PGSIZE,
@ -895,35 +895,36 @@ sys_shmat(void)
// //
// Called from exit() and from exec() before the old address space goes away. // Called from exit() and from exec() before the old address space goes away.
void void
proc_release_shared(struct proc *p) mm_release_shared(struct mm *m)
{ {
for (int i = 0; i < NVMA; i++) { for (int i = 0; i < NVMA; i++) {
if (!p->vma[i].used || !p->vma[i].no_free) if (!m->vma[i].used || !m->vma[i].no_free)
continue; continue;
vm_unmap_norelease(p->pgdir, p->vma[i].addr, p->vma[i].length); if (m->pgdir)
if (p->vma[i].shmid >= 0) vm_unmap_norelease(m->pgdir, m->vma[i].addr, m->vma[i].length);
shm_detach(p->vma[i].shmid); if (m->vma[i].shmid >= 0)
p->vma[i].used = 0; shm_detach(m->vma[i].shmid);
p->vma[i].no_free = 0; m->vma[i].used = 0;
p->vma[i].shmid = -1; m->vma[i].no_free = 0;
m->vma[i].shmid = -1;
} }
} }
addr_t static addr_t
sys_shmdt(void) shmdt_locked(void)
{ {
addr_t addr; addr_t addr;
if (argaddr(0, &addr) < 0) if (argaddr(0, &addr) < 0)
return -EINVAL; return -EINVAL;
for (int i = 0; i < NVMA; i++) { for (int i = 0; i < NVMA; i++) {
if (myproc()->vma[i].used && myproc()->vma[i].addr == addr && if (myproc()->mm->vma[i].used && myproc()->mm->vma[i].addr == addr &&
myproc()->vma[i].shmid >= 0) { myproc()->mm->vma[i].shmid >= 0) {
vm_unmap_norelease(myproc()->pgdir, addr, myproc()->vma[i].length); vm_unmap_norelease(myproc()->pgdir, addr, myproc()->mm->vma[i].length);
shm_detach(myproc()->vma[i].shmid); shm_detach(myproc()->mm->vma[i].shmid);
myproc()->vma[i].used = myproc()->vma[i].addr = myproc()->vma[i].length = 0; myproc()->mm->vma[i].used = myproc()->mm->vma[i].addr = myproc()->mm->vma[i].length = 0;
myproc()->vma[i].no_free = 0; myproc()->mm->vma[i].no_free = 0;
myproc()->vma[i].shmid = -1; myproc()->mm->vma[i].shmid = -1;
return 0; return 0;
} }
} }
@ -981,3 +982,21 @@ sys_setpriority(void)
return -EINVAL; return -EINVAL;
return (addr_t)proc_setnice(who, nice); return (addr_t)proc_setnice(who, nice);
} }
// One mapping change at a time per address space: threads share the VMA table
// and the page tables, and two of them allocating the same missing page-table
// level at once would each install their own and lose the other's mappings.
#define MM_LOCKED(name) \
addr_t \
sys_##name(void) \
{ \
struct mm *m = myproc()->mm; \
mm_lock(m); \
addr_t r = name##_locked(); \
mm_unlock(m); \
return r; \
}
MM_LOCKED(mmap)
MM_LOCKED(munmap)
MM_LOCKED(shmat)
MM_LOCKED(shmdt)

View file

@ -123,7 +123,7 @@ sock_to_fd(struct socket *s, int sflags)
f->off = 0; f->off = 0;
f->nonblock = (sflags & SOCK_NONBLOCK) != 0; f->nonblock = (sflags & SOCK_NONBLOCK) != 0;
if (sflags & SOCK_CLOEXEC) if (sflags & SOCK_CLOEXEC)
myproc()->ofile_flags[fd] |= FD_CLOEXEC; myproc()->files->flags[fd] |= FD_CLOEXEC;
return fd; return fd;
} }
@ -136,7 +136,7 @@ argsock(int n, struct socket **ps)
if (argint(n, &fd) < 0) if (argint(n, &fd) < 0)
return -EBADF; return -EBADF;
if (fd < 0 || fd >= NOFILE || !(f = myproc()->ofile[fd])) if (fd < 0 || fd >= NOFILE || !(f = myproc()->files->ofile[fd]))
return -EBADF; return -EBADF;
if (f->type != FD_SOCKET) if (f->type != FD_SOCKET)
return -ENOTSOCK; return -ENOTSOCK;
@ -471,7 +471,7 @@ SYSCALL_DEFINE(socketpair)
return fd0; return fd0;
} }
if ((fd1 = sock_to_fd(b, sflags)) < 0) { if ((fd1 = sock_to_fd(b, sflags)) < 0) {
myproc()->ofile[fd0] = 0; myproc()->files->ofile[fd0] = 0;
sockclose(a); sockclose(a);
sockclose(b); sockclose(b);
return fd1; return fd1;
@ -479,8 +479,8 @@ SYSCALL_DEFINE(socketpair)
int sv[2] = { fd0, fd1 }; int sv[2] = { fd0, fd1 };
if (copy_to_user(usv, sv, sizeof(sv)) < 0) { if (copy_to_user(usv, sv, sizeof(sv)) < 0) {
myproc()->ofile[fd0] = 0; myproc()->files->ofile[fd0] = 0;
myproc()->ofile[fd1] = 0; myproc()->files->ofile[fd1] = 0;
sockclose(a); sockclose(a);
sockclose(b); sockclose(b);
return -EFAULT; return -EFAULT;
@ -808,7 +808,7 @@ argsockfile(int n, struct socket **ps, struct file **pf)
if (argint(n, &fd) < 0) if (argint(n, &fd) < 0)
return -EBADF; return -EBADF;
if (fd < 0 || fd >= NOFILE || !(f = myproc()->ofile[fd])) if (fd < 0 || fd >= NOFILE || !(f = myproc()->files->ofile[fd]))
return -EBADF; return -EBADF;
if (f->type != FD_SOCKET) if (f->type != FD_SOCKET)
return -ENOTSOCK; return -ENOTSOCK;
@ -879,7 +879,7 @@ scm_gather(struct msghdr *m, struct file **fs, int *nfp)
r = -EINVAL; r = -EINVAL;
goto fail; goto fail;
} }
if (ufd < 0 || ufd >= NOFILE || !(f = myproc()->ofile[ufd])) { if (ufd < 0 || ufd >= NOFILE || !(f = myproc()->files->ofile[ufd])) {
r = -EBADF; r = -EBADF;
goto fail; goto fail;
} }
@ -1078,7 +1078,7 @@ SYSCALL_DEFINE(recvmsg)
if (d < 0) if (d < 0)
break; break;
if (flags & MSG_CMSG_CLOEXEC) if (flags & MSG_CMSG_CLOEXEC)
myproc()->ofile_flags[d] |= FD_CLOEXEC; myproc()->files->flags[d] |= FD_CLOEXEC;
ufd[installed++] = d; ufd[installed++] = d;
} }

View file

@ -79,12 +79,12 @@ do_poll(struct pollfd *fds, int nfds, int timeout_ticks)
fds[i].revents = 0; fds[i].revents = 0;
if (fds[i].fd < 0) if (fds[i].fd < 0)
continue; // negative fd: ignore, per poll(2) continue; // negative fd: ignore, per poll(2)
if (fds[i].fd >= NOFILE || !myproc()->ofile[fds[i].fd]) { if (fds[i].fd >= NOFILE || !myproc()->files->ofile[fds[i].fd]) {
fds[i].revents = POLLNVAL; fds[i].revents = POLLNVAL;
nready++; nready++;
continue; continue;
} }
int rev = file_poll(myproc()->ofile[fds[i].fd], fds[i].events); int rev = file_poll(myproc()->files->ofile[fds[i].fd], fds[i].events);
if (rev) { if (rev) {
fds[i].revents = (short)rev; fds[i].revents = (short)rev;
nready++; nready++;
@ -217,7 +217,7 @@ sys_select(void)
int want_w = fdset_isset(wr, fd); int want_w = fdset_isset(wr, fd);
if (!want_r && !want_w) if (!want_r && !want_w)
continue; continue;
struct file *f = myproc()->ofile[fd]; struct file *f = myproc()->files->ofile[fd];
if (!f) if (!f)
return -EBADF; return -EBADF;
int rev = file_poll(f, (want_r ? POLLIN : 0) | (want_w ? POLLOUT : 0)); int rev = file_poll(f, (want_r ? POLLIN : 0) | (want_w ? POLLOUT : 0));
@ -292,7 +292,7 @@ sys_pselect6(void)
int want_w = fdset_isset(wr, fd); int want_w = fdset_isset(wr, fd);
if (!want_r && !want_w) if (!want_r && !want_w)
continue; continue;
struct file *f = myproc()->ofile[fd]; struct file *f = myproc()->files->ofile[fd];
if (!f) if (!f)
return -EBADF; return -EBADF;
int rev = file_poll(f, (want_r ? POLLIN : 0) | (want_w ? POLLOUT : 0)); int rev = file_poll(f, (want_r ? POLLIN : 0) | (want_w ? POLLOUT : 0));

View file

@ -24,13 +24,27 @@ addr_t sys_wait(void) { return wait(); }
// wait() reports back through WEXITSTATUS. // wait() reports back through WEXITSTATUS.
addr_t addr_t
sys_exit(void) sys_exit(void)
{
int code = 0;
argint(0, &code);
// Linux's exit is one thread's: pthread_exit, and a thread returning
// from its start function. The program ends with exit_group -- or here,
// when this is the last thread, or the native ABI, which has no threads.
struct proc *p = myproc();
if (p->syscall_map && p->tgid != p->pid && proc_thread_count(p->tgid) > 1)
thread_exit();
exit(W_EXITCODE(code, 0));
return 0;
}
addr_t
sys_exit_group(void)
{ {
int code = 0; int code = 0;
argint(0, &code); argint(0, &code);
exit(W_EXITCODE(code, 0)); exit(W_EXITCODE(code, 0));
return 0; return 0;
} }
addr_t sys_exit_group(void) { return sys_exit(); }
addr_t addr_t
@ -62,9 +76,10 @@ sys_kill(void)
// Defined with getrusage() further down; wait4() needs it too. // Defined with getrusage() further down; wait4() needs it too.
static void rusage_fill(struct rusage *ru, uint64 ut, uint64 st); static void rusage_fill(struct rusage *ru, uint64 ut, uint64 st);
addr_t sys_getpid(void) { return myproc()->pid; } // A process is its thread group: getpid is the group's id, gettid the thread's.
addr_t sys_getppid(void) { return myproc()->parent ? myproc()->parent->pid : 1; } addr_t sys_getpid(void) { return myproc()->tgid; }
addr_t sys_gettid(void) { return myproc()->pid; } // single-threaded: tid == pid addr_t sys_getppid(void) { return myproc()->parent ? myproc()->parent->tgid : 1; }
addr_t sys_gettid(void) { return myproc()->pid; }
addr_t addr_t
sys_waitpid(void) sys_waitpid(void)
@ -122,11 +137,13 @@ sys_wait4(void)
addr_t addr_t
sys_clone(void) sys_clone(void)
{ {
int flags; uint64 flags, stack, ptid, ctid, tls;
argint(0, &flags); argaddr(0, &flags);
if (flags & (CLONE_VM | CLONE_THREAD | CLONE_SIGHAND)) argaddr(1, &stack);
return -ENOSYS; argaddr(2, &ptid);
return fork(); argaddr(3, &ctid);
argaddr(4, &tls);
return clone_proc(flags, stack, ptid, ctid, tls);
} }
addr_t addr_t
@ -424,10 +441,11 @@ addr_t sys_tgkill(void)
{ {
int tgid, tid, sig; int tgid, tid, sig;
argint(0, &tgid); argint(1, &tid); argint(2, &sig); argint(0, &tgid); argint(1, &tid); argint(2, &sig);
// In our single-threaded model tgid == tid == pid. // To one thread: musl's pthread_kill, and raise(), which is tgkill on
if (tgid <= 0) return 0; // the calling thread.
if (sig == 0) return proc_exists(tgid) ? 0 : -ESRCH; if (tid <= 0) return -EINVAL;
return sigsend(tgid, sig) < 0 ? -ESRCH : 0; if (sig == 0) return proc_exists(tid) ? 0 : -ESRCH;
return sigsend(tid, sig) < 0 ? -ESRCH : 0;
} }
addr_t sys_tkill(void) addr_t sys_tkill(void)

View file

@ -115,12 +115,12 @@ sys_rt_sigaction(void)
struct proc *p = myproc(); struct proc *p = myproc();
if (oldactp) { if (oldactp) {
put_user(p->sig_handler[sig], oldactp + SA_HANDLER_OFF); put_user(p->sighand->handler[sig], oldactp + SA_HANDLER_OFF);
put_user(p->sig_restorer[sig], oldactp + SA_RESTORER_OFF(p)); put_user(p->sighand->restorer[sig], oldactp + SA_RESTORER_OFF(p));
if (p->syscall_map) { if (p->syscall_map) {
put_user(p->sig_flags[sig], oldactp + SA_FLAGS_OFF(p)); put_user(p->sighand->flags[sig], oldactp + SA_FLAGS_OFF(p));
} else { } else {
uint32 fl = (uint32)p->sig_flags[sig]; uint32 fl = (uint32)p->sighand->flags[sig];
put_user(fl, oldactp + SA_FLAGS_OFF(p)); put_user(fl, oldactp + SA_FLAGS_OFF(p));
} }
} }
@ -134,9 +134,9 @@ sys_rt_sigaction(void)
// SIGKILL and SIGSTOP cannot be caught or ignored. // SIGKILL and SIGSTOP cannot be caught or ignored.
if (sig == SIGKILL || sig == SIGSTOP) if (sig == SIGKILL || sig == SIGSTOP)
return -EINVAL; return -EINVAL;
p->sig_handler[sig] = h; p->sighand->handler[sig] = h;
p->sig_restorer[sig] = r; p->sighand->restorer[sig] = r;
p->sig_flags[sig] = fl; p->sighand->flags[sig] = fl;
} }
return 0; return 0;
} }
@ -218,16 +218,16 @@ deliver_signals(struct trapframe *tf)
// SIGSTOP cannot be caught or ignored; the other three stop only by // SIGSTOP cannot be caught or ignored; the other three stop only by
// default. Stopped here and resumed here, by SIGCONT or SIGKILL. // default. Stopped here and resumed here, by SIGCONT or SIGKILL.
if (sig == SIGSTOP || if (sig == SIGSTOP ||
(sig_default_stops(sig) && myproc()->sig_handler[sig] == 0)) { (sig_default_stops(sig) && myproc()->sighand->handler[sig] == 0)) {
myproc()->pending_signals &= ~(1ULL << sig); myproc()->pending_signals &= ~(1ULL << sig);
proc_stop(sig); proc_stop(sig);
if (myproc()->killed) if (myproc()->killed)
exit(W_EXITCODE(0, myproc()->killsig ? myproc()->killsig : SIGKILL)); exit(W_EXITCODE(0, myproc()->killsig ? myproc()->killsig : SIGKILL));
continue; continue;
} }
if (myproc()->sig_handler[sig] <= 1) { if (myproc()->sighand->handler[sig] <= 1) {
myproc()->pending_signals &= ~(1ULL << sig); myproc()->pending_signals &= ~(1ULL << sig);
if (myproc()->sig_handler[sig] == 0 && sig_default_kills(sig)) { if (myproc()->sighand->handler[sig] == 0 && sig_default_kills(sig)) {
myproc()->killed = 1; myproc()->killed = 1;
if (myproc()->killsig == 0) if (myproc()->killsig == 0)
myproc()->killsig = sig; myproc()->killsig = sig;
@ -283,8 +283,8 @@ deliver_signals(struct trapframe *tf)
// shifted rsp, and its ret fell into the sigreturn trampoline, // shifted rsp, and its ret fell into the sigreturn trampoline,
// silently undoing everything. exec() handles this same quirk by // silently undoing everything. exec() handles this same quirk by
// setting tf->rcx = elf.entry. // setting tf->rcx = elf.entry.
tf->rip = myproc()->sig_handler[sig]; tf->rip = myproc()->sighand->handler[sig];
tf->rcx = myproc()->sig_handler[sig]; tf->rcx = myproc()->sighand->handler[sig];
return; return;
} }
} }
@ -344,7 +344,7 @@ addr_t sys_rt_sigsuspend(void)
uint64 deliverable = myproc()->pending_signals & ~myproc()->signal_mask; uint64 deliverable = myproc()->pending_signals & ~myproc()->signal_mask;
int will_deliver = 0; int will_deliver = 0;
for (int sig = 1; sig < 32; sig++) for (int sig = 1; sig < 32; sig++)
if ((deliverable & (1ULL << sig)) && myproc()->sig_handler[sig]) if ((deliverable & (1ULL << sig)) && myproc()->sighand->handler[sig])
{ will_deliver = 1; break; } { will_deliver = 1; break; }
if (will_deliver && !myproc()->in_signal) { if (will_deliver && !myproc()->in_signal) {