Threads
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:
parent
2372806862
commit
f4d7f9100d
19 changed files with 1300 additions and 248 deletions
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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_, "..");
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue