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
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue