nullplan wrote:You are currently arguing against the specific way in which processes are created, but not against the concept of processes.
Not only how they are created, but also how they are managed. By allowing fork and sharing pages between many processes with possibly a long inheritance chain will bloat the page management code. By allowing this you need reference counting on every page in user space, which is a major problem. I decided I could focus that uglyness only on forked processes, and so the normal process have no reference counting for pages.
nullplan wrote:
The only critiques of that concept I have ever heard came from the unikernel crowd, who don't want to make a general purpose OS. fork() or spawn() makes little difference in the end, although spawn() is less general. I have in the past argued against fork() myself, but I think I will have to implement it since some programs use fork() not to spawn a program, nor to background themselves, but to do something else entirely. I will however go for the more general clone() concept, which allows creating a new process in the same address space. Which is its own can of worms, but managed properly it can allow spawning of child processes without setting the pages to copy-on-write.
None of that is really needed other than to support legacy code. It's perfecly possible to define a memory sharing mechanism between processes without needing to resort to fork.
nullplan wrote:
Implementation detail. Threads are defined in POSIX, and as long as the implementation complies with the spec, whether threads are a kernel creation or a userspace idea should not matter except for performance. And I don't really care about performance.
Not naming threads is not an implementation detail. I could support POSIX threads, but I rather not since Posix lacks thread names. That would mean I would have to use thread names like "Thread 1233" or something. Without names on threads, I have some ID that I have no idea how it links to code. My process info include thread names, which makes the dumps much more informative. The thread names are listed in the application debugger and kernel debugger as well.
nullplan wrote:
Device files are apparently a contentious part of UNIX, but bespoke OS-internal handles for each service aren't that different, especially when the file handles in UNIX then are only used in ioctl() calls.
I don't support ioctl, and never will. That's perhaps the ugliest legacy of UNIX.
nullplan wrote:
But file handles are easy to use, have a defined life time, can be transmitted via UNIX domain socket, can be inherited through fork(), and whether they can be created or not depends solely on file permissions which are easy to check and adjust as needed. I can easily set the owning group of all sound devices as the group "sound", and deny access to the files to anyone not in that group, and then only users in the "sound" group can access the card. Or I can allow world access to the files and everyone gets a go. I cannot do the same with OS-internal APIs.
Single user systems have no need for the access control of ancient centralized solutions. I mostly see that as legacy too. The important issues today are more like who is allowed to use your camera, microphone and listen to your keyboard and recording touch screen activity. File permissions will not solve that.
nullplan wrote:
But device files and pseudo-files were not my point. I was talking about regular files and directories, which every OS has had since CP/M, and they continue to exist, because they are a useful abstraction. And we continue to interact with them through open(), read(), write(), and close(), or close approximations of these. I have yet to see an OS allow you to insert data into the middle of a file.
Perhaps, but they forgot to name threads and syscalls.
nullplan wrote:
I disagree. Every OS I have seen so far that has not had signals has had to implement an inferior copy of signals to implement catchable exceptions e.g. on memory access violation. Inferior because there was not a general API that could be used, although the effect would have been the same. Instead a handler could be installed in some special way for just those exceptions. With no signals, no concept of signal-safety was defined in the OS, and so it was not clear what APIs the handler was allowed to call. In practise you just did as you would for a signal handler, avoiding obvious traps like printf(), but opening a crash log to write out the information.
For exception handling, I use the Windows method that is implemented as part of PE exceptables. Exception handling is important, but it is not related to signals. Signals are best defined as part of the syscall API, either for general usage or for specific device functions.
nullplan wrote:
rdos wrote:Syscalls are typically implemented through a central service (interrupt or processor specific syscall instruction) in UNIX. This is inefficient because function numbers must be coded at the caller side and decoded at the kernel side. I use call gates instead, which avoid both coding & decoding. Unfortunately, Intel and AMD optimized for OSes that used central service points.
Minutiae. I mean, look at it. You are currently arguing that whether the system call entry point is centralized or different for each one is a change in design philosophy.
That's not the only difference. In the application code, syscalls are coded as far calls through the null-selector which causes a GPF. The kernel then typically will patch the code with a call gate, but it could also decide that the application is not allowed to access the camera API, and so will patch it to a null-call that fails. IOW, this allows the OS to control which syscalls an application can use, without any penalty in performance.
Another feature of the RDOS syscall interface is that the debugger can display the name of the syscall instead of a far call with a selector. Every syscall server must define the entry-point & name of syscalls it exports.
Device drivers in kernel can also use syscalls. They work both in user space and kernel space, because they are not libraries and do not rely on decoding logic. The kernel will patch syscalls in kernel either to far calls, or if in the same driver, as push cs and a near call.
The kernel also has a similar system called "os calls" which drivers or kernel export for the benefit of other drivers. It also works with registration, and patching the code to a far call or near call. This way I don't need link a huge kernel image, rather I can load selected drivers and build an image using the pre-linked driver files.
I wouldn't say this in any way resembles the syscall interface of UNIX.