oo design: device drivers

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
jonathanmcdougall

oo design: device drivers

Post by jonathanmcdougall »

I have a working fat filesystem driver and a floppy driver. I need a way to connect these two.

My kernel is object oriented, so I have a class Floppy and a class Fat. Floppy allows for reading and writing sectors and Fat allows to find files and folders, provided a Device.

Should Fat be a member of Floppy? Does the device own the fs or the fs own the device? Or perhaps should I user other means to encapsulate both the device and the filesystem?

Thanks for any advices,


Jonathan
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:oo design: device drivers

Post by Brendan »

Hi,
Jonathan Mcdougall wrote: I have a working fat filesystem driver and a floppy driver. I need a way to connect these two.
I'd have a "Virtual File System" layer (or VFS) and a "File IO Interface". The floppy driver would create an entry for each floppy drive in the VFS (e.g. /dev/fd0, /dev/fd1), and the FAT file system driver would mount these. The FAT driver would talk to the VFS layer using normal file IO in order to read from the floppy driver.

If done properly this would allow you to use floppies directly ("copy /dev/fd0 image.bin"), and allow normal files to be mounted ("mount image.bin /mnt/tempfloppy -t FAT").

The VFS layer would handle all File IO. For example, if the application does "open ("/mnt/floppy/readme.txt", "r")" you'd end up with:
  • Application asks VFS to open file
    VFS checks if file is already open
    VFS asks FAT driver for directory information
    FAT driver asks VFS to read data from /dev/fd0
    VFS asks floppy driver to read data
    Floppy driver returns data to VFS
    VFS returns data to FAT driver
    FAT driver returns the directory information to VFS
    VFS checks if the file exists
    VFS builds data structure for the file handle being opened
    VFS returns status and the new file handle to the applicaton
Then (for performance reasons) I'd build caching into the VFS layer too, so that the VFS code doesn't need to get information from elsewhere as much. Caching both directory contents and file data could reduce the above into:
  • Application asks VFS to open file
    VFS checks if file is already open
    VFS checks if the file exists
    VFS builds data structure for the file handle being opened
    VFS returns status and the new file handle to the applicaton
It would also mean that the file system drivers and storage device drivers don't have to bother with any caching, and the VFS code can handle things like if write-back or write-through should be used, and adjusting the amount of memory used for all cached file data according to how much memory is present/free.

This is just how I'd do it though - everyone has their own ideas.


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
jonathanmcdougall

Re:oo design: device drivers

Post by jonathanmcdougall »

>> I have a working fat filesystem driver and a floppy driver. I
>> need a way to connect these two.

> I'd have a "Virtual File System" layer (or VFS) and a "File IO
> Interface". The floppy driver would create an entry for each
> floppy drive in the VFS (e.g. /dev/fd0, /dev/fd1), and the FAT
> file system driver would mount these. The FAT driver would
> talk to the VFS layer using normal file IO in order to read from
> the floppy driver.

What do you mean by "normal file IO"? Using the standard
library?

> If done properly this would allow you to use floppies directly
> ("copy /dev/fd0 image.bin"), and allow normal files to be
> mounted ("mount image.bin /mnt/tempfloppy -t FAT").

Ok.

> The VFS layer would handle all File IO. For example, if the
> application does "open ("/mnt/floppy/readme.txt", "r")" you'd
> end up with:
>
> Application asks VFS to open file
> VFS checks if file is already open
> VFS asks FAT driver for directory information
> FAT driver asks VFS to read data from /dev/fd0
> VFS asks floppy driver to read data
> Floppy driver returns data to VFS
> VFS returns data to FAT driver
> FAT driver returns the directory information to VFS
> VFS checks if the file exists
> VFS builds data structure for the file handle being opened
> VFS returns status and the new file handle to the applicaton

I don't really get what's a vfs. It looks like it acts both as a
filesystem (with highlevel functions such as for accessing files)
and as a device driver (such as returning sectors from a floppy).
Am I right?

