How to properly implement a synchronous system call ?

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
wichtounet
Member
Member
Posts: 90
Joined: Fri Nov 01, 2013 4:05 pm
Location: Fribourg, Switzerland
Contact:

How to properly implement a synchronous system call ?

Post by wichtounet »

Hi,

I've started implementing multitasking in my OS and the basis work well. I can have several user processes printing to the console via a system call and being rescheduled every X ms.

I'm now having a hard time figuring out what is a good way to implement a blocking system call. For instance, a keyboard driver. At the beginning I though of implementing it simply with a semaphore:

Code: Select all

keyboard_handler:
   put char in buffer
   V(sem)

get_char_blocking:
   P(sem)
   get char from buffer
At first it sounded simple, but the problem is that in the P(sem), I will put the process to sleep and schedule another one. The thread will only be waken when the interrupt is raised. The problem here is that the process will return not after P(sem), but after the call to the system call since I saved the context at the system call entry. (Hope I'm clear here :oops: )

Another thing I have thought of doing is something like that:

Code: Select all

keyboard_handler:
   if waiter in list: give him the char by modifying its context and set him READY so that it will be rescheduled as soon as possible
   else: put char in buffer

get_char_blocking:
   if char: gets it and return
   else: put me in waiting list
(of course with synchronization to protect manipulation of shared data)

It seems to me that the first solution is cleaner: use of a well known synchronization primitive and interrupt handler kept simpler. But I don't see how it is possible to implement it like that ?

So here is my question: Is the second solution a good solution ? And if not, what is the proper way to make a blocking system call ?

Thank you

P.S. I hope that for once my question is not too stupid :oops:
Thor Operating System: C++ 64 bits OS: https://github.com/wichtounet/thor-os
Good osdeving!
User avatar
bluemoon
Member
Member
Posts: 1761
Joined: Wed Dec 01, 2010 3:41 am
Location: Hong Kong

Re: How to properly implement a synchronous system call ?

Post by bluemoon »

This question should be break down to two part:
1. For keyboard event handling, it is not a good idea to do blocking call
2. how to do blocking call.


For keyboard event handling. most people would implement some sort of keyboard buffer, or event queue - depends on your design choice. Anyway, the event most likely generated from an IRQ or other abstracted device layers like usb, or even virtual on-screen keyboard, and you design an event path that propagate the signal to interested party (keyboard buffer, UI server, application, etc). Furthermore, this may not involve a scheduler for a monolithic kernel, or otherwise trigger a process switch (IPC) for micro-kernel design.


2. For blocking call, visit the wiki, there are some very simple, basic model (sleep queue), once you got the idea you can design more advanced method.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: How to properly implement a synchronous system call ?

Post by Combuster »

if waiter in list: give him the char by modifying its context and set him READY so that it will be rescheduled as soon as possible
else: put char in buffer
Overcomplicating?

Writer:
1: Put char in buffer in a way it doesn't disturb the reader - it's an IRQ after all. Circular buffers with separate reader-writer pointers work well for this.
2: Wake up relevant threads

Reader:
1: Fetch char from buffer if possible. No locking needed because there should be one reader to prevent fights over who gets the characters.
2: If so, return
3: If not, sleep, then go back to 1


Your other problem is that you should save the kernel state as well on a block. Typically you push the CPU state onto the kernel stack then switch stacks, but if you don't want that you can also tag a process for needing to do something in kernel space before returning to userland.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
wichtounet
Member
Member
Posts: 90
Joined: Fri Nov 01, 2013 4:05 pm
Location: Fribourg, Switzerland
Contact:

Re: How to properly implement a synchronous system call ?

Post by wichtounet »

Thanks guys.
bluemoon wrote:This question should be break down to two part:
1. For keyboard event handling, it is not a good idea to do blocking call
2. how to do blocking call.

For keyboard event handling. most people would implement some sort of keyboard buffer, or event queue - depends on your design choice. Anyway, the event most likely generated from an IRQ or other abstracted device layers like usb, or even virtual on-screen keyboard, and you design an event path that propagate the signal to interested party (keyboard buffer, UI server, application, etc). Furthermore, this may not involve a scheduler for a monolithic kernel, or otherwise trigger a process switch (IPC) for micro-kernel design.

2. For blocking call, visit the wiki, there are some very simple, basic model (sleep queue), once you got the idea you can design more advanced method.
1. Yes, keyboard was probably not the best example. In the future, I'll have to improve the concept of terminal/console in my kernel but for now, the pseudo shell does everything by itself.
2. I'm sorry, but I haven't found any of them. I don't see how to block it inside the call (see following explanation).
Combuster wrote:Writer:
1: Put char in buffer in a way it doesn't disturb the reader - it's an IRQ after all. Circular buffers with separate reader-writer pointers work well for this.
2: Wake up relevant threads

Reader:
1: Fetch char from buffer if possible. No locking needed because there should be one reader to prevent fights over who gets the characters.
2: If so, return
3: If not, sleep, then go back to 1
My problem is that I don't know how to sleep in a system call. When the process makes a system call, the context is saved. When there is reschedule, I modify the stack so that iret goes back to the new thread. It works well. But how do I save the context of the process inside the system call so that it is awaken inside the system call ?

Perhaps the solution is obvious or perhaps the way I've implement scheduling is making it impossible.
Thor Operating System: C++ 64 bits OS: https://github.com/wichtounet/thor-os
Good osdeving!
User avatar
bluemoon
Member
Member
Posts: 1761
Joined: Wed Dec 01, 2010 3:41 am
Location: Hong Kong

Re: How to properly implement a synchronous system call ?

Post by bluemoon »

If you want full preemptive kernel, you need one kernel stack per thread.
Otherwise, put restrictions with kernel locks and postpone the scheduling until the time you return back to user-land (to different process), and put the calling process to sleep.
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: How to properly implement a synchronous system call ?

Post by Owen »

During an interrupt or a system call save the user state on the stack

During a reschedule (System call blocks, timer interrupt), save the active kernel state on the stack (e.g. registers defined by the ABI as caller-preserve), then switch to the kernel stack of the new process and restore the same set of registers.

Disconnect context switching and privilege switches (i.e. system calls, interrupts)

One tricky area: What to do if there is no process to run?

Common solution: Have one absolutely minimal priority process per core. This task does nothing more than "hlt" (i.e. wait for interrupts). This guarantees the scheduler always has a task available to run (simplifying its' logic). You can see an example of this in the Windows "System Idle Process"
User avatar
wichtounet
Member
Member
Posts: 90
Joined: Fri Nov 01, 2013 4:05 pm
Location: Fribourg, Switzerland
Contact:

Re: How to properly implement a synchronous system call ?

Post by wichtounet »

bluemoon wrote:If you want full preemptive kernel, you need one kernel stack per thread.
Otherwise, put restrictions with kernel locks and postpone the scheduling until the time you return back to user-land (to different process), and put the calling process to sleep.
Oh yeah, what I want to do means having a fully preemptible kernel... I didn't thought about that :(

For now, I'm not planning to do a preemptible kernel, so I'll use a solution based on my second solution (see first post).
Owen wrote:During an interrupt or a system call save the user state on the stack

During a reschedule (System call blocks, timer interrupt), save the active kernel state on the stack (e.g. registers defined by the ABI as caller-preserve), then switch to the kernel stack of the new process and restore the same set of registers.

Disconnect context switching and privilege switches (i.e. system calls, interrupts)
I didn't though that I could switching the stacks. I'll use that when I go further in my implementation or if go in direction of a preemptible kernel.
Owen wrote:One tricky area: What to do if there is no process to run?

Common solution: Have one absolutely minimal priority process per core. This task does nothing more than "hlt" (i.e. wait for interrupts). This guarantees the scheduler always has a task available to run (simplifying its' logic). You can see an example of this in the Windows "System Idle Process"
For this part, at least, I'm already done. I've already implemented an idle task (although priority is not handled for now, but will be soon).

Thanks a lot for your answers. I think I'll need to try to keep it simple at first. As ever in this field, now that I understand more the problem and the solutions, it is more complicated than I thought.
Thor Operating System: C++ 64 bits OS: https://github.com/wichtounet/thor-os
Good osdeving!
Post Reply