Chapter 40 — Shell Job References with %1

Chapter 40 — Shell Job References with %1
This entry is part 37 of 38 in the series Writing A Linux Style Operating System From Scratch

Post Stastics

  • This post has 985 words.
  • Estimated read time is 4.69 minute(s).

Before reading the shell examples in this chapter, be explicit about the prompt transition:

toyix> run shell
ush>

toyix> is the kernel monitor.

ush> is the user shell.

So when this chapter shows commands like:

ush> runbg counter victim
ush> jobs
ush> kill %1
ush> wait %1

those commands must be typed inside the user shell after toyix> run shell.

Chapter 39 made /programs/<name> usable as a launch target. That fixed the namespace bridge between the filesystem and the embedded program registry, but one brittle test problem remained:

kill 5
wait 5

Those commands only work if every earlier step in the boot script creates exactly the same number of processes.

That is too fragile for a chapter series that keeps growing.

Chapter 40 fixes that by giving the shell its own job references:

%1
%2
%3

The shell still launches the same background children.

The kernel still tracks the same PIDs.

But the shell now gives each background job a local handle that can be used in jobs, kill, and wait.


1. What this chapter adds

Modify:

user/shell.c
kernel/program.c
tests/smoke.py
README.md
CHANGELOG.md
index.md
docs/roadmap.md

No syscall changes are needed.

No kernel scheduler changes are needed.

No VFS changes are needed.

This is a shell-facing workflow improvement built on top of the background-job table that already exists.


2. Why this chapter exists

Toyix already had two related but separate ideas:

kernel PID
shell background job

A PID is a kernel identity.

A shell job reference is a user-facing convenience.

Before this chapter, the shell exposed only the kernel PID:

shell: runbg counter pid=5

That meant the test script had to remember 5 and assume nothing else changed before that step.

Chapter 40 makes the shell record a local job number as well:

shell: runbg counter pid=5 job=%1

That lets the shell refer to the same job in a stable way even if process IDs shift later.


3. Supported job references

This chapter supports both of these forms:

5
%1

5 means “use PID 5 directly.”

%1 means “look up shell job 1 and use the PID associated with it.”

Plain PIDs are still supported for compatibility.

The new %N syntax is a shell-local alias, not a kernel feature.

That distinction matters:

kernel PID  -> actual process identity
shell job %N -> stable shell handle for a background child

4. Shell job table changes

In user/shell.c, each job entry now stores a shell job ID in addition to the PID:

typedef struct shell_job {
    toyix_u32 job_id;
    toyix_u32 pid;
    int active;
    char name[SHELL_JOB_NAME_MAX];
} shell_job_t;

The shell keeps a small counter for new job IDs:

static toyix_u32 shell_next_job_id = 1;

That means the first background job in a shell session becomes %1, the next becomes %2, and so on.

The counter is shell-local. It resets when the shell starts.

That is exactly what we want.


5. Parsing %N

The shell needs one small helper to resolve either form:

kill 5
kill %1
wait 5
wait %1

The helper checks whether the argument begins with %.

If it does, the shell looks up the matching job ID in its table and uses that job’s PID.

If it does not, the shell parses the value as a PID directly.

That gives us a narrow and predictable rule:

%N  -> job lookup
N   -> PID lookup

The kernel never sees %1.

Only the shell does.


6. Updated shell commands

runbg now prints the shell job reference when a launch succeeds:

shell: runbg counter pid=5 job=%1

jobs now shows the job reference for each active entry:

shell jobs:
  %1 pid=5 parent=<shell pid> name=counter state=running

kill accepts either a PID or a job reference:

ush> kill %1
shell: kill requested job=%1 pid=5

wait does the same:

ush> wait %1
shell: wait job=%1 pid=5 name=counter code=128

Plain PID form still works too:

ush> kill 5
ush> wait 5

That compatibility keeps the shell usable for direct debugging while the new job references make scripted tests more robust.


7. Test flow update

The Chapter 40 test sequence in kernel/program.c now uses %1 for the background job:

runbg counter victim
jobs
kill %1
jobs
wait %1
jobs

That change removes the dependency on a hardcoded PID for the shell job-control part of the boot script.

The rest of the scripted flow stays the same.

That is deliberate.

The goal is to test the new shell job reference feature without destabilizing unrelated chapter checks.


8. What the user should see

Inside the user shell:

ush> runbg counter victim
shell: runbg counter pid=5 job=%1

ush> jobs
shell jobs:
  %1 pid=5 parent=<shell pid> name=counter state=running

ush> kill %1
shell: kill requested job=%1 pid=5

ush> wait %1
shell: wait job=%1 pid=5 name=counter code=128

The local shell job number is the stable reference.

The PID remains visible for debugging.

Both are useful, and both are now shown.


9. What this is not

This chapter does not add:

process groups
job control signals
foreground/background terminal control
shell pipelines
shell command history

It is only the first shell-local job reference layer.

That keeps the scope small and makes the chapter easy to verify.


10. Files changed in this chapter

The final file set for this chapter should be:

user/shell.c
kernel/program.c
tests/smoke.py
README.md
CHANGELOG.md
index.md
docs/roadmap.md
articles/chapter_40.md

That is a good sign that the chapter stays focused on shell-facing behavior rather than sprawling into unrelated subsystems.


Next Chapter

Now that the shell has stable job references, the next natural step is to keep tightening the program-launch path around the embedded registry and filesystem namespace.

That means Chapter 41 can move on to dynamic /programs behavior driven by the registry instead of static launch tables.


Resources

Closure

Chapter 40 gives Toyix shell-local %N job references so background work can be managed by stable shell handles instead of brittle hardcoded PIDs, while keeping the underlying kernel process model unchanged.

Happy Coding!

Writing A Linux Style Operating System From Scratch

Chapter 39 — Path-Based Program Launching with /programs

Leave a Reply

Your email address will not be published. Required fields are marked *