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
|
|
@ -636,6 +636,11 @@ oom:
|
|||
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
|
||||
switchuvm(struct proc *p)
|
||||
{
|
||||
|
|
@ -650,6 +655,8 @@ switchuvm(struct proc *p)
|
|||
// every process that is not hosting a driver.
|
||||
ioperm_switch(p);
|
||||
|
||||
cr3_loads[cpunum()]++;
|
||||
cpu_pgdir[cpunum()] = p->pgdir;
|
||||
lcr3(v2p(p->pgdir));
|
||||
popcli();
|
||||
}
|
||||
|
|
@ -695,9 +702,109 @@ walkpgdir(pml4e_t *pml4, const void *va, int alloc)
|
|||
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
|
||||
switchkvm(void)
|
||||
{
|
||||
cr3_loads[cpunum()]++;
|
||||
cpu_pgdir[cpunum()] = 0;
|
||||
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);
|
||||
if (pte && (*pte & PTE_P)) {
|
||||
uint64 pa = PTE_ADDR(*pte);
|
||||
kfree((char*)P2V(pa));
|
||||
*pte = 0;
|
||||
invlpg((void*)a);
|
||||
user_page_free(pgdir, pa);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -883,11 +990,10 @@ deallocuvm(pml4e_t *pgdir, uint64 oldsz, uint64 newsz)
|
|||
pa = PTE_ADDR(*pte);
|
||||
if(pa == 0)
|
||||
panic("kfree");
|
||||
if(pa < PHYSTOP) {
|
||||
char *v = P2V(pa);
|
||||
kfree(v);
|
||||
}
|
||||
*pte = 0;
|
||||
invlpg((void*)a);
|
||||
if(pa < PHYSTOP)
|
||||
user_page_free(pgdir, pa);
|
||||
}
|
||||
}
|
||||
return newsz;
|
||||
|
|
|
|||
|
|
@ -711,9 +711,21 @@ consoleread(struct inode *ip, uint64 off, char *dst, int n)
|
|||
|
||||
iunlock(ip);
|
||||
target = n;
|
||||
uint t0 = ticks;
|
||||
acquire(&input_lock);
|
||||
while(n > 0){
|
||||
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) {
|
||||
v->in.eof = 0;
|
||||
release(&input_lock);
|
||||
|
|
@ -752,7 +764,11 @@ consoleread(struct inode *ip, uint64 off, char *dst, int n)
|
|||
acquire(&input_lock);
|
||||
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];
|
||||
*dst++ = c;
|
||||
|
|
|
|||
|
|
@ -407,6 +407,7 @@ kmain(uint64 fb_addr)
|
|||
netuser_init();
|
||||
dbg_thread_start();
|
||||
alarm_thread_start();
|
||||
thread_reaper_start();
|
||||
if (boot_verbose) cprintf("verbose boot: dbg thread started\n");
|
||||
|
||||
// The greeting is not printed here.
|
||||
|
|
|
|||
|
|
@ -664,7 +664,7 @@ do_dma(uint64 argp)
|
|||
|
||||
struct vma *v = 0;
|
||||
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) {
|
||||
blk->proc = 0;
|
||||
return -ENOMEM;
|
||||
|
|
@ -672,9 +672,9 @@ do_dma(uint64 argp)
|
|||
|
||||
// Its own mapping in the caller, taken from the same bump allocator mmap
|
||||
// uses so the two cannot collide.
|
||||
if (p->mmap_next == 0)
|
||||
p->mmap_next = 0x40000000;
|
||||
uint64 va = p->mmap_next;
|
||||
if (p->mm->mmap_next == 0)
|
||||
p->mm->mmap_next = 0x40000000;
|
||||
uint64 va = p->mm->mmap_next;
|
||||
if (va + size < va || va + size > KERNBASE) {
|
||||
blk->proc = 0;
|
||||
return -ENOMEM;
|
||||
|
|
@ -685,7 +685,7 @@ do_dma(uint64 argp)
|
|||
blk->proc = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
p->mmap_next += size;
|
||||
p->mm->mmap_next += size;
|
||||
|
||||
uint64 phys = dma_phys(kva);
|
||||
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;
|
||||
struct elfhdr elf;
|
||||
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
|
||||
// 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));
|
||||
|
||||
for(int i = 0; i < NOFILE; i++){
|
||||
if(myproc()->ofile[i] && (myproc()->ofile_flags[i] & FD_CLOEXEC)){
|
||||
fileclose(myproc()->ofile[i]);
|
||||
myproc()->ofile[i] = 0;
|
||||
myproc()->ofile_flags[i] = 0;
|
||||
if(myproc()->files->ofile[i] && (myproc()->files->flags[i] & FD_CLOEXEC)){
|
||||
fileclose(myproc()->files->ofile[i]);
|
||||
myproc()->files->ofile[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;
|
||||
}
|
||||
|
||||
// Detach shared segments before anything forgets which mappings they were.
|
||||
//
|
||||
// This has to happen while myproc()->pgdir and myproc()->vma[] still describe the
|
||||
// *old* image: the memset below erases the bookkeeping, and freevm() at the
|
||||
// end would then free a shared memory segment's pages as though they were
|
||||
// ordinary heap. Doing it here rather than there is the difference between
|
||||
// detaching a segment and corrupting whoever else has it mapped.
|
||||
proc_release_shared(myproc());
|
||||
|
||||
oldpgdir = myproc()->pgdir;
|
||||
// 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,
|
||||
// and does nothing at all while a vfork parent is still using it.
|
||||
if ((newmm = mm_alloc()) == 0)
|
||||
goto bad;
|
||||
proc_exec_threads();
|
||||
newmm->pgdir = pgdir;
|
||||
newmm->sz = sz;
|
||||
oldmm = myproc()->mm;
|
||||
myproc()->mm = newmm;
|
||||
myproc()->pgdir = pgdir;
|
||||
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.
|
||||
// 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)):
|
||||
// neither points into the old image. `nohup` and every program that ignores
|
||||
// SIGPIPE before running a helper depend on it.
|
||||
memset(myproc()->vma, 0, sizeof(myproc()->vma));
|
||||
for (int i = 0; i < 32; i++)
|
||||
if (myproc()->sig_handler[i] != 1)
|
||||
myproc()->sig_handler[i] = 0;
|
||||
memset(myproc()->sig_restorer, 0, sizeof(myproc()->sig_restorer));
|
||||
if (myproc()->sighand->handler[i] != 1)
|
||||
myproc()->sighand->handler[i] = 0;
|
||||
memset(myproc()->sighand->restorer, 0, sizeof(myproc()->sighand->restorer));
|
||||
myproc()->fs_base = 0;
|
||||
myproc()->pending_signals = 0;
|
||||
myproc()->in_signal = 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
|
||||
// it, exit frees it. mmap carries on above it.
|
||||
if (interp[0]) {
|
||||
myproc()->vma[0].addr = INTERP_BASE;
|
||||
myproc()->vma[0].length = PGROUNDUP(interp_end);
|
||||
myproc()->vma[0].used = 1;
|
||||
myproc()->vma[0].no_free = 0;
|
||||
myproc()->vma[0].shmid = -1;
|
||||
myproc()->mmap_next = INTERP_BASE + PGROUNDUP(interp_end);
|
||||
myproc()->mm->vma[0].addr = INTERP_BASE;
|
||||
myproc()->mm->vma[0].length = PGROUNDUP(interp_end);
|
||||
myproc()->mm->vma[0].used = 1;
|
||||
myproc()->mm->vma[0].no_free = 0;
|
||||
myproc()->mm->vma[0].shmid = -1;
|
||||
myproc()->mm->mmap_next = INTERP_BASE + PGROUNDUP(interp_end);
|
||||
}
|
||||
|
||||
myproc()->tf->rax = 0;
|
||||
|
|
@ -637,7 +638,8 @@ exec_elf(char *path, struct argsrc *av, struct argsrc *ev)
|
|||
// old one's registers.
|
||||
memmove(myproc()->fpu, fpu_default, sizeof(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(bounce);
|
||||
fsstat.exec_cycles += rdtsc() - _t0;
|
||||
|
|
|
|||
|
|
@ -70,7 +70,8 @@
|
|||
#define PF_SCHEDSTAT 6
|
||||
#define PF_FD 7 // the directory /proc/<pid>/fd
|
||||
#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 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
|
||||
// path it was opened by, or at "socket:[ino]" and the like.
|
||||
{ "fd", PF_FD },
|
||||
// The process's threads, one directory each, named by thread id. A process
|
||||
// here is one thread, so task/ holds a single entry, the pid, and it *is*
|
||||
// /proc/<pid> -- the same inode. htop reads every process through it and
|
||||
// The process's threads, one directory each, named by thread id; each is
|
||||
// /proc/<tid> -- the same inode. htop reads every process through it and
|
||||
// shows nothing at all without it.
|
||||
{ "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])))
|
||||
|
||||
|
|
@ -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->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
|
||||
sbputc(s, ' ');
|
||||
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, "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, "\nPPid:\t"); sbputi(s, pi->ppid);
|
||||
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);
|
||||
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
|
||||
// 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_STATM: gen_pid_statm(&s, &pi); 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:
|
||||
sbputi(&s, (int64)pi.vruntime); 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)
|
||||
: 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 (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 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.
|
||||
|
|
@ -647,7 +655,11 @@ procfs_getdents(struct inode *dp, char *out, int cap, uint64 *off)
|
|||
EMIT(PROCFS_SELF, DT_LNK_, "self");
|
||||
for (i = 0; i < NGLOBALS; i++)
|
||||
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++) {
|
||||
if (pi.tgid != pi.pid)
|
||||
continue;
|
||||
utoa((uint)pi.pid, 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) {
|
||||
EMIT(dp->inum, DT_DIR_, ".");
|
||||
EMIT(dp->inum & ~0xFFULL, DT_DIR_, "..");
|
||||
utoa((uint)INO_PID(dp->inum), name);
|
||||
EMIT((dp->inum & ~0xFFULL) | PF_DIR, DT_DIR_, name);
|
||||
for (i = 0; proc_snapshot(i, &pi) == 0; i++) {
|
||||
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 {
|
||||
EMIT(dp->inum, DT_DIR_, ".");
|
||||
EMIT(PROCFS_ROOT, DT_DIR_, "..");
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ net_interrupted(void)
|
|||
return 1;
|
||||
uint64 live = p->pending_signals & ~p->signal_mask;
|
||||
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 0;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -32,10 +32,12 @@ sys_enable_linux_abi(void)
|
|||
addr_t
|
||||
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;
|
||||
argaddr(0, &tidp);
|
||||
if (tidp)
|
||||
put_user(myproc()->pid, tidp);
|
||||
myproc()->clear_child_tid = tidp;
|
||||
return myproc()->pid;
|
||||
}
|
||||
|
||||
|
|
@ -46,12 +48,15 @@ addr_t sys_set_robust_list(void) { return 0; }
|
|||
addr_t
|
||||
sys_futex(void)
|
||||
{
|
||||
int op;
|
||||
addr_t uaddr, utime, uaddr2;
|
||||
int op, val, val3;
|
||||
argaddr(0, &uaddr);
|
||||
argint(1, &op);
|
||||
op &= ~(FUTEX_WAIT_PRIVATE - FUTEX_WAIT);
|
||||
if (op == FUTEX_WAKE || op == FUTEX_WAKE_PRIVATE)
|
||||
return 0;
|
||||
return -EAGAIN; // no blocking, musl retries
|
||||
argint(2, &val);
|
||||
argaddr(3, &utime);
|
||||
argaddr(4, &uaddr2);
|
||||
argint(5, &val3);
|
||||
return futex_op(uaddr, op, val, utime, uaddr2, val3);
|
||||
}
|
||||
|
||||
// arch_prctl: save FS_BASE for TLS.
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ ev_open(struct evobj *o, int flags, int readable, int writable)
|
|||
return -EMFILE;
|
||||
}
|
||||
if (flags & O_CLOEXEC)
|
||||
myproc()->ofile_flags[fd] |= FD_CLOEXEC;
|
||||
myproc()->files->flags[fd] |= FD_CLOEXEC;
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
|
@ -163,7 +163,7 @@ ev_arg(int n, int kind, struct file **pf)
|
|||
{
|
||||
int fd;
|
||||
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;
|
||||
if (f->type != FD_EVENT || f->ev->kind != kind)
|
||||
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.
|
||||
if (fd != -1) {
|
||||
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)
|
||||
return -EINVAL;
|
||||
acquiresleep(&f->ev->lock);
|
||||
|
|
@ -606,9 +606,9 @@ sys_epoll_ctl(void)
|
|||
return -EBADF;
|
||||
if (argint(1, &op) < 0 || argint(2, &fd) < 0 || argaddr(3, &uev) < 0)
|
||||
return -EINVAL;
|
||||
if (fd < 0 || fd >= NOFILE || !myproc()->ofile[fd])
|
||||
if (fd < 0 || fd >= NOFILE || !myproc()->files->ofile[fd])
|
||||
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
|
||||
// check is the deep one Linux does, and nothing here builds such loops.)
|
||||
if (tf == ef)
|
||||
|
|
@ -662,7 +662,7 @@ ep_ready(struct evobj *o, struct epoll_event *out, int max)
|
|||
acquiresleep(&o->lock);
|
||||
for (int i = 0; i < o->ep.n && got < max; ) {
|
||||
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) {
|
||||
ep_drop(o, i); // closed: it leaves the set
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ argfd(int n, int *pfd, struct file **pf)
|
|||
struct file *f;
|
||||
if (argint(n, &fd) < 0)
|
||||
return -1;
|
||||
if (fd < 0 || fd >= NOFILE || !(f = myproc()->ofile[fd]))
|
||||
if (fd < 0 || fd >= NOFILE || !(f = myproc()->files->ofile[fd]))
|
||||
return -1;
|
||||
if (pfd) *pfd = fd;
|
||||
if (pf) *pf = f;
|
||||
|
|
@ -155,13 +155,18 @@ argfd(int n, int *pfd, struct file **pf)
|
|||
int
|
||||
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++) {
|
||||
if (!myproc()->ofile[fd]) {
|
||||
myproc()->ofile[fd] = f;
|
||||
myproc()->ofile_flags[fd] = 0;
|
||||
if (!myproc()->files->ofile[fd]) {
|
||||
myproc()->files->ofile[fd] = f;
|
||||
myproc()->files->flags[fd] = 0;
|
||||
files_unlock();
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
files_unlock();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -178,7 +183,7 @@ file_setpath(struct file *f, int dirfd, const char *path)
|
|||
struct file *d;
|
||||
if (dirfd == AT_FDCWD)
|
||||
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;
|
||||
else
|
||||
return;
|
||||
|
|
@ -218,8 +223,8 @@ static struct inode *
|
|||
at_dir(int dirfd)
|
||||
{
|
||||
if (dirfd == AT_FDCWD) return myproc()->cwd;
|
||||
if (dirfd < 0 || dirfd >= NOFILE || !myproc()->ofile[dirfd]) return 0;
|
||||
struct file *f = myproc()->ofile[dirfd];
|
||||
if (dirfd < 0 || dirfd >= NOFILE || !myproc()->files->ofile[dirfd]) return 0;
|
||||
struct file *f = myproc()->files->ofile[dirfd];
|
||||
if (f->type != FD_INODE) return 0;
|
||||
return f->ip;
|
||||
}
|
||||
|
|
@ -393,12 +398,12 @@ addr_t sys_dup2(void)
|
|||
int oldfd, newfd;
|
||||
struct file *f;
|
||||
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 (oldfd == newfd) return newfd;
|
||||
if (myproc()->ofile[newfd]) fileclose(myproc()->ofile[newfd]);
|
||||
myproc()->ofile[newfd] = filedup(f);
|
||||
myproc()->ofile_flags[newfd] = 0; // dup2 never carries FD_CLOEXEC over
|
||||
if (myproc()->files->ofile[newfd]) fileclose(myproc()->files->ofile[newfd]);
|
||||
myproc()->files->ofile[newfd] = filedup(f);
|
||||
myproc()->files->flags[newfd] = 0; // dup2 never carries FD_CLOEXEC over
|
||||
return newfd;
|
||||
}
|
||||
|
||||
|
|
@ -412,9 +417,9 @@ addr_t sys_dup3(void)
|
|||
// Unlike dup2, equal fds are an error (dup3(2)). Without this check we
|
||||
// would fileclose(f) and then filedup() the file we just released.
|
||||
if (oldfd == newfd) return -EINVAL;
|
||||
if (myproc()->ofile[newfd]) fileclose(myproc()->ofile[newfd]);
|
||||
myproc()->ofile[newfd] = filedup(f);
|
||||
myproc()->ofile_flags[newfd] = 0;
|
||||
if (myproc()->files->ofile[newfd]) fileclose(myproc()->files->ofile[newfd]);
|
||||
myproc()->files->ofile[newfd] = filedup(f);
|
||||
myproc()->files->flags[newfd] = 0;
|
||||
return newfd;
|
||||
}
|
||||
|
||||
|
|
@ -423,8 +428,8 @@ addr_t sys_close(void)
|
|||
int fd;
|
||||
struct file *f;
|
||||
if (argfd(0, &fd, &f) < 0) return -EBADF;
|
||||
myproc()->ofile[fd] = 0;
|
||||
myproc()->ofile_flags[fd] = 0;
|
||||
myproc()->files->ofile[fd] = 0;
|
||||
myproc()->files->flags[fd] = 0;
|
||||
lock_close_fd(f);
|
||||
fileclose(f);
|
||||
return 0;
|
||||
|
|
@ -444,23 +449,23 @@ sys_fcntl(void)
|
|||
{
|
||||
int fd, cmd, arg;
|
||||
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;
|
||||
struct file *f = myproc()->ofile[fd];
|
||||
if (fd < 0 || fd >= NOFILE || !myproc()->files->ofile[fd]) return -1;
|
||||
struct file *f = myproc()->files->ofile[fd];
|
||||
switch (cmd) {
|
||||
case F_DUPFD:
|
||||
case F_DUPFD_CLOEXEC: {
|
||||
if (arg < 0) return -EINVAL;
|
||||
for (int nfd = arg; nfd < NOFILE; nfd++) {
|
||||
if (!myproc()->ofile[nfd]) {
|
||||
myproc()->ofile[nfd] = filedup(f);
|
||||
myproc()->ofile_flags[nfd] = (cmd == F_DUPFD_CLOEXEC) ? FD_CLOEXEC : 0;
|
||||
if (!myproc()->files->ofile[nfd]) {
|
||||
myproc()->files->ofile[nfd] = filedup(f);
|
||||
myproc()->files->flags[nfd] = (cmd == F_DUPFD_CLOEXEC) ? FD_CLOEXEC : 0;
|
||||
return nfd;
|
||||
}
|
||||
}
|
||||
return -EMFILE;
|
||||
}
|
||||
case F_GETFD: return myproc()->ofile_flags[fd];
|
||||
case F_SETFD: myproc()->ofile_flags[fd] = arg; return 0;
|
||||
case F_GETFD: return myproc()->files->flags[fd];
|
||||
case F_SETFD: myproc()->files->flags[fd] = arg; return 0;
|
||||
case F_GETFL: {
|
||||
int flags = 0;
|
||||
if (f->readable && f->writable) flags = O_RDWR;
|
||||
|
|
@ -592,7 +597,7 @@ addr_t sys_pipe(void)
|
|||
int fd0 = -1, fd1;
|
||||
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 >= 0) myproc()->ofile[fd0] = 0;
|
||||
if (fd0 >= 0) myproc()->files->ofile[fd0] = 0;
|
||||
fileclose(rf); fileclose(wf);
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -614,14 +619,14 @@ addr_t sys_pipe2(void)
|
|||
if (!fds || pipealloc(&rf, &wf) < 0) return -1;
|
||||
fd0 = fd1 = -1;
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
rf->nonblock = wf->nonblock = (flags & O_NONBLOCK) != 0;
|
||||
if (flags & O_CLOEXEC) {
|
||||
myproc()->ofile_flags[fd0] |= FD_CLOEXEC;
|
||||
myproc()->ofile_flags[fd1] |= FD_CLOEXEC;
|
||||
myproc()->files->flags[fd0] |= FD_CLOEXEC;
|
||||
myproc()->files->flags[fd1] |= FD_CLOEXEC;
|
||||
}
|
||||
fds[0] = fd0; fds[1] = fd1;
|
||||
return 0;
|
||||
|
|
@ -1518,7 +1523,7 @@ addr_t sys_ioctl(void)
|
|||
|
||||
if (f->type != FD_INODE || !f->ip || !f->writable)
|
||||
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;
|
||||
if (sf->type != FD_INODE || !sf->ip || !sf->readable)
|
||||
return -EBADF;
|
||||
|
|
@ -2344,8 +2349,8 @@ addr_t sys_fstatat(void)
|
|||
|
||||
// AT_EMPTY_PATH: if path is empty, fstat the dirfd itself.
|
||||
if ((flags & AT_EMPTY_PATH) && path[0] == '\0') {
|
||||
if (dirfd < 0 || dirfd >= NOFILE || !myproc()->ofile[dirfd]) return -1;
|
||||
struct file *f = myproc()->ofile[dirfd];
|
||||
if (dirfd < 0 || dirfd >= NOFILE || !myproc()->files->ofile[dirfd]) return -1;
|
||||
struct file *f = myproc()->files->ofile[dirfd];
|
||||
if (f->type != FD_INODE) return -1;
|
||||
ilock(f->ip);
|
||||
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)
|
||||
{
|
||||
for (int i = 0; i < NVMA; i++)
|
||||
if (!p->vma[i].used)
|
||||
return &p->vma[i];
|
||||
if (!p->mm->vma[i].used)
|
||||
return &p->mm->vma[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ static int
|
|||
vma_note_anon(struct proc *p, uint64 va, uint64 len)
|
||||
{
|
||||
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) {
|
||||
v->length += len;
|
||||
return 0;
|
||||
|
|
@ -81,7 +81,7 @@ static int
|
|||
range_free(struct proc *p, uint64 va, uint64 len)
|
||||
{
|
||||
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)
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -104,21 +104,21 @@ range_free(struct proc *p, uint64 va, uint64 len)
|
|||
static uint64
|
||||
mmap_va(struct proc *p, uint64 len, int mergeable)
|
||||
{
|
||||
if (p->mmap_next == 0)
|
||||
p->mmap_next = 0x40000000;
|
||||
if (p->mm->mmap_next == 0)
|
||||
p->mm->mmap_next = 0x40000000;
|
||||
if (mergeable) {
|
||||
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;
|
||||
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))
|
||||
return e;
|
||||
}
|
||||
}
|
||||
uint64 va = p->mmap_next;
|
||||
uint64 va = p->mm->mmap_next;
|
||||
if (bad_user_range(va, len))
|
||||
return 0;
|
||||
p->mmap_next += len;
|
||||
p->mm->mmap_next += len;
|
||||
return va;
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +138,7 @@ vma_cover_anon(struct proc *p, uint64 va, uint64 len)
|
|||
uint64 next = end;
|
||||
int inside = -1;
|
||||
for (int i = 0; i < NVMA; i++) {
|
||||
struct vma *v = &p->vma[i];
|
||||
struct vma *v = &p->mm->vma[i];
|
||||
if (!v->used)
|
||||
continue;
|
||||
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;
|
||||
}
|
||||
if (inside >= 0) {
|
||||
cur = p->vma[inside].addr + p->vma[inside].length;
|
||||
cur = p->mm->vma[inside].addr + p->mm->vma[inside].length;
|
||||
continue;
|
||||
}
|
||||
if (vma_note_anon(p, cur, next - cur) < 0)
|
||||
|
|
@ -180,8 +180,8 @@ sys_brk(void)
|
|||
return myproc()->sz;
|
||||
}
|
||||
|
||||
addr_t
|
||||
sys_mmap(void)
|
||||
static addr_t
|
||||
mmap_locked(void)
|
||||
{
|
||||
uint64 addr, length, offset;
|
||||
int prot, flags, fd;
|
||||
|
|
@ -193,7 +193,7 @@ sys_mmap(void)
|
|||
return -1;
|
||||
|
||||
if (fd >= 0 && fd < NOFILE)
|
||||
f = myproc()->ofile[fd];
|
||||
f = myproc()->files->ofile[fd];
|
||||
|
||||
length = PGROUNDUP(length);
|
||||
uint64 va = addr;
|
||||
|
|
@ -425,8 +425,8 @@ sys_mmap(void)
|
|||
return va;
|
||||
}
|
||||
|
||||
addr_t
|
||||
sys_munmap(void)
|
||||
static addr_t
|
||||
munmap_locked(void)
|
||||
{
|
||||
uint64 addr, length;
|
||||
struct proc *p = myproc();
|
||||
|
|
@ -441,7 +441,7 @@ sys_munmap(void)
|
|||
// Part of one is refused: unmapping it the anonymous way below would kfree
|
||||
// pages that are the framebuffer's or another process's.
|
||||
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))
|
||||
continue;
|
||||
if (v->addr == addr) {
|
||||
|
|
@ -466,7 +466,7 @@ sys_munmap(void)
|
|||
// are not there, and the bump allocator never hands the addresses out
|
||||
// again.
|
||||
for (int i = 0; i < NVMA; i++) {
|
||||
struct vma *v = &p->vma[i];
|
||||
struct vma *v = &p->mm->vma[i];
|
||||
if (!vma_anon(v))
|
||||
continue;
|
||||
uint64 vs = v->addr, ve = v->addr + v->length;
|
||||
|
|
@ -835,8 +835,8 @@ sys_shmget(void)
|
|||
return -ENOSPC;
|
||||
}
|
||||
|
||||
addr_t
|
||||
sys_shmat(void)
|
||||
static addr_t
|
||||
shmat_locked(void)
|
||||
{
|
||||
int id, flags;
|
||||
addr_t addr;
|
||||
|
|
@ -859,15 +859,15 @@ sys_shmat(void)
|
|||
|
||||
struct vma *v = 0;
|
||||
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; }
|
||||
|
||||
// Ignore the hint and take a fresh range, the same way mmap does.
|
||||
if (myproc()->mmap_next == 0)
|
||||
myproc()->mmap_next = 0x40000000;
|
||||
uint64 va = myproc()->mmap_next;
|
||||
if (myproc()->mm->mmap_next == 0)
|
||||
myproc()->mm->mmap_next = 0x40000000;
|
||||
uint64 va = myproc()->mm->mmap_next;
|
||||
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++) {
|
||||
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.
|
||||
void
|
||||
proc_release_shared(struct proc *p)
|
||||
mm_release_shared(struct mm *m)
|
||||
{
|
||||
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;
|
||||
vm_unmap_norelease(p->pgdir, p->vma[i].addr, p->vma[i].length);
|
||||
if (p->vma[i].shmid >= 0)
|
||||
shm_detach(p->vma[i].shmid);
|
||||
p->vma[i].used = 0;
|
||||
p->vma[i].no_free = 0;
|
||||
p->vma[i].shmid = -1;
|
||||
if (m->pgdir)
|
||||
vm_unmap_norelease(m->pgdir, m->vma[i].addr, m->vma[i].length);
|
||||
if (m->vma[i].shmid >= 0)
|
||||
shm_detach(m->vma[i].shmid);
|
||||
m->vma[i].used = 0;
|
||||
m->vma[i].no_free = 0;
|
||||
m->vma[i].shmid = -1;
|
||||
}
|
||||
}
|
||||
|
||||
addr_t
|
||||
sys_shmdt(void)
|
||||
static addr_t
|
||||
shmdt_locked(void)
|
||||
{
|
||||
addr_t addr;
|
||||
if (argaddr(0, &addr) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (int i = 0; i < NVMA; i++) {
|
||||
if (myproc()->vma[i].used && myproc()->vma[i].addr == addr &&
|
||||
myproc()->vma[i].shmid >= 0) {
|
||||
vm_unmap_norelease(myproc()->pgdir, addr, myproc()->vma[i].length);
|
||||
shm_detach(myproc()->vma[i].shmid);
|
||||
myproc()->vma[i].used = myproc()->vma[i].addr = myproc()->vma[i].length = 0;
|
||||
myproc()->vma[i].no_free = 0;
|
||||
myproc()->vma[i].shmid = -1;
|
||||
if (myproc()->mm->vma[i].used && myproc()->mm->vma[i].addr == addr &&
|
||||
myproc()->mm->vma[i].shmid >= 0) {
|
||||
vm_unmap_norelease(myproc()->pgdir, addr, myproc()->mm->vma[i].length);
|
||||
shm_detach(myproc()->mm->vma[i].shmid);
|
||||
myproc()->mm->vma[i].used = myproc()->mm->vma[i].addr = myproc()->mm->vma[i].length = 0;
|
||||
myproc()->mm->vma[i].no_free = 0;
|
||||
myproc()->mm->vma[i].shmid = -1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -981,3 +982,21 @@ sys_setpriority(void)
|
|||
return -EINVAL;
|
||||
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->nonblock = (sflags & SOCK_NONBLOCK) != 0;
|
||||
if (sflags & SOCK_CLOEXEC)
|
||||
myproc()->ofile_flags[fd] |= FD_CLOEXEC;
|
||||
myproc()->files->flags[fd] |= FD_CLOEXEC;
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
|
@ -136,7 +136,7 @@ argsock(int n, struct socket **ps)
|
|||
|
||||
if (argint(n, &fd) < 0)
|
||||
return -EBADF;
|
||||
if (fd < 0 || fd >= NOFILE || !(f = myproc()->ofile[fd]))
|
||||
if (fd < 0 || fd >= NOFILE || !(f = myproc()->files->ofile[fd]))
|
||||
return -EBADF;
|
||||
if (f->type != FD_SOCKET)
|
||||
return -ENOTSOCK;
|
||||
|
|
@ -471,7 +471,7 @@ SYSCALL_DEFINE(socketpair)
|
|||
return fd0;
|
||||
}
|
||||
if ((fd1 = sock_to_fd(b, sflags)) < 0) {
|
||||
myproc()->ofile[fd0] = 0;
|
||||
myproc()->files->ofile[fd0] = 0;
|
||||
sockclose(a);
|
||||
sockclose(b);
|
||||
return fd1;
|
||||
|
|
@ -479,8 +479,8 @@ SYSCALL_DEFINE(socketpair)
|
|||
|
||||
int sv[2] = { fd0, fd1 };
|
||||
if (copy_to_user(usv, sv, sizeof(sv)) < 0) {
|
||||
myproc()->ofile[fd0] = 0;
|
||||
myproc()->ofile[fd1] = 0;
|
||||
myproc()->files->ofile[fd0] = 0;
|
||||
myproc()->files->ofile[fd1] = 0;
|
||||
sockclose(a);
|
||||
sockclose(b);
|
||||
return -EFAULT;
|
||||
|
|
@ -808,7 +808,7 @@ argsockfile(int n, struct socket **ps, struct file **pf)
|
|||
|
||||
if (argint(n, &fd) < 0)
|
||||
return -EBADF;
|
||||
if (fd < 0 || fd >= NOFILE || !(f = myproc()->ofile[fd]))
|
||||
if (fd < 0 || fd >= NOFILE || !(f = myproc()->files->ofile[fd]))
|
||||
return -EBADF;
|
||||
if (f->type != FD_SOCKET)
|
||||
return -ENOTSOCK;
|
||||
|
|
@ -879,7 +879,7 @@ scm_gather(struct msghdr *m, struct file **fs, int *nfp)
|
|||
r = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
if (ufd < 0 || ufd >= NOFILE || !(f = myproc()->ofile[ufd])) {
|
||||
if (ufd < 0 || ufd >= NOFILE || !(f = myproc()->files->ofile[ufd])) {
|
||||
r = -EBADF;
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -1078,7 +1078,7 @@ SYSCALL_DEFINE(recvmsg)
|
|||
if (d < 0)
|
||||
break;
|
||||
if (flags & MSG_CMSG_CLOEXEC)
|
||||
myproc()->ofile_flags[d] |= FD_CLOEXEC;
|
||||
myproc()->files->flags[d] |= FD_CLOEXEC;
|
||||
ufd[installed++] = d;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,12 +79,12 @@ do_poll(struct pollfd *fds, int nfds, int timeout_ticks)
|
|||
fds[i].revents = 0;
|
||||
if (fds[i].fd < 0)
|
||||
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;
|
||||
nready++;
|
||||
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) {
|
||||
fds[i].revents = (short)rev;
|
||||
nready++;
|
||||
|
|
@ -217,7 +217,7 @@ sys_select(void)
|
|||
int want_w = fdset_isset(wr, fd);
|
||||
if (!want_r && !want_w)
|
||||
continue;
|
||||
struct file *f = myproc()->ofile[fd];
|
||||
struct file *f = myproc()->files->ofile[fd];
|
||||
if (!f)
|
||||
return -EBADF;
|
||||
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);
|
||||
if (!want_r && !want_w)
|
||||
continue;
|
||||
struct file *f = myproc()->ofile[fd];
|
||||
struct file *f = myproc()->files->ofile[fd];
|
||||
if (!f)
|
||||
return -EBADF;
|
||||
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.
|
||||
addr_t
|
||||
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;
|
||||
argint(0, &code);
|
||||
exit(W_EXITCODE(code, 0));
|
||||
return 0;
|
||||
}
|
||||
addr_t sys_exit_group(void) { return sys_exit(); }
|
||||
|
||||
|
||||
addr_t
|
||||
|
|
@ -62,9 +76,10 @@ sys_kill(void)
|
|||
// Defined with getrusage() further down; wait4() needs it too.
|
||||
static void rusage_fill(struct rusage *ru, uint64 ut, uint64 st);
|
||||
|
||||
addr_t sys_getpid(void) { return myproc()->pid; }
|
||||
addr_t sys_getppid(void) { return myproc()->parent ? myproc()->parent->pid : 1; }
|
||||
addr_t sys_gettid(void) { return myproc()->pid; } // single-threaded: tid == pid
|
||||
// A process is its thread group: getpid is the group's id, gettid the thread's.
|
||||
addr_t sys_getpid(void) { return myproc()->tgid; }
|
||||
addr_t sys_getppid(void) { return myproc()->parent ? myproc()->parent->tgid : 1; }
|
||||
addr_t sys_gettid(void) { return myproc()->pid; }
|
||||
|
||||
addr_t
|
||||
sys_waitpid(void)
|
||||
|
|
@ -122,11 +137,13 @@ sys_wait4(void)
|
|||
addr_t
|
||||
sys_clone(void)
|
||||
{
|
||||
int flags;
|
||||
argint(0, &flags);
|
||||
if (flags & (CLONE_VM | CLONE_THREAD | CLONE_SIGHAND))
|
||||
return -ENOSYS;
|
||||
return fork();
|
||||
uint64 flags, stack, ptid, ctid, tls;
|
||||
argaddr(0, &flags);
|
||||
argaddr(1, &stack);
|
||||
argaddr(2, &ptid);
|
||||
argaddr(3, &ctid);
|
||||
argaddr(4, &tls);
|
||||
return clone_proc(flags, stack, ptid, ctid, tls);
|
||||
}
|
||||
|
||||
addr_t
|
||||
|
|
@ -424,10 +441,11 @@ addr_t sys_tgkill(void)
|
|||
{
|
||||
int tgid, tid, sig;
|
||||
argint(0, &tgid); argint(1, &tid); argint(2, &sig);
|
||||
// In our single-threaded model tgid == tid == pid.
|
||||
if (tgid <= 0) return 0;
|
||||
if (sig == 0) return proc_exists(tgid) ? 0 : -ESRCH;
|
||||
return sigsend(tgid, sig) < 0 ? -ESRCH : 0;
|
||||
// To one thread: musl's pthread_kill, and raise(), which is tgkill on
|
||||
// the calling thread.
|
||||
if (tid <= 0) return -EINVAL;
|
||||
if (sig == 0) return proc_exists(tid) ? 0 : -ESRCH;
|
||||
return sigsend(tid, sig) < 0 ? -ESRCH : 0;
|
||||
}
|
||||
|
||||
addr_t sys_tkill(void)
|
||||
|
|
|
|||
|
|
@ -115,12 +115,12 @@ sys_rt_sigaction(void)
|
|||
|
||||
struct proc *p = myproc();
|
||||
if (oldactp) {
|
||||
put_user(p->sig_handler[sig], oldactp + SA_HANDLER_OFF);
|
||||
put_user(p->sig_restorer[sig], oldactp + SA_RESTORER_OFF(p));
|
||||
put_user(p->sighand->handler[sig], oldactp + SA_HANDLER_OFF);
|
||||
put_user(p->sighand->restorer[sig], oldactp + SA_RESTORER_OFF(p));
|
||||
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 {
|
||||
uint32 fl = (uint32)p->sig_flags[sig];
|
||||
uint32 fl = (uint32)p->sighand->flags[sig];
|
||||
put_user(fl, oldactp + SA_FLAGS_OFF(p));
|
||||
}
|
||||
}
|
||||
|
|
@ -134,9 +134,9 @@ sys_rt_sigaction(void)
|
|||
// SIGKILL and SIGSTOP cannot be caught or ignored.
|
||||
if (sig == SIGKILL || sig == SIGSTOP)
|
||||
return -EINVAL;
|
||||
p->sig_handler[sig] = h;
|
||||
p->sig_restorer[sig] = r;
|
||||
p->sig_flags[sig] = fl;
|
||||
p->sighand->handler[sig] = h;
|
||||
p->sighand->restorer[sig] = r;
|
||||
p->sighand->flags[sig] = fl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -218,16 +218,16 @@ deliver_signals(struct trapframe *tf)
|
|||
// SIGSTOP cannot be caught or ignored; the other three stop only by
|
||||
// default. Stopped here and resumed here, by SIGCONT or SIGKILL.
|
||||
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);
|
||||
proc_stop(sig);
|
||||
if (myproc()->killed)
|
||||
exit(W_EXITCODE(0, myproc()->killsig ? myproc()->killsig : SIGKILL));
|
||||
continue;
|
||||
}
|
||||
if (myproc()->sig_handler[sig] <= 1) {
|
||||
if (myproc()->sighand->handler[sig] <= 1) {
|
||||
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;
|
||||
if (myproc()->killsig == 0)
|
||||
myproc()->killsig = sig;
|
||||
|
|
@ -283,8 +283,8 @@ deliver_signals(struct trapframe *tf)
|
|||
// shifted rsp, and its ret fell into the sigreturn trampoline,
|
||||
// silently undoing everything. exec() handles this same quirk by
|
||||
// setting tf->rcx = elf.entry.
|
||||
tf->rip = myproc()->sig_handler[sig];
|
||||
tf->rcx = myproc()->sig_handler[sig];
|
||||
tf->rip = myproc()->sighand->handler[sig];
|
||||
tf->rcx = myproc()->sighand->handler[sig];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -344,7 +344,7 @@ addr_t sys_rt_sigsuspend(void)
|
|||
uint64 deliverable = myproc()->pending_signals & ~myproc()->signal_mask;
|
||||
int will_deliver = 0;
|
||||
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; }
|
||||
|
||||
if (will_deliver && !myproc()->in_signal) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue