Discussions on more advanced topics such as monolithic vs micro-kernels, transactional memory models, and paging vs segmentation should go here. Use this forum to expand and improve the wiki!
en.wikipedia.org wrote:In computing, ioctl (an abbreviation of input/output control) is a system call for device-specific input/output operations and other operations which cannot be expressed by regular system calls.
What do you think about everything-but-the-kitchen-sink ioctl system call? I am now wondering whether it is a good idea to implement it when making a new OS. There are two extremities when it comes this: one ioctl-like system call to handle everything or one system call for every service. Of course there could be something between these two approaches.
I have a simple case what I am trying to solve. I am at the early stage when it comes to OS and I have just reached the level of having a console. Actually there are several virtual consoles that can be switched by using common key combinations (Alt + F1, F2...). The "problem" is that now the keyboard driver catches these special key combinations and sends a message to the "terminal service" and it switches the console. Another approach could be to have an ioctl-like call to do this. For example, Linux seems to do this task by using ioctl and VT_ACTIVATE interface. Then a user-space program would be fully responsible to do all this. This is actually another thing I am worrying about: whether to allow user-space to be responsible for such a big things.
Although this is a very simple example, this ioctl issue is a huge crossroad where I am standing at. In which direction should I go? Of course this is a decision that has to be made by myself. The most safest way could be just follow the rules (mimic Linux) and stop thinking.
The ioctl interface is a way to send a message to a file, except the interface is very generic and type-unsafe. In new systems, you do not need to implement ioctl as a system call, you can rather implement it as a function in libc that sends the real message using the real message API. There certainly is use for such a facility, because there are messages that do not fit clearly in the traditional file operations/messages (read, write, open, ...). The ioctl interface allows adding new specialized messages to objects easily, without the need for adding a special system call / library routine.
Note that ioctl can indeed be improved and made more general by encoding type information and parameter sizes into the ioctl number, which the libc routine can use for various purposes when forwarding. If the ioctl also contains information about the size of the parameter, this can also be used by the kernel to forward such messages to user-space if you have user-space filesystems.
Certainly, you do want to implement a ioctl function to help you port software, but it doesn't have to be a system call.
ioctl is a communication protocol analogue to tcp - you either provide the tcp and let application build another layer on top of it (like block storage commands, cdrom commands, etc - analogue to http, ftp); or you let each type of device talk with their own protocol directly (analogue to sending ip packets directly)
IMO the traditional ioctl lacks the enumeration features (as it pretend everything is same stuff as a descriptor), which you could query devices and protocol you like to talk with, thus prevent non-sense like querying modem state on cdrom. Something like COM interface:
Certainly, you do want to implement a ioctl function to help you port software
Since all the official posix things ioctl was meant to do is pretty much useless, any meaningful use of ioctl you will be seeing in practice is just nonportable code talking to (linux-)specific interfaces. That means it needs to be gotten rid of anyway if your design is not linux.
Having a bottom-end function that basically just drops anything into the kernel or into some other process typically ends up being a practical necessity, but applications should be provided with properly defined interfaces for safety and maintainability reasons, and not write everything in terms of a single poke function.
"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 ]
I was currently thinking to keep user-space/kernel communication as thin as possible when it comes to libraries. Relying on user-space library routines to offer a nice interface and functionality could be a good idea but I am not very into that. However, I have to study it a little bit more.
It goes without saying that some kind of libary must be available because making a system call is architecture dependent (unless doing architecture dependent programs, e.g. in assembly). What I am planning is more like a wrapper without too much hassle there. All this means that the system call interface must be elegant enough in kernel-space. Because my own design would eventually fail, I have decided to use "Unix-like" system calls. I now have exit, fork, open, close, read and write. These are OK but now I am stuck at ioctl. Do I need it or not... or have another way to do the task it does.
I experimented with other extreme - making ioctl the only syscall. File IO functions - read(), write() and similar then became just another ioctl functions for file object. Things like fork(), exit() was "ioctl" functions for process-manager object.
Essentially you get an object-oriented design where handles (equivalent of file descriptors) points to different in-kernel objects. Each type of objects having their own set of functions, which are accessed in a way similar to ioctl.
If something looks overcomplicated, most likely it is.
Antti wrote:@Velko: That is an interesting experiment. Do you have Unix-like file hierarchies? Have you ported third-party programs?
Well, my OS is not complete enough to port 3rd party software for it. Or at least - to be too serious about porting. But, yes, I have Unix-like file layout.
If something looks overcomplicated, most likely it is.
Antti wrote:Because my own design would eventually fail, I have decided to use "Unix-like" system calls. I now have exit, fork, open, close, read and write. These are OK but now I am stuck at ioctl. Do I need it or not... or have another way to do the task it does.
That seems like a bad idea. The Posix API is better provided on top of any other real kernel API that is not as ancient as Posix. The additional functions related to devices that are not in Posix can just be implemented in additional functions in your libc without any issues of compatibility. You don't need to use ioctl for everything that is not Posix. Porting compilers and libraries for C compatibility can typically consist of mapping your available functions to the C functions or Posix functions. This is how Cygwin operates in the GCC environment. They don't rewrite Windows to become Posix, rather puts a wrapper on top of Windows to create a Posix interface.
bluemoon wrote:ioctl is a communication protocol analogue to tcp - you either provide the tcp and let application build another layer on top of it (like block storage commands, cdrom commands, etc - analogue to http, ftp); or you let each type of device talk with their own protocol directly (analogue to sending ip packets directly)
IMO the traditional ioctl lacks the enumeration features (as it pretend everything is same stuff as a descriptor), which you could query devices and protocol you like to talk with, thus prevent non-sense like querying modem state on cdrom. Something like COM interface:
I would have provided the "IsCd(DriveId)" and "EjectCd(DriveId)" syscalls instead. Then I would provide functions to query block device parameters (I already have that) and raw data read/write of blocks that use DriveID as a parameter.At any rate, these syscalls have nothing to do with file-IO or directory operations.
rdos wrote:The Posix API is better provided on top of any other real kernel API that is not as ancient as Posix.
This is a very valid point and maybe I should start outlining it. However, it is quite hard to design a good kernel API at this point because I do not fully understand the whole scope of services the OS is going to need to offer. Like I said earlier, at this point it feels easier to just make system calls that map very easily to Posix function declarations. User-space libraries are just wrappers when it comes to making system calls.
rdos wrote:The Posix API is better provided on top of any other real kernel API that is not as ancient as Posix.
This is a very valid point and maybe I should start outlining it. However, it is quite hard to design a good kernel API at this point because I do not fully understand the whole scope of services the OS is going to need to offer. Like I said earlier, at this point it feels easier to just make system calls that map very easily to Posix function declarations. User-space libraries are just wrappers when it comes to making system calls.
That's true. It is an advantage to know several different APIs when designing your OS API, but even in the absence of such knowledge you could decide to provide functions that Posix doesn't have in any way you want, and provide variations on the Posix APIs that could be used to implement Posix. For instance, the usual file-API is really inefficient as it relies on potentially filtering input/output (text mode), while most sensible IO uses binary format. Thus, you can create a basic file-IO API that uses raw data, and put filtering in the user library implementing Posix compliance. Another thing could be that you might not want com-ports and TCP/IP connections to only be accesible though the file-IO API, and instead implement specific syscalls for them, and then put wrappers on these to allow using them with Posix file-IO.
POSIX file I/O (read/readv/write/writev) is all binary.
It's C standard file IO (fwrite/fread/etc) which can require filtering of data (for "text" open modes) in some cases (namely OSes which don't use LF as their EOL character). Then again, C File I/O can also require character set translation...