feat: extend sched
This commit is contained in:
1 parent
30188c37d0
commit
268707b571
1 file changed
+159
-1
+159
-1
@@ -120,8 +120,166 @@ typedef struct Thread {
|
|||||||
uint64 exitCode;
|
uint64 exitCode;
|
||||||
uint32 weight;
|
uint32 weight;
|
||||||
uint32 id;
|
uint32 id;
|
||||||
|
/* Id of whichever thread was actually running (currentIndex, sched.c)
|
||||||
|
at the moment this thread was created -- threadCreate/
|
||||||
|
threadCreateUser both set this unconditionally, so it is always
|
||||||
|
meaningful, not just for real userspace parent/child pairs: a
|
||||||
|
boot-time spawnUser (kmain.c) call happens before schedStart() ever
|
||||||
|
runs, so every process kmain.c spawns directly gets the permanent
|
||||||
|
idle thread (id 0) as its parentId, which is deliberately never a
|
||||||
|
valid sigRaise target (see signal.c's targetId==0 rejection) --
|
||||||
|
only a RUNTIME sysSpawn (syscall.c's doSpawn, called from inside a
|
||||||
|
live thread's own syscall) ever produces a parentId that is itself
|
||||||
|
a live, signalable process. Consulted by threadExit/threadKill
|
||||||
|
below to deliver a real SIGCHLD to whoever spawned this thread the
|
||||||
|
instant it actually dies, matching real wait()/SIGCHLD semantics --
|
||||||
|
NOT the same thing as "whoever later calls threadJoin/sysWait on
|
||||||
|
this id", which can be anyone that knows the pid, not just the
|
||||||
|
spawner (this kernel's honest fork()-less design already documents
|
||||||
|
that distinction, see doSpawn's own file comment in syscall.c). */
|
||||||
|
uint32 parentId;
|
||||||
|
/* Distinguishes THIS specific occupant of slot `id` from any other
|
||||||
|
thread that has occupied (or will ever occupy) the same numeric slot
|
||||||
|
before or after -- the exact same "handle number alone is not enough
|
||||||
|
once a slot is recycled" idea pipe.c's own Pipe.incarnation already
|
||||||
|
solves for pipe handles (see pipeIncarnation's own doc comment,
|
||||||
|
pipe.h). Assigned once, at creation time, from a single monotonic
|
||||||
|
counter (sched.c's nextThreadGeneration, never reused, mirroring
|
||||||
|
pipe.c's nextPipeIncarnation exactly) -- threadReap/threadDiscard's
|
||||||
|
memset resets this back to 0 when the slot is freed, and the next
|
||||||
|
threadCreate/threadCreateUser to land on that slot gets a brand new,
|
||||||
|
never-before-seen value, so 0 here always means "this exact slot is
|
||||||
|
currently unoccupied," never a live thread's own generation. Exists
|
||||||
|
purely so pidfd.h's pidfdOpen/pidfdTarget can tell "the same thread I
|
||||||
|
opened a handle on earlier" apart from "a different, unrelated thread
|
||||||
|
that has since been handed the same slot number" -- allocSlot's own
|
||||||
|
doc comment (sched.c) documents the real, previously-unguarded race
|
||||||
|
this closes for any caller that opts into pidfd.h instead of a raw
|
||||||
|
pid (kill/wait/sigSend/setpgid/nice below all still take a raw pid,
|
||||||
|
completely unchanged, exactly as that same comment explains why). */
|
||||||
|
uint32 generation;
|
||||||
|
/* Process group id (POSIX pgid): the scope real job control and real
|
||||||
|
kill(2)/sigSend's pid<0 convention actually target, not a single
|
||||||
|
thread (signalnum.h's sigInt/sigTstp arriving from keyboard.c's real
|
||||||
|
Ctrl-C/Ctrl-Z tracking are the live proof -- see keyboard.c's
|
||||||
|
keyboardHandleScancode and signal.c's sigRaiseGroup). Set once, at
|
||||||
|
creation time, by threadCreate/threadCreateUser's own initialPgid
|
||||||
|
helper (sched.c): a thread created while the idle thread (id 0) is
|
||||||
|
"current" -- i.e. one of kmain.c's boot-time spawnUser/threadCreate
|
||||||
|
calls, the only time that can ever be true, see parentId's own
|
||||||
|
comment just above -- becomes its own new group leader (pgid == its
|
||||||
|
own id), exactly like a real init process placing each service it
|
||||||
|
starts into its own job; a thread created by a genuinely running
|
||||||
|
parent (any runtime sysSpawn, syscall.c's doSpawn) instead INHERITS
|
||||||
|
that parent's pgid, matching real fork()'s own default (a child
|
||||||
|
stays in its parent's process group until something explicitly
|
||||||
|
calls setpgid). threadSetPgid (below) is the only thing that ever
|
||||||
|
changes it again after creation -- the sysSetpgid syscall's
|
||||||
|
userspace-facing half of real setpgid(2). */
|
||||||
|
uint32 pgid;
|
||||||
|
/* Real POSIX credential (uid(2)/setuid(2)): the identity vfs.c's
|
||||||
|
vfsPermitted consults, alongside a file's own owner uid and mode, to
|
||||||
|
decide owner-vs-other access at exactly the two enforcement choke
|
||||||
|
points fs.h's fsModeOwner and fsModeOther bits already gate
|
||||||
|
(resolveOpen's write check, loadImageByPath/jobctlSpawn's exec check) -- see
|
||||||
|
vfsPermitted's own doc comment for the full owner/other algorithm
|
||||||
|
(deliberately no uid-0-bypasses-everything superuser rule -- see that
|
||||||
|
same comment for why). Same creation-time default/copy split as pgid
|
||||||
|
immediately above (sched.c's new initialUid, mirroring initialPgid's
|
||||||
|
own "is the idle thread currently running" test): a boot-time thread
|
||||||
|
(kmain.c's spawnUser/threadCreate calls, idle currently "current")
|
||||||
|
starts as uid 0 ("root" in name only here, not in VFS privilege --
|
||||||
|
see fs.h's fsModeOwner* doc comment), exactly like a real init
|
||||||
|
process; a genuine runtime child (syscall.c's doSpawn) instead
|
||||||
|
INHERITS its parent's uid, matching real fork()'s own default before
|
||||||
|
any setuid ever runs. currentThreadSetUid (below) is the only thing
|
||||||
|
that ever changes it again after creation -- and, matching real
|
||||||
|
setuid(2)'s own privilege rule, only ever lets uid 0 become anyone;
|
||||||
|
any other uid may only "become" itself (a no-op), so dropping root is
|
||||||
|
real and permanent for the lifetime of this thread, exactly like a
|
||||||
|
real daemon that setuid()s away from root at startup and can never
|
||||||
|
regain it. */
|
||||||
|
uint32 uid;
|
||||||
|
/* Real POSIX group credential (getgid(2)/setgid(2)): the group identity
|
||||||
|
vfs.c's vfsPermitted consults, alongside a file's own group gid, as
|
||||||
|
the MIDDLE fallback between the owner and other checks fs.h's
|
||||||
|
fsModeGroup* bits gate. Same creation-time default/copy split as uid
|
||||||
|
immediately above (sched.c's new initialGid, mirroring initialUid's
|
||||||
|
own shape exactly): a boot-time thread starts at gid 0, a genuine
|
||||||
|
runtime child (syscall.c's doSpawn) INHERITS its parent's gid.
|
||||||
|
currentThreadSetGid (below) is the only thing that ever changes it
|
||||||
|
again -- unlike currentThreadSetUid, the privilege gate for THIS
|
||||||
|
call is the calling thread's own uid (currentThreadUid() == 0), not
|
||||||
|
its gid, matching real setgid(2): a privileged (root) PROCESS may
|
||||||
|
setgid to any group; an unprivileged one may only pass its own
|
||||||
|
current gid back in (a no-op), so dropping to a group is real and
|
||||||
|
permanent for the lifetime of a non-root thread, same shape as uid's
|
||||||
|
own one-way drop. */
|
||||||
|
uint32 gid;
|
||||||
|
/* Real per-process working directory (POSIX chdir(2)/getcwd(2)): always
|
||||||
|
a fully resolved absolute path (vfs.h's vfsChdir resolves a relative
|
||||||
|
argument against the CALLING thread's own cwd before ever storing
|
||||||
|
here, so this field itself never needs re-resolving against
|
||||||
|
anything). Set once, at creation time, by threadCreate/
|
||||||
|
threadCreateUser's own initialCwd helper (sched.c) -- a thread
|
||||||
|
created while the idle thread (id 0) is "current" (kmain.c's
|
||||||
|
boot-time spawns, see parentId's own comment above) starts at "/",
|
||||||
|
exactly like a real init process; a thread created by a genuinely
|
||||||
|
running parent (any runtime sysSpawn, syscall.c's doSpawn) instead
|
||||||
|
INHERITS a byte-for-byte COPY of that parent's cwd, matching real
|
||||||
|
fork()'s own copy-not-share semantics -- the two are independent
|
||||||
|
buffers from that point on, so a child's later chdir() can never
|
||||||
|
reach back and change its parent's, and vice versa. Before this
|
||||||
|
field existed, the only cwd concept anywhere in this kernel was a
|
||||||
|
single static local in shell.c shared by nothing else -- a spawned
|
||||||
|
user process calling open()/mkdir()/etc with a relative path had
|
||||||
|
never actually worked, since vfs.c's lookupNode/sfsLookup always
|
||||||
|
resolved relative to the filesystem root, never to any notion of
|
||||||
|
"current directory" (see vfs.c's vfsResolvePath, the only thing that
|
||||||
|
reads this field). */
|
||||||
|
char cwd[fsPathMax];
|
||||||
|
/* Real per-process environment variables (POSIX getenv(3)/setenv(3)):
|
||||||
|
up to envMaxVars "NAME=VALUE\0" strings, each at most envEntryMax-1
|
||||||
|
bytes, plus envCount tracking how many slots are actually in use.
|
||||||
|
Same creation-time default/copy split as Thread.cwd immediately
|
||||||
|
above (sched.c's new initialEnv, mirroring initialCwd's own shape):
|
||||||
|
a boot-time thread (idle is currently running) starts with an EMPTY
|
||||||
|
table, like a real init process before anything ever calls
|
||||||
|
setenv(); a genuine runtime child (syscall.c's doSpawn) instead
|
||||||
|
inherits a byte-for-byte COPY of its parent's table, so a child's
|
||||||
|
later setenv() can never reach back and change its parent's, and
|
||||||
|
vice versa -- the exact same independent-buffer guarantee cwd
|
||||||
|
already gives. */
|
||||||
|
char env[envMaxVars][envEntryMax];
|
||||||
|
int envCount;
|
||||||
ThreadState state;
|
ThreadState state;
|
||||||
const char* name;
|
/* Set once, permanently, the first time schedule() ever actually
|
||||||
|
switches execution to this thread. Distinguishes "freshly created,
|
||||||
|
never scheduled even once" (state threadRunnable, everRan false) from
|
||||||
|
"was running and got preempted, still Runnable" (everRan true) -- the
|
||||||
|
two look identical by state alone, but only the former is safe for
|
||||||
|
threadDiscard to tear down (see threadDiscard). */
|
||||||
|
bool everRan;
|
||||||
|
/* Fixed buffer, not a pointer: threadCreateUser/doSpawn (syscall.c) may
|
||||||
|
be called with a name that lives in a caller's stack-local buffer
|
||||||
|
(a path just copied in from userspace), which does not outlive the
|
||||||
|
call. thread->name must remain valid for the thread's entire life
|
||||||
|
(shell.c's `ps` listing and the exit log both read it long after
|
||||||
|
creation), so the bytes are always copied in, never aliased -- see
|
||||||
|
threadSetName/copyThreadName in sched.c. */
|
||||||
|
char name[threadNameMax];
|
||||||
|
const void* waitChannel;
|
||||||
|
/* Base virtual address of this thread's elfSlotSize-sized user-memory
|
||||||
|
slot (image + stack + brk-heap all live within
|
||||||
|
[userSlotBase, userSlotBase+elfSlotSize), see elf.h) -- 0 for kernel
|
||||||
|
threads (threadCreate), which own no user pages to give back.
|
||||||
|
Set from the entry point by threadCreateUser at spawn time and
|
||||||
|
updated by exec() (syscall.c doExec) when a thread's image moves to
|
||||||
|
a different slot. threadReap/threadDiscard pass this to
|
||||||
|
pagingUnmapUserRange so a dead or rolled-back process's physical
|
||||||
|
frames are actually reclaimed instead of leaking forever. */
|
||||||
|
uint64 userSlotBase;
|
||||||
|
PrioQNode schedNode;
|
||||||
FileDescriptor fds[maxOpenFiles];
|
FileDescriptor fds[maxOpenFiles];
|
||||||
} Thread;
|
} Thread;
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user