(I'm thinking while I write)

If I am, could I replicate that using polymorphism: the vfs contains only high-level functions, but also gives access to a Device, which would be a base class for all devices. The Fat driver uses that Device to get its data.

So by doing "ls /dev/fd0/test", the vfs resolves "fd0" to the floppy and then calls the Fat's functions to retrieve the file, providing a reference to the Floppy, being a derived class from Device. Phew :)

What do you think?


Jonathan
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:oo design: device drivers

Post by Pype.Clicker »

what i'd suggest is that you have a reference to a I/O block driver (be it a floppy, an USB key or whatever) passed to the FAT object when it is created.

Code: Select all

interface Blocks {
    read(block_id, buffer, nb_blocks);
    write(block_id, buffer, nb_blocks);
}

class Floppy implements Blocks {
    disk_id;
    setgeometry(floppy_type) {
        ...
    }
    read(...) {
        ...
    }
    write(...) {
        ...
    }
}

class AtaHdd implements Blocks { ... }
class ScsiHdd implements Blocks { ... }

interface FileSystem {
     bool mount(Blocks device);
     DirectoryHandle locate(String path);
}

class FAT implements FileSystem {
    ...    
}
I guess that should translate quite straightforward to C++ using "pure virtual classes" instead of interfaces (or something alike).
distantvoices
Member
Member
Posts: 1600
Joined: Wed Oct 18, 2006 11:59 am
Location: Vienna/Austria
Contact:

Re:oo design: device drivers

Post by distantvoices »

Upon mounting a device/partition, I create a vfs object (actually a vnode, which has the root node of the mounted fs attached to it - and a reference to the device driver object (he - just the id of the device driver as well as a kind of minor nr which identifies the actual partition.)

Every call to fs goes throu the vfs layer whch then delegates the grunt work to the fs driver thread for the fs in question. I don't really need what the low level functions for parsing the fs look like as long as they are entered correctly in the fs_driver_t structure. *gg* I've just vnode->drv->schedule(command)
which wakes up the fs driver thread, which carries out the requested task while vfs layer sets off to do something else.

usually this looks like this:

process:read() - vfs->read() - vfs->vnode->attach(command:read) - return.
then there comes:
vfs->vnode->fetch_command() - vfs->vnode->drv->do_readwrite() - vfs->vnode->drv->reply()
then the process is woken up to fetch the reply message from its post box.
... the osdever formerly known as beyond infinity ...
BlueillusionOS iso image
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:oo design: device drivers

Post by Brendan »

Hi,
Jonathan Mcdougall wrote:What do you mean by "normal file IO"? Using the standard library?
By "normal file IO" I mean using the same file IO interface that is common to everything that uses the VFS. The file IO interface would be a standardised way of opening/closing files, reading/writing data, getting directory information, etc. It may be closely related to the file IO functions found in C libararies, but could be something completely different (as long as it's the same for all applicable things, rather than having one interface for accessing storage devices and a different interface for accessing normal files).
Jonathan Mcdougall wrote:I don't really get what's a vfs. It looks like it acts both as a
filesystem (with highlevel functions such as for accessing files)
and as a device driver (such as returning sectors from a floppy).
Am I right?
Sort of - it's an abstraction layer between clients (e.g. applications) and suppliers (e.g. file system drivers and storage device drivers). It's an abstraction used by most OS's that allow a unique file to be located from it's path, regardless of which file system (or device) it actually comes from. Some links might help:

http://www.linux-tutorial.info/modules. ... pageid=278
http://www.linux.it/~rubini/docs/vfs/vfs.html
http://www.cse.unsw.edu.au/~neilb/oss/l ... y/vfs.html
http://jakarta.apache.org/commons/sandbox/vfs/
Jonathan Mcdougall wrote:If I am, could I replicate that using polymorphism: the vfs contains only high-level functions, but also gives access to a Device, which would be a base class for all devices. The Fat driver uses that Device to get its data.
I'm probably the wrong person to ask about OOP things - I implement everything using NASM and/or C on top of a micro-kernel, and use a seperate binary for each "object" (VFS, floppy disk controller, floppy driver, file system, etc). Most of these binaries are multi-threaded, with a thread per "instance" (for e.g. floppy driver has a thread for each floppy drive, file system has a thread for each partition, etc). Everything communicates via. messaging.


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
jonathanmcdougall

Re:oo design: device drivers

Post by jonathanmcdougall »

I'm not sure about what 'mounting' does exactly. That's what I'm seeing for the moment:

Code: Select all

// a device with its filesystem
struct Entry
{
  Device *dev;
  FS *fs;
};


class Vfs
{
private:
  // all devices
  std::vector<Entry> entries_;

public:

  // registers a device in the vfs
  void add(Device *d)
  {
    // detect() returns a polymorphic fs driver
    FS *fs = FS::detect(d);

    entries_.push_back(Entry(d, fs));
  }

  // returns the File object represented by the filename
  // fn looks like "/dev/fd0/test"
  File find(std::string fn)
  {
    // getting device (a Floppy for "fd0")
    Device *d = parse_for_device(fn);
    Entry e = find_entry(d);

    // calls for example Fat::find()
    return e.fs->find(fn);
  } 
};

int main()
{
  // somehow calls Vfs::find()
  std::ifstream ifs("/dev/fd0/test");
}
Does that make sense?


Jonathan
distantvoices
Member
Member
Posts: 1600
Joined: Wed Oct 18, 2006 11:59 am
Location: Vienna/Austria
Contact:

Re:oo design: device drivers

Post by distantvoices »

mounting is: attaching a file system to the virtual file system tree.

I do it like this:

1. check if the user has the proper access ights to do the mount command.

2. check if the device to mount is a block device (now, mounting /dev/mouse does make sense, hm ;) ;))

3. fetch the superblock of that device: check: is this a ext2fs? is this a fat12fs? is this reiserFS?

4. If found an appropriate fs and if having a driver for it at hands: insert the vnode for the fs at the desired place. init the thread for the fs with the appropriate driver functions (actually, we pass a structure fs_driver_t to it.

5. if that succeeded: fetch the root directory nodes and fill in the root directory (it is a collction of vnodes with real nodes attached to)

6. set the thread to runnable state and give it some init parms (like the device it has to talk to)

7. that's it roughly.

Unmounting the filesystem will only succeed, if there is no more reference to any of its files (ie the file system isn't busy).

Thats simply writing back inode/block data, flushing caches and dropping the whole subtree to our famous bin (free(instance)) --> I've implemented it, but it isn't yet tested, so I'm rather bragging around than talking about something I *know* to work. Take it with some grain of salt. ;)
... the osdever formerly known as beyond infinity ...
BlueillusionOS iso image
jonathanmcdougall

Re:oo design: device drivers

Post by jonathanmcdougall »

Thanks to all, I worked on something along those lines and it looks good, though I don't quite think that would make the kernel modular. I'll probably be looking for better ways later on, so I may get back to you soon.

Thanks again,

Jonathan
Post Reply