Object-Orientated Design for a Small OS
-
- Member
- Posts: 2566
- Joined: Sun Jan 14, 2007 9:15 pm
- Libera.chat IRC: miselin
- Location: Sydney, Australia (I come from a land down under!)
- Contact:
Object-Orientated Design for a Small OS
Hi everyone,
Been a *long* time since I've been around here, I didn't realise how much I missed OS development!
And so, as well as working on Pedigree with JamesM (which I'll be rejoining later this year, not out of school yet ), I'm looking at making my own little OS for the sole purpose of writing networking code. So, it'll have a very simple device interface and I won't be looking at spending much time on a CLI or stdlib or anything. Just a tiny little system that I can build my network stack on.
However, I don't want to take someone else's code for this core system. I really like coding stuff like this myself (ie, GDT et al) . The trouble is, I am looking at the moment at a more OO design (so C++ for sure) and as such am trying to figure out the best way to pull it off.
I was thinking of having (at the least) static objects for each individual element (eg, GDT, IDT, Console, device manager), so a call to setup the GDT is as simple as GDT::Init(), or similar. However, it sort of feels like this defeats the purpose of such a design (and is super-duper unportable). I also want to try to make the design as robust as possible so I don't have to worry about the kernel itself breaking when I want to test the network stuff (ie, no packets coming in shouldn't be caused by the kernel).
Any tips or ideas or criticism would be appreciated!
Been a *long* time since I've been around here, I didn't realise how much I missed OS development!
And so, as well as working on Pedigree with JamesM (which I'll be rejoining later this year, not out of school yet ), I'm looking at making my own little OS for the sole purpose of writing networking code. So, it'll have a very simple device interface and I won't be looking at spending much time on a CLI or stdlib or anything. Just a tiny little system that I can build my network stack on.
However, I don't want to take someone else's code for this core system. I really like coding stuff like this myself (ie, GDT et al) . The trouble is, I am looking at the moment at a more OO design (so C++ for sure) and as such am trying to figure out the best way to pull it off.
I was thinking of having (at the least) static objects for each individual element (eg, GDT, IDT, Console, device manager), so a call to setup the GDT is as simple as GDT::Init(), or similar. However, it sort of feels like this defeats the purpose of such a design (and is super-duper unportable). I also want to try to make the design as robust as possible so I don't have to worry about the kernel itself breaking when I want to test the network stuff (ie, no packets coming in shouldn't be caused by the kernel).
Any tips or ideas or criticism would be appreciated!
- AndrewAPrice
- Member
- Posts: 2299
- Joined: Mon Jun 05, 2006 11:00 pm
- Location: USA (and Australia)
Re: Object-Orientated Design for a Small OS
By static objects do you mean singletons?
In it's simplest form:
Then you can access your single instance and it's members through with: MyClass:Singleton.
Some objects can be dynamically generated the first time they're created. e.g.
In it's simplest form:
Code: Select all
class MyClass
{
public:
static MyClass Singleton;
}
Some objects can be dynamically generated the first time they're created. e.g.
Code: Select all
class MyClass
{
public:
static MyClass *SingletonPtr()
{
if(!m_singleton)
m_singleton = new MyClass();
return m_singleton;
}
private:
static MyClass *m_singleton;
}
//MyClass.cpp:
MyClass *MyClass::m_singleton = 0;
My OS is Perception.
-
- Member
- Posts: 2566
- Joined: Sun Jan 14, 2007 9:15 pm
- Libera.chat IRC: miselin
- Location: Sydney, Australia (I come from a land down under!)
- Contact:
Re: Object-Orientated Design for a Small OS
Actually, I meant classes with all static members (but I'd be better off using namespaces for something like that). Singletons are probably a far better idea
- AndrewAPrice
- Member
- Posts: 2299
- Joined: Mon Jun 05, 2006 11:00 pm
- Location: USA (and Australia)
Re: Object-Orientated Design for a Small OS
Yeah, if you really wanted the GDT::init() style, then just use C-style global functions and variables inside of a namespace.
But I don't find this very elegant, since realistically that's the same as doing GDT_init in C except replace the '_' with '::'.
Also if you're going to use OO design, you could go with a few other of C++ features. Templates are cool, I recommend you make your own array/collection class, like std::map (or std::vector at a minimum). I like maps since they allow really fast lookup - the key could be anything from a process/thread ID, and if you don't know what to use as a key then you can use the address (you can delete something really fast without iterating through the array). Make your own std::string style class, really useful so you don't have to stick with predefined buffer sizes.
Deciding what should be classes/structs and if they should have member functions required a few large redesigns. In my first design, processes and threads were classes and you could call process->switchToContext() but I didn't really like the design after the novelty of using C++ wore off. Now I have "managers" which are classes (e.g. ProcessManager, MemoryManager, InterruptManager), and the processes, threads, etc are structs that hold only data, no member functions. I found it much more elegant to call MemoryManager->switchToProcessContext(myProcess).
You could inherit all of these managers from an interface (IManager), with common functions (initialise, count the memory used, cleanup) and loop through an array of them, but usually these managers have to be started/cleaned-up in a certain order. I don't find it worth the effort of doing this if I only have a few managers (I'm writing a microkernel, so I just have ProcessManager, MemoryManager, DriverManager, InterruptManager - perhaps a UserManager in the future, all the FS and window managing code is done in a server).
You could also have an object factory (kinda overkill for my kernel which I'm trying to keep as minimalistic as possible, but a possibility to keep control of a huge monolithic kernel). Basically, instead of typing "new Process()" you could do ObjectFactory::Singleton.CreateProcess() and it'll initialise it with default values, and have a ObjectFactory::Singleton.Destroy(myObject) to destroy the object. Factories can keep count of the number of objects of different types that exist and return how much memory has been used by them. They're also useful if you want to allocate your object in a special location (e.g. from a predefined pool or a specific region of memory) other than where 'new' will allocate it.
Be creative, maybe you'll invent a new OO paradigm/abstraction and get to make a cool name for it When you design your OS I recommend you find yourself a nice UML software package. UML is a modelling language (there is no code, the language IS the diagrams and stuff you draw) designed for OO software development. You can layout class diagrams so you can plan each class's members, and their relationships and stuff. You might find state diagrams useful as well.
You could inherit all of your managing classes (ProcessManager
Code: Select all
namespace GDT
{
extern void init();
}
Also if you're going to use OO design, you could go with a few other of C++ features. Templates are cool, I recommend you make your own array/collection class, like std::map (or std::vector at a minimum). I like maps since they allow really fast lookup - the key could be anything from a process/thread ID, and if you don't know what to use as a key then you can use the address (you can delete something really fast without iterating through the array). Make your own std::string style class, really useful so you don't have to stick with predefined buffer sizes.
Deciding what should be classes/structs and if they should have member functions required a few large redesigns. In my first design, processes and threads were classes and you could call process->switchToContext() but I didn't really like the design after the novelty of using C++ wore off. Now I have "managers" which are classes (e.g. ProcessManager, MemoryManager, InterruptManager), and the processes, threads, etc are structs that hold only data, no member functions. I found it much more elegant to call MemoryManager->switchToProcessContext(myProcess).
You could inherit all of these managers from an interface (IManager), with common functions (initialise, count the memory used, cleanup) and loop through an array of them, but usually these managers have to be started/cleaned-up in a certain order. I don't find it worth the effort of doing this if I only have a few managers (I'm writing a microkernel, so I just have ProcessManager, MemoryManager, DriverManager, InterruptManager - perhaps a UserManager in the future, all the FS and window managing code is done in a server).
You could also have an object factory (kinda overkill for my kernel which I'm trying to keep as minimalistic as possible, but a possibility to keep control of a huge monolithic kernel). Basically, instead of typing "new Process()" you could do ObjectFactory::Singleton.CreateProcess() and it'll initialise it with default values, and have a ObjectFactory::Singleton.Destroy(myObject) to destroy the object. Factories can keep count of the number of objects of different types that exist and return how much memory has been used by them. They're also useful if you want to allocate your object in a special location (e.g. from a predefined pool or a specific region of memory) other than where 'new' will allocate it.
Be creative, maybe you'll invent a new OO paradigm/abstraction and get to make a cool name for it When you design your OS I recommend you find yourself a nice UML software package. UML is a modelling language (there is no code, the language IS the diagrams and stuff you draw) designed for OO software development. You can layout class diagrams so you can plan each class's members, and their relationships and stuff. You might find state diagrams useful as well.
You could inherit all of your managing classes (ProcessManager
My OS is Perception.
-
- Member
- Posts: 2566
- Joined: Sun Jan 14, 2007 9:15 pm
- Libera.chat IRC: miselin
- Location: Sydney, Australia (I come from a land down under!)
- Contact:
Re: Object-Orientated Design for a Small OS
I know about UML - doing a course on object orientated programming at uni at the moment (although it's pretty much stuff I already know thanks to working with a heavily-OO-based design for my game engine).
One thing I am interested in is C#'s abstraction of objects - every single class in a C# program inherits Object, which means you can do something like:
The problem is knowing what 'bar' is, which is why they have the 'is' operator (note that in C++ that'd be Object* bar):
Terrible example, but it gets the idea across. Is it possible to do something similar in C++? It'd make the object-orientated design a bit easier to visualise.
Other than that, I think I'll start off with a normal C-style initialisation of the GDT and IDT and all that, and then build on it with classes for more advanced routines. std::string and std::maps are close to my heart , as are many other template classes.
Thanks for the tips MessiahAndrw, I'm certainly taking them into consideration.
One thing I am interested in is C#'s abstraction of objects - every single class in a C# program inherits Object, which means you can do something like:
Code: Select all
void foo(Object bar)
Code: Select all
if(bar is GDT)
bar.Init();
Other than that, I think I'll start off with a normal C-style initialisation of the GDT and IDT and all that, and then build on it with classes for more advanced routines. std::string and std::maps are close to my heart , as are many other template classes.
Thanks for the tips MessiahAndrw, I'm certainly taking them into consideration.
- AndrewAPrice
- Member
- Posts: 2299
- Joined: Mon Jun 05, 2006 11:00 pm
- Location: USA (and Australia)
Re: Object-Orientated Design for a Small OS
I've taken a liking to C# aswell. I still haven't gotten my head around all the new language features in C# 3.0 (LINQ, extension methods, etc).
If you want an "object" in C++ you can specify:
Now you can do:
You can see what type you're pointing to using Run-Time Type Info (RTTI - wiki), though you need run-time support, and I think there is a little extra overhead but I don't know the specific details of how RTTI works internally.
Another way, which doesn't require RTTI is to have an interface called IObject or Object, and everything inherit from that. Within IObject have a virtual method like "virtual types getType() = 0" where types is a HUUUUUGE enum describing EVERY possible type. You will also need something like:
then you can do:
But that function will be huge in a complex system since you need to hard code the inheritance paths for EVERY class. Or just use a 2D look up table (bitfield?) with one row representing the current pointer type, another row representing each type and if it can convert to it or not.
For a C# feel, you can do smart pointers that have a reference counter to imitate C#'s references (no need to call delete!). Though this is a little extra effort for the programmer, since to make it look as neat as c# you'd need:
Though to create an object you'd have to do:
but you'd have no need to delete the pointer!
I really dunno what of the above rambling is useful to you, I'm just throwing ideas around.
What uni do you go to? I'm in Brisbane ATM (not a native Brisbanite) getting my bachelor in game programming so I might know it, or know someone who knows it.
If you want an "object" in C++ you can specify:
Code: Select all
typedef void * object;
Code: Select all
std::vector<object> m_arrayOfObjects;
Another way, which doesn't require RTTI is to have an interface called IObject or Object, and everything inherit from that. Within IObject have a virtual method like "virtual types getType() = 0" where types is a HUUUUUGE enum describing EVERY possible type. You will also need something like:
Code: Select all
template<class T> T* canConvert(IObject *ptr); // returns a pointer if it can be casted to that type, else returns 0 if failed.
Code: Select all
if(canConvert<Process>(foo))
// DoStuff
For a C# feel, you can do smart pointers that have a reference counter to imitate C#'s references (no need to call delete!). Though this is a little extra effort for the programmer, since to make it look as neat as c# you'd need:
Code: Select all
class _myClass
{
// ...
};
typecast smartPtr<_myClass> MyClass; // smartPtr is your smart pointer class
Code: Select all
MyClass foo = MyClass(new _myClass(/*params*/));
I really dunno what of the above rambling is useful to you, I'm just throwing ideas around.
What uni do you go to? I'm in Brisbane ATM (not a native Brisbanite) getting my bachelor in game programming so I might know it, or know someone who knows it.
My OS is Perception.
-
- Member
- Posts: 2566
- Joined: Sun Jan 14, 2007 9:15 pm
- Libera.chat IRC: miselin
- Location: Sydney, Australia (I come from a land down under!)
- Contact:
Re: Object-Orientated Design for a Small OS
In Brisbane too - QUT
EDIT: To add something useful to this post...
I've just today figured out the last little problem in my NE2K driver that would stop packets being received or sent, so now I'm ready to start working on this little system to run my networking code. The object orientated design also gives a lot of options later on when developing the stack - as an example I'll show something fun I could do with a proper OO design:
Just as an example of what could be done. I'll most likely release all this code to the public domain later on as it's for my own research purposes and nothing else.
EDIT 2: Did some work, got to a point where I have printf et al (C-style output, I know, but it's based off my Console class, and it's already written - why remake it all in Console?), have a heap that works, GDT, IDT, custom IRQ handlers if needed... String is written too. Now I just need to write the network stack itself.
Thanks MessiahAndrw for providing some insight into this, your ideas have definitely helped me with my implementation of this idea.
EDIT: To add something useful to this post...
I've just today figured out the last little problem in my NE2K driver that would stop packets being received or sent, so now I'm ready to start working on this little system to run my networking code. The object orientated design also gives a lot of options later on when developing the stack - as an example I'll show something fun I could do with a proper OO design:
Code: Select all
ArpPacket* arpreq = new ArpPacket( iReqIP ); // iReqIP is the IP of the station we want the MAC for
// --- make up the request --- //
EthPacket* sendpack = Ethernet::Encapsulate( arpreq->toBuffer(), Ethernet::Broadcast ); // Encapsulate fills in the ethernet frame header
Ethernet::SendPacket( sendpack ); // Should really check for a bad pointer, but for the example it's implied...
EDIT 2: Did some work, got to a point where I have printf et al (C-style output, I know, but it's based off my Console class, and it's already written - why remake it all in Console?), have a heap that works, GDT, IDT, custom IRQ handlers if needed... String is written too. Now I just need to write the network stack itself.
Thanks MessiahAndrw for providing some insight into this, your ideas have definitely helped me with my implementation of this idea.
- codemastersnake
- Member
- Posts: 148
- Joined: Sun Nov 07, 2004 12:00 am
- Contact:
Re: Object-Orientated Design for a Small OS
you people can check out Magneto, it's written in C++ using OOP.