Hi,
wordnice wrote:I don't know how to write "the layer under glue". I have not got implemented processes and threads (these "under glue") yet, and don't know where and with what should I start.
Start by inventing/creating some sort of data structure that contains information about a thread ("thread data structure"). This must include something to keep track of the thread's CPU state when it's not running. It might (or might not) also include things like:
- Information about the thread's priority (used by scheduler)
- Information about what the thread belongs to (e.g. if it belongs to the kernel or if it belongs to a process and which one)
- Information about what the thread is able to access (e.g. if it is allowed to use networking or file IO, or whatever).
- For multi-CPU; information about which CPUs the thread is allowed to use.
- Statistical information (how much CPU time the thread has consumed, how many messages it has sent/received, etc).
- A thread name (e.g. "GUI worker thread"), which can be used by utilities that show running threads/processes to the user (to make it much easier for users to understand than meaningless "thread #1234" numbers).
Don't get too worried about including everything you could possibly want - it should be easy enough to add new fields to your "thread data structure" later.
At the moment you already have one thread running; and you need to create a "thread data structure" for it. After inventing/designing your "thread data structure" create one for the initial/existing thread.
Then you need a way to create a new thread. This will be a function that creates a new "thread data structure"; including setting up values for the "something to keep track of the thread's CPU state when it's not running".
Next step is to implement code that does "switch immediately to thread # X". This will mostly just save the current thread's state in its "something to keep track of the thread's CPU state when it's not running" and load the new thread's "something to keep track of the thread's CPU state when it's not running". This needs to be tested to make sure it works - for example, create a second thread, and add some code so that your initial/first thread calls this function to switch to the new thread, and the new thread calls this function to switch back to the initial/first thread; so that you end up with 2 threads constantly switching to each other. Do not continue until you're extremely sure that this function definitely works correctly.
Once you know that works, you need code to decide which thread to switch to (and calls the "switch immediately to thread # X" function once it has decided which thread to switch to). There are many very different ways to do this. My advice is, if you don't know what you're doing just implement a very simple (and very crappy) "round robin" thing for now.
The next step is IPC ("Inter Process Communication"). The name is very misleading - this is how "things" (mostly threads) communicate and isn't for processes at all. There's many ways of doing IPC too. Research the alternatives and pick whichever is right for you; then implement it. You will find that sometimes threads need to wait until they receive data. This is important because it effects the scheduler. Basically, when a thread has to wait you tell the scheduler "don't give this thread CPU time until whatever it's waiting for occurs" and then when whatever it was waiting for occurs (e.g. it receives data via. IPC) you tell the scheduler "Hey, that thread is no longer waiting and can be given CPU time again now".
After your IPC works; if you implemented a very simple (and very crappy) "round robin" thing before; replace it with something that doesn't suck. At this point I'd strongly recommend doing some benchmarking - e.g. how long does a thread switch take, how quickly can you send a data via. IPC and get a response back? Most importantly; if there's 123 low priority threads running and a high priority task becomes ready to run, how long does it take for the high priority thread to get CPU time? You want to make sure the scheduler behaves the way you hoped it would under various conditions; and possibly tune/modify the scheduler to improve its behaviour under various conditions.
Next; you want to have some way to kill a thread. Actually, you want 2 ways - one for when the thread voluntarily terminates itself (e.g. "thread_exit()"); and one for when a thread has been naughty (crashed) and needs to be forcibly killed. This mostly means code to free any resources the thread was using, and (possibly) notifying other threads that the terminated thread was killed (and why).
Once all of that is done, you will be able to create new threads and terminate them, and schedule them (give them CPU time); and the threads will be able to communicate with each other and do useful work. However; you will probably only be using "kernel threads" at this stage. The next step would be implementing code to create "user-space" processes; where starting a process includes creating an initial thread for the new process. This will probably involve some sort of executable file loader.
Once you're able to start new processes, you're going to want to provide some sort of kernel API that processes can use to do things like allocate memory, create threads, communicate (via. IPC), etc. This kernel API is what your "threading library" would use.
wordnice wrote:Does it work like setjmp & longjmp?
Sort of, but not really.
Cheers,
Brendan