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;
|
||||
uint32 weight;
|
||||
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;
|
||||
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];
|
||||
} Thread;
|
||||
|
||||
|
||||
Reference in new issue
Block a user