diff options
| author | user <user@clank> | 2026-07-16 11:10:45 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-07-16 11:10:45 +0200 |
| commit | 2d3597c3a02f821bb364b8ecc23fc5949fca738d (patch) | |
| tree | 714cc7463eb36306f44fcfd67fcf52de0c13b3d1 | |
| parent | shell: I/O redirection (>/<) and pipes (|); rewrite shell in C (diff) | |
| download | gbos-2d3597c3a02f821bb364b8ecc23fc5949fca738d.tar.gz gbos-2d3597c3a02f821bb364b8ecc23fc5949fca738d.tar.xz gbos-2d3597c3a02f821bb364b8ecc23fc5949fca738d.zip | |
kill + ps: implement SYS_KILL (pid 1 immortal) and a ps tool
- PCB gains PROC_PROG (running program id), set by exec, inherited on fork.
- sys_kill(pid): reject pid 1 (immortal) and unknown/dead pids; mark the target
zombie, reparent its children to init, wake its parent if blocked in wait;
schedule away if a process kills itself. Reuses FindPcbByPid/ReparentToInit.
- sys_ps(slot,buf)->{pid,state,prog}; sys_progname(id) via NameTable reverse
lookup. NameTable gains sh/ps/kill.
- libc: kill/psget/progname; tools ps (pid state cmd) and kill <pid>.
- verified: ps lists sh(B)+ps(R); kill 1 refused; kill 42 fails.
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | c/gbos.h | 3 | ||||
| -rw-r--r-- | c/kill.c | 10 | ||||
| -rw-r--r-- | c/libc.s | 20 | ||||
| -rw-r--r-- | c/ps.c | 14 | ||||
| -rw-r--r-- | include/gbos.inc | 11 | ||||
| -rw-r--r-- | src/kdata.asm | 3 | ||||
| -rw-r--r-- | src/proc.asm | 18 | ||||
| -rw-r--r-- | src/programs.asm | 16 | ||||
| -rw-r--r-- | src/syscall.asm | 168 |
10 files changed, 260 insertions, 5 deletions
@@ -20,7 +20,7 @@ $(BUILD)/%.o: src/%.asm | $(BUILD) # C programs: compiled with SDCC into ROM-bank blobs and INCBIN'd by programs.asm CBLOBS := c/chello.bin c/echo.bin c/true.bin c/false.bin c/uname.bin \ c/pid.bin c/cat.bin c/wc.bin c/head.bin c/args.bin \ - c/ls.bin c/save.bin c/rm.bin c/sh.bin + c/ls.bin c/save.bin c/rm.bin c/sh.bin c/ps.bin c/kill.bin $(BUILD)/programs.o: $(CBLOBS) # C programs also depend on the shared libc sources @@ -38,4 +38,7 @@ unsigned char wait(void); void setin(unsigned char fd); /* redirect stdin ($FF=console) */ void setout(unsigned char fd); /* redirect stdout ($FF=console) */ unsigned char lookup(const char *name); /* command name -> program id */ +unsigned char kill(unsigned char pid); /* terminate a pid (1 is immortal)*/ +unsigned char psget(unsigned char slot, unsigned char *buf); /* {pid,state,prog}*/ +void progname(unsigned char id, char *buf); /* id -> name */ #endif diff --git a/c/kill.c b/c/kill.c new file mode 100644 index 0000000..e524627 --- /dev/null +++ b/c/kill.c @@ -0,0 +1,10 @@ +#include "gbos.h" +/* kill <pid>: terminate a process (pid 1 is immortal). */ +void main(void) { + char *argv[4]; + unsigned char argc = argv_parse(argv, 4); + unsigned char pid; + if (argc < 1) { puts("usage: kill <pid>"); nl(); return; } + pid = atou(argv[0]); + if (kill(pid)) { puts("kill: no such pid (or immortal)"); nl(); } +} @@ -167,3 +167,23 @@ _lookup: ld c, #19 ; SYS_LOOKUP rst #0x30 ret + + .globl _kill, _psget, _progname + ;; unsigned char kill(unsigned char pid /*A*/) -> A (0 ok, $FF fail) +_kill: + ld b, a + ld c, #9 ; SYS_KILL + rst #0x30 + ret + ;; unsigned char psget(unsigned char slot /*A*/, unsigned char *buf /*DE*/) -> A +_psget: + ld b, a + ld c, #20 ; SYS_PS + rst #0x30 + ret + ;; void progname(unsigned char id /*A*/, char *buf /*DE*/) +_progname: + ld b, a + ld c, #21 ; SYS_PROGNAME + rst #0x30 + ret @@ -0,0 +1,14 @@ +#include "gbos.h" +/* ps: list live processes (pid, state, command). */ +void main(void) { + unsigned char i, buf[3]; + char name[9]; + for (i = 0; i < 8; i++) { + if (psget(i, buf)) { /* buf = {pid, state, prog} */ + putu(buf[0]); putc(' '); + putc("-RrBZ"[buf[1]]); putc(' '); + progname(buf[2], name); + puts(name); nl(); + } + } +} diff --git a/include/gbos.inc b/include/gbos.inc index 6aaaf73..30c3a50 100644 --- a/include/gbos.inc +++ b/include/gbos.inc @@ -69,8 +69,11 @@ DEF PROC_PARENT RB 1 DEF PROC_EXIT RB 1 ; exit code (valid when PS_ZOMBIE) DEF PROC_STDIN RB 1 ; $FF = console, else an open-file fd DEF PROC_STDOUT RB 1 ; $FF = console, else an open-file fd +DEF PROC_PROG RB 1 ; program id currently running ($FF = none), for ps DEF PROC_SIZE RB 0 +DEF KILL_CODE EQU 137 ; exit status of a killed process (128 + SIGKILL) + DEF STD_CONSOLE EQU $FF ; ----------------------------------------------------------------------------- @@ -96,7 +99,10 @@ DEF SYS_PUTC EQU 16 ; write a byte to stdout (E=byte) DEF SYS_SETIN EQU 17 ; set current stdin fd (B=fd, $FF=console) DEF SYS_SETOUT EQU 18 ; set current stdout fd (B=fd, $FF=console) DEF SYS_LOOKUP EQU 19 ; program id for a command name (DE=name -> A=id/$FF) -DEF SYS_MAX EQU 20 +DEF SYS_PS EQU 20 ; process table entry (B=slot, DE=buf3 -> A=1/0) +DEF SYS_PROGNAME EQU 21 ; program id -> name (B=id, DE=namebuf) +DEF SYS_MAX EQU 22 +; SYS_KILL (9) is now implemented (B=pid -> A=0/$FF; pid 1 is immortal) ; (SYS_OPEN=4 / SYS_CLOSE=5 are now implemented by the filesystem) ; ----------------------------------------------------------------------------- @@ -138,5 +144,8 @@ DEF PROG_ARGS EQU 12 DEF PROG_LS EQU 13 DEF PROG_SAVE EQU 14 DEF PROG_RM EQU 15 +DEF PROG_PS EQU 16 +DEF PROG_KILL EQU 17 +DEF PROG_SPIN EQU 18 ENDC diff --git a/src/kdata.asm b/src/kdata.asm index ea18756..b299021 100644 --- a/src/kdata.asm +++ b/src/kdata.asm @@ -46,3 +46,6 @@ wFsSlot:: DS 1 wFsByte:: DS 1 wFsOFPtr:: DS 2 wKChar:: DS 1 ; scratch for KPutc +wKillParent::DS 1 ; scratch for sys_kill +wPsBuf:: DS 2 ; scratch for sys_ps +wPnStart:: DS 2 ; scratch for sys_progname diff --git a/src/proc.asm b/src/proc.asm index ab1c807..cd79160 100644 --- a/src/proc.asm +++ b/src/proc.asm @@ -224,7 +224,9 @@ ProcCreate:: ld [hl+], a ; PROC_EXIT ld a, STD_CONSOLE ld [hl+], a ; PROC_STDIN = console - ld [hl], a ; PROC_STDOUT = console + ld [hl+], a ; PROC_STDOUT = console + ld a, $FF + ld [hl], a ; PROC_PROG = none ; build the initial stack frame in the task's RAM bank ld a, [wTmpEntry] @@ -453,7 +455,10 @@ sys_fork:: ld [hl+], a ; child STDIN = parent STDIN inc de ld a, [de] - ld [hl], a ; child STDOUT = parent STDOUT + ld [hl+], a ; child STDOUT = parent STDOUT + inc de + ld a, [de] + ld [hl], a ; child PROC_PROG = parent PROC_PROG ; ---- return to the parent with child pid ---- ld a, [wForkUserSP] @@ -484,6 +489,15 @@ sys_fork:: ; our demo programs keep no pre-initialized RAM. ; ============================================================================= sys_exec:: + ; record the running program id in the PCB (for ps) + ld a, [wCurProc] + add PROC_PROG + ld l, a + ld a, [wCurProc+1] + adc 0 + ld h, a + ld a, b + ld [hl], a ; HL = &ProgramTable[B] = ProgramTable + B*4 ld hl, ProgramTable ld a, b diff --git a/src/programs.asm b/src/programs.asm index 1a7092e..f21d40b 100644 --- a/src/programs.asm +++ b/src/programs.asm @@ -88,6 +88,12 @@ ProgSave: SECTION "prog_rm", ROMX[$4000], BANK[17] ProgRm: INCBIN "c/rm.bin" +SECTION "prog_ps", ROMX[$4000], BANK[18] +ProgPs: + INCBIN "c/ps.bin" +SECTION "prog_kill", ROMX[$4000], BANK[19] +ProgKill: + INCBIN "c/kill.bin" ; ----------------------------------------------------------------------------- ; PROG_SH (bank 3) - the shell, now written in C (c/sh.c): parses >/</| and @@ -162,6 +168,10 @@ ProgramTable:: dw ProgSave db LOW(BANK(ProgRm)), HIGH(BANK(ProgRm)) dw ProgRm + db LOW(BANK(ProgPs)), HIGH(BANK(ProgPs)) + dw ProgPs + db LOW(BANK(ProgKill)), HIGH(BANK(ProgKill)) + dw ProgKill ; ----------------------------------------------------------------------------- ; NameTable (ROM0) - command name -> program id, searched by sys_lookup. @@ -198,4 +208,10 @@ NameTable:: db PROG_SAVE db "rm", 0 db PROG_RM + db "sh", 0 + db PROG_SH + db "ps", 0 + db PROG_PS + db "kill", 0 + db PROG_KILL db 0 diff --git a/src/syscall.asm b/src/syscall.asm index f149a76..0b47c05 100644 --- a/src/syscall.asm +++ b/src/syscall.asm @@ -43,7 +43,7 @@ SyscallTable: dw sys_exec ; 6 EXEC dw sys_wait ; 7 WAIT dw sys_getpid ; 8 GETPID - dw sys_nosys ; 9 KILL (TODO) + dw sys_kill ; 9 KILL dw sys_nosys ; 10 BRK (TODO) dw sys_yield ; 11 YIELD dw sys_getb ; 12 GETB @@ -54,6 +54,8 @@ SyscallTable: dw sys_setin ; 17 SETIN dw sys_setout ; 18 SETOUT dw sys_lookup ; 19 LOOKUP + dw sys_ps ; 20 PS + dw sys_progname ; 21 PROGNAME ; ----------------------------------------------------------------------------- sys_nosys: @@ -388,3 +390,167 @@ sys_wait: .nochild ld a, $FF ret + +; ============================================================================= +; sys_kill(B = pid) -> A = 0 ok / $FF fail. pid 1 (init/shell) is immortal. +; Terminates the target: mark zombie, reparent its children to init, wake its +; parent if blocked in wait. If we killed ourselves, schedule away forever. +; ============================================================================= +sys_kill:: + ld a, b + cp INIT_PID + jr z, .fail ; init is immortal + call FindPcbByPid ; A=pid -> DE=&PCB, CF if not found (B preserved=pid) + jr c, .fail + ld a, [de] ; state + cp PS_FREE + jr z, .fail + cp PS_ZOMBIE + jr z, .fail ; already dead + ; mark ZOMBIE + ld a, PS_ZOMBIE + ld [de], a + ; PROC_EXIT = KILL_CODE + ld a, e + add PROC_EXIT + ld l, a + ld a, d + adc 0 + ld h, a + ld a, KILL_CODE + ld [hl], a + ; victim's parent pid + ld a, e + add PROC_PARENT + ld l, a + ld a, d + adc 0 + ld h, a + ld a, [hl] + ld [wKillParent], a + ; reparent victim's children to init (B still = victim pid) + ld a, b + ld [wExitMyPid], a + call ReparentToInit + ; wake victim's parent if it is blocked in wait() + ld a, [wKillParent] + call FindPcbByPid + jr c, .maybe_self + ld a, [de] + cp PS_BLOCKED + jr nz, .maybe_self + ld a, PS_READY + ld [de], a +.maybe_self + ; if the victim is the current process, don't return to it + ld a, [wCurProc] + add PROC_PID + ld l, a + ld a, [wCurProc+1] + adc 0 + ld h, a + ld a, [wExitMyPid] + cp [hl] + jr nz, .ok +.dead + call SchedYield + jr .dead +.ok + xor a + ret +.fail + ld a, $FF + ret + +; ============================================================================= +; sys_ps(B = slot, DE = buf[3]) -> A = 1 if used (buf = pid,state,prog), else 0 +; ============================================================================= +sys_ps:: + ld a, e + ld [wPsBuf], a + ld a, d + ld [wPsBuf+1], a + ld a, b + cp MAX_PROCS + jr nc, .no + call PcbPtr ; DE = &PCB[slot] + ld a, [de] ; state + cp PS_FREE + jr z, .no + ld a, [wPsBuf] + ld l, a + ld a, [wPsBuf+1] + ld h, a ; HL = buf + push de ; PCB + ld a, e + add PROC_PID + ld e, a + ld a, d + adc 0 + ld d, a + ld a, [de] ; pid + ld [hl+], a + pop de + push de + ld a, [de] ; state + ld [hl+], a + pop de + ld a, e + add PROC_PROG + ld e, a + ld a, d + adc 0 + ld d, a + ld a, [de] ; prog id + ld [hl], a + ld a, 1 + ret +.no + xor a + ret + +; ============================================================================= +; sys_progname(B = id, DE = namebuf) - copy the command name for a program id. +; ============================================================================= +sys_progname:: + ld hl, NameTable +.l + ld a, [hl] + or a + jr z, .none + ld a, l + ld [wPnStart], a + ld a, h + ld [wPnStart+1], a +.sk + ld a, [hl+] + or a + jr nz, .sk ; HL now at the id byte + ld a, [hl] + cp b + jr z, .match + inc hl ; skip id -> next entry + jr .l +.match + ld a, [wPnStart] + ld l, a + ld a, [wPnStart+1] + ld h, a + ld b, 8 +.cp + ld a, [hl+] + ld [de], a + inc de + or a + jr z, .done + dec b + jr nz, .cp +.done + ret +.none + ld a, $3F ; '?' + ld [de], a + inc de + xor a + ld [de], a + ret |
