Skip to main content

Rubix: My Simple Kernel written in C for arm

Sidebar

During the second year of university, I created a kernel for the ARMv7 instruction set. I went above and beyond what was required on this project, achieving a clean design and features such as a blocked process queue, piping, kill, and a simple filesystem. This was my favourite coursework so far. I found it very interesting to learn about and implement the things that we take for granted as programmers. I tried to stick to POSIX as much as possible, and stuck to the Linux method of having everything as either a file or process. Because pipes and standard in/out were both “files”, I was able to implement both popen and piping of the output of a process to another process.

During the second year of university, I created a kernel for the ARMv7 instruction set. I went above and beyond what was required on this project, achieving a clean design and features such as a blocked process queue, piping, kill, and a simple filesystem. This was my favourite coursework so far. I found it very interesting to learn about and implement the things that we take for granted as programmers.

I tried to stick to POSIX as much as possible, and stuck to the Linux method of having everything as either a file or process. Because pipes and standard in/out were both “files”, I was able to implement both popen and piping of the output of a process to another process.

Features #

System Calls and Helper Functions #

All system calls conform to the equivalent POSIX standard, except for close which will close the appropriate resource (no need for fclose or pclose) and set_nonblocking which is custom.

  • yield - ends the current time slice.
  • write - writes to an open file descriptor.
  • read - reads from an file descriptor. Returns length of read, 0 on EOF. May blocking - see set_nonblocking.
  • close - closes a file descriptor.
  • dup2 - duplicates fd from old to new. new is closed if it already exists.
  • pipe - creates a pipe. fd[0] is read, fd[1] is write.
  • fopen - open file. Not quite POSIX, as it’s non-blocking
  • fork - clones process. Return value is 0 if child, PID of child if parent, -1 if error.
  • exec - replaces the process with another program. PID is kept. Stack and FDs (except in/out/err) are destroyed.
  • exit - exits with exit code.
  • wait - waits for a child program to exit, and gives exit code.
  • kill - sends a kill signal to a process. Killed processes will not return an exit code. signal is not yet implemented.
  • setpriority - set priority of child process.
  • set_nonblocking - is not POSIX, unfortunately. Set pipe non-blocking.

The following functions use system calls to provide a higher function:

  • popen - opens a process and returns a FD. Uses fork, pipe, exec, and dup2.
  • wait/waitpid - both use the wait syscall.

Processes #

  • time slicing - timer based timer slices.
  • priority-based - priority(P) = priority_base(P) + slices_since_last_ran(P)
  • blocked queue - for processes waiting for a process or file resource.
  • process ownership - processes have a parent, which can kill/wait them.
  • process groups - a limited type of process group, where all processes that share a parent and the parent itself are in a group.

Files #

The kernel allows the use of file descriptors to refer to resources. They are implemented under the hood using function pointers, which means that the main part of the kernel doesn’t even know what type of file they are. Can be blocking or not.

Types:

  • pipe - Pointed to by a FD.
  • in/out/err - these are “files” too!
  • filesystem - Files are limited to 256 bytes with a maximum of 10 files.

Comments

Leave comment

Shown publicly next to your comment. Leave blank to show as "Anonymous".
Optional, to notify you if rubenwardy replies. Not shown publicly.
Max 1800 characters. You may use plain text, HTML, or Markdown.