Page 2 of 6

Re: To POSIX or not to POSIX

Posted: Tue Apr 09, 2019 2:46 pm
by glauxosdever
Hi,


I think all discussion is good discussion. But I'll zoom in on two sentences that are almost always very true:
Korona wrote:Producing something is always better than producing nothing (regardless whether that means producing a POSIX-compatible or a POSIX-incompatible OS). I really do believe that the perfect is the enemy of the good.
Indeed, willing to do a project of a quadruple complexity and the quadruple time requirements than just a mostly unoriginal POSIX OS made me not willing to try to find some time for it (because I knew I hadn't enough time anyway). Thus, I've been producing almost nothing lately.

I think I decided it. I'll do a POSIX OS for now, just to have something to keep me going. Expect some bootloader questions during the next days. :-)


Regards,
glauxosdever

Re: To POSIX or not to POSIX

Posted: Tue Apr 09, 2019 5:36 pm
by zaval
I couldn't care less about linux, moreover - improving it, I've chosen to POSIX my (mostly non-existent yet) OS just for one reason, and it's practical - to attract possible future (or rather futuristic :D) users. to let them run those frigging console things. On ARM SBC landscape, they all are used to this environment (there is nothing except linux and its lame brother from the Corporation of Goodness), so providing them a basic set of familiar utilities and programs, would be good for the OS. And it's impossible to implement all that by a single person or even small group. So porting existing implementations comes to mind. Because of this combination, POSIX has been chosen to implement. Mostly because there are tons of prgrams openly available, written to POSSIX. But of course, it has danger of distracting interested people from targetting the main API. It's a tradeoff. Anyway, where are those "interested people"?
My OS is going to have environmental subsystems (ESSs) - API implementations, providing the environments for programs written for them. It's an NT concept. I want to create a minimalistic WinAPI-like subsystem (the main one), - I have no illusion to implement all that cosmos, just a basic set and POSIX subsystem. Binary or at least source code compatibility for the former would be so kewl, but it's infeasible. Binary compatibility is even irrelevant, because I target MIPS! :D NT begun on MIPS, but it was not today. Maybe for some silly simple WinAPI programs, it could be possible to port them over, but mostly I am left here with the need of writing my own stuff. So I have a broad field for my own API design desicions, creativity shortly speaking. for POSIX on the other hand, I'll try to keep the standard to easen porting (compiling) its programs to the OS. No X stuff though (I know it's not POSIX). just no no no. Every graphical need should be satisfied through the main WinAPI ESS. Graphical subsystem is considered an (important) extension of the base API. From this, we see, that it's possible to have hybrid programs having bindings both to WinAPI an POSIX. How is that feasible - subject for farther work. :D
Every ESS may be extended by frameworks and similar stuff the way it's defined for that API set. Every ESS consists of:
1) possible kernel module dealing with ESS specifics (like fork() thing mentioned above), that can be efficiently resolved only in kernel. Anything non-specific should be taken from the main (always present) ESS - WinAPI-like.
2) support processes, creating all the needed runtime aspects of the particular ESS.
3) dynamic libraries implementing the API in question.

Except the main ESS, every other ESS should be installed (and could be deinstalled). Any ESS except the main one can and do depend on other ESSs (at least main one, that means - we try not to duplicate things hardly). In theory, it's possible to add as many new ESSs (or extend existing ones) as possible. In theory. :) This is why I am looking at POSIX. I don't care too much of all uninspiring things in it, because I have a strong reason to still have it, that prevails over my reluctance to everything UNIX*. POSIX is the embodiment of UNIX literally.
* - except C. :)

Re: To POSIX or not to POSIX

Posted: Wed Apr 10, 2019 4:53 am
by OSwhatever
I suggest not to go POSIX for the kernel API as it would limit your own creativity, user space POSIX layer would be better. Also POSIX isn't a good fit for all operating system, for example fork support or IO handling. The reason you would want POSIX is that there is a lot of SW out there written for POSIX that you can reuse. However, more important than POSIX is full libc support and you can come quite far with that. Unfortunately, many seem to mix POSIX and standard C libraries in existing software.

Re: To POSIX or not to POSIX

Posted: Wed Apr 10, 2019 8:48 am
by nullplan
OSwhatever wrote:Unfortunately, many seem to mix POSIX and standard C libraries in existing software.
Donning my application developer's hat for a moment, of course I mix POSIX and standard C. If I'm doing network code, I have literally no other choice. Standard C is very limited in what it allows you to do. Standard C has no directories, no sockets, no links, no file locks, no pipes. I might need some of those at some point or another.

Re: To POSIX or not to POSIX

Posted: Wed Apr 17, 2019 4:11 am
by eekee
That's odd, C was from the beginning expected to provide a Unix-like API even on non-Unix systems. (I think Dennis Ritchie wrote that. It's been a while.) I guess it needed to be divided up somehow, whether between two separate standards organizations or as a single standard with optional components. (That's almost microkernel vs. modular macrokernel. :)) As it is, with C having become such a minimal standard, I don't see anything wrong with taking the nicer parts of POSIX to use with your C.

Re: To POSIX or not to POSIX

Posted: Wed Jan 15, 2020 1:49 pm
by nyc
bzt wrote: So if you don't mind, I'd like to draw the conclusion here, let's hope the OP agrees: a hobby OS should not tie itself to POSIX, instead it should experiement with new features; and if porting existing software became apparent then as a first step the hobby OS should provide a user space POSIX library which would hide the OS specific stuff. This also means that ported POSIX software will not be able to use the OS' specific features (like async calls or LWP) unless they are rewritten as native applications, but imho that's totally acceptable.
There are also features to consider removing outright, such as fork() as per https://www.microsoft.com/en-us/researc ... otos19.pdf or, perhaps, much of the tty/pty infrastructure in UNIX and POSIX beyond just devising better-working features instead of POSIX threading and asynchronous IO.

Re: To POSIX or not to POSIX

Posted: Thu Jan 16, 2020 3:50 pm
by rdos
nyc wrote:
bzt wrote: So if you don't mind, I'd like to draw the conclusion here, let's hope the OP agrees: a hobby OS should not tie itself to POSIX, instead it should experiement with new features; and if porting existing software became apparent then as a first step the hobby OS should provide a user space POSIX library which would hide the OS specific stuff. This also means that ported POSIX software will not be able to use the OS' specific features (like async calls or LWP) unless they are rewritten as native applications, but imho that's totally acceptable.
There are also features to consider removing outright, such as fork() as per https://www.microsoft.com/en-us/researc ... otos19.pdf or, perhaps, much of the tty/pty infrastructure in UNIX and POSIX beyond just devising better-working features instead of POSIX threading and asynchronous IO.
I don't find fork() that horrible, but it should be implemented late in the OS and after there is a CreateProcess-like interface. It's a fun challenge to get fork() right, and I don't think every odd feature of it must be supported.

I find the use of file handles for non-file objects a lot more horrible. I think this too go back to horrible UNIX hacks that are 50 years old and that should not be supported in modern OSes. I started out by refusing to support this hack, and instead users had to create a handle for the resource in question, and then need to use the functions defined for that resource-type only. So, when a a socket is created, a socket handle is returned, and it cannot be used it with write() or WriteFile(). I handle the C handle stuff simply by providing read/write (and a few other) methods for the resource-types that could be associated with C handles. This way I keep the object separation intact, but still support these horrible hacks. In fact, I think this way of handling C handles make fork() a lot more simple. Fork just duplicated the C handle translation table for the process which is kept in kernel. Resources opened with the native API rather than as C handles could just be ignored when forking as those would not be used in POSIX code anyway.

Re: To POSIX or not to POSIX

Posted: Thu Jan 16, 2020 4:03 pm
by mmdmine
We have a lot of POSIX OSes. It's good to support existing softwares, but a POSIX OS Project isnt a new thing. they may have different builtins but what the user see is same in Linux, BSD, Darwin, Solaris, etc. If you want to have something new, dont be POSIX.

Re: To POSIX or not to POSIX

Posted: Thu Jan 16, 2020 9:51 pm
by eekee
rdos wrote:I find the use of file handles for non-file objects a lot more horrible. I think this too go back to horrible UNIX hacks that are 50 years old and that should not be supported in modern OSes.
If you talk to Plan 9 programmers, you'll likely hear them say those hacks should never have been implemented. Or maybe they'll be okay with device nodes, but against BSD sockets. Plan 9 doesn't need any of them because all resources represented in the filesystem are represented by actual file objects, not device nodes or sockets or anything else. All the same, this leads to some conceptually odd hacks of its own, like when you open /net/tcp/clone, the fd you get refers not to clone, but to /net/tcp/n/ctl, where n is a new directory. You then have to read ctl to find the value of n so you can use the other files of your new connection. It's not complicated, just odd. (I've seen telnet implemented in a few lines of shell script.) There are C function wrappers around all these file interfaces, which is not as redundant as it sounds because the file interfaces can be mounted from elsewhere, especially with private namespaces. You can, for instance, mount another machine's /net for a quick and easy tunnel. (Perhaps best done with IL rather than TCP; IL won't compound the congestion control.) You can mount another machine's /proc for remote debugging. The cpu command, essentially ssh-like, presents many of the client's files to processes running on the server so they can use more than just stdio, they can do graphics and sound. But this is starting to sound like an advert for Plan 9.

Here's an idea: Clone Plan 9 instead of POSIX, but make the code readable, implement consistent arbitrary-length string handling throughout, (perhaps making use of the barely-used String library,) and for crying out loud, (lol) find a way to pass strings around without needing to escape the quotes.

Sample from my window system start script:

Code: Select all

w '''''label man; cd doc/man; . _funcs; exec rc -i'''''
window -r 268 0 868 400 -scroll rc -c \
	'''label -f kprint; echo kprint; cat /dev/kprint'''
window -r 0 0 128 128 colorclock_l
window is a script which writes to one of the window system's control files. w is a script which calls window.

EDIT: I forgot it's not easy enough to write fileservers for Plan 9, and the fix might be deep.

Re: To POSIX or not to POSIX

Posted: Thu Jan 16, 2020 10:47 pm
by nullplan
rdos wrote:I don't find fork() that horrible, but it should be implemented late in the OS and after there is a CreateProcess-like interface. It's a fun challenge to get fork() right, and I don't think every odd feature of it must be supported.
A while ago, someone proposed a different abstraction here: An egg. Basically, a process in creation. So, in addition to the POSIX process states of "alive" and "zombie", there is also the "egg". The idea being that you create a new OS object, that you can then change with different APIs, to finally spawn the process at the end (shouldn't that be "hatch" then?) This way, you again have the arbitrary number of steps between child process creation and the start of the next process that made the fork()/exec() model such a success, and the whole thing is extensible. If you later think of a new thing you need to pass on to a child process, you can add a new API. See, this is my major problem with CreateProcess(): You want to change anything about the child process the system call designer did not consider, you are SOL. There is no way to change the environment variables just for the child, for instance. No way to pass file descriptors other than the standard three. No way to set the user for the child. Etc.

posix_spawn() is almost that idea, but not quite yet.
rdos wrote: I find the use of file handles for non-file objects a lot more horrible. I think this too go back to horrible UNIX hacks that are 50 years old and that should not be supported in modern OSes. I started out by refusing to support this hack, and instead users had to create a handle for the resource in question, and then need to use the functions defined for that resource-type only.[...]
And what, precisely, is the benefit of that? Why do you think that file type abstraction is a hack? Well, OK, it is, but only because the abstraction is leaky (there are some things you can only do with some file descriptors). The basic idea, that read() and write() do sensible things, whether the plugged-in file is a disk file, a pipe, or a terminal, is still sound. This means, for instance, you can write a utility that expects a stream of text on standard input and writes it back out to standard output, but with line numbers in front. And you can call that utility with stdin being a disk file or a pipe or a socket, or maybe even a device file, and stdout being something completely different, and the utility doesn't have to care. In your regiment, this is impossible in general, and cumbersome in the special case that you enumerate all resource types.

CGI started out with this idea, that you just redirect the output of a script to a socket. And indeed, writing TCP services such that they read from stdin and write to stdout makes them way easier to test. Then you only need a general purpose TCP server that spawns your client handler as needed and boom, you're done. Also, I just invented inetd.

Re: To POSIX or not to POSIX

Posted: Fri Jan 17, 2020 3:35 am
by rdos
eekee wrote:If you talk to Plan 9 programmers, you'll likely hear them say those hacks should never have been implemented. Or maybe they'll be okay with device nodes, but against BSD sockets. Plan 9 doesn't need any of them because all resources represented in the filesystem are represented by actual file objects, not device nodes or sockets or anything else.
To map devices into the filesystem is even worse. Even on Windows, these hacks are present by opening strange files when you really want to access some resource that is neither stream-oriented nor is anything similar to a file. A clean interface should have a specific, relevant, interface for all resource types and should NOT assume that read/write is meaningful for everything.

Re: To POSIX or not to POSIX

Posted: Fri Jan 17, 2020 3:56 am
by rdos
nullplan wrote:
rdos wrote:I don't find fork() that horrible, but it should be implemented late in the OS and after there is a CreateProcess-like interface. It's a fun challenge to get fork() right, and I don't think every odd feature of it must be supported.
A while ago, someone proposed a different abstraction here: An egg. Basically, a process in creation. So, in addition to the POSIX process states of "alive" and "zombie", there is also the "egg". The idea being that you create a new OS object, that you can then change with different APIs, to finally spawn the process at the end (shouldn't that be "hatch" then?) This way, you again have the arbitrary number of steps between child process creation and the start of the next process that made the fork()/exec() model such a success, and the whole thing is extensible.
First, I don't create a single forked tree like POSIX OSes do. Rather, the use of fork() and exec() are inside ported applications, and typically will be done in a process that was initially created with CreateProcess(). I also use it in the command shell to spawn a process inside the same console as CreateProcess() will create a new console too. Also, when exec() is called, all the history of the fork is deleted and the resulting child is redone to a process that is indistinguishable from a process created with CreateProcess().
nullplan wrote:If you later think of a new thing you need to pass on to a child process, you can add a new API. See, this is my major problem with CreateProcess(): You want to change anything about the child process the system call designer did not consider, you are SOL. There is no way to change the environment variables just for the child, for instance. No way to pass file descriptors other than the standard three. No way to set the user for the child. Etc.
I pass command-line, startup directory and environment to the child, but if file descriptor sharing is desired, then fork() / exec() must be used.'
nullplan wrote:And what, precisely, is the benefit of that? Why do you think that file type abstraction is a hack? Well, OK, it is, but only because the abstraction is leaky (there are some things you can only do with some file descriptors). The basic idea, that read() and write() do sensible things, whether the plugged-in file is a disk file, a pipe, or a terminal, is still sound. This means, for instance, you can write a utility that expects a stream of text on standard input and writes it back out to standard output, but with line numbers in front. And you can call that utility with stdin being a disk file or a pipe or a socket, or maybe even a device file, and stdout being something completely different, and the utility doesn't have to care.
As an example, read() from a socket cannot handle timeouts on the socket, and so might hang-up if the socket is terminated without being closed properly (which seems to be the default today). write() on a socket could block because the output buffer is full, and then hangup indefinitely too. The same applies to pipes.
nullplan wrote:In your regiment, this is impossible in general, and cumbersome in the special case that you enumerate all resource types.
It still can be done, but requires explicit mapping of a resource to a C handle (provided it is sane & supported). IOW, you cannot open a resource as a C handle by opening a device in the filesystem, or using any other strange filename. You always must open it using the native API.
nullplan wrote:CGI started out with this idea, that you just redirect the output of a script to a socket. And indeed, writing TCP services such that they read from stdin and write to stdout makes them way easier to test. Then you only need a general purpose TCP server that spawns your client handler as needed and boom, you're done. Also, I just invented inetd.
Sure, and it will malfunction when something unexpected happens to the socket. In addition to that, sockets must have timeouts that are checked regularly, wasting CPU time for no reason. My native socket API has a push() method to force the data to be sent immediately without a timeout.

Re: To POSIX or not to POSIX

Posted: Fri Jan 17, 2020 7:29 am
by eekee
rdos wrote:As an example, read() from a socket cannot handle timeouts on the socket, and so might hang-up if the socket is terminated without being closed properly (which seems to be the default today). write() on a socket could block because the output buffer is full, and then hangup indefinitely too. The same applies to pipes.
Uhh... I had *some* trouble with blocking IO in Plan 9... Can't remember clearly, but I basically agree with you; checking for timeouts in Plan 9 is a nuisance. It's not Linux, there is no D state, anything waiting for IO can be killed, but that's not quite enough to make it as easy as I'd like.

((EDIT: About 10 years ago I wrote a shell-script web server for Plan 9. It's still in use, although I don't maintain it. I've emailed the guy who does to ask about improperly closed connections, timeouts, etc.))

I'm *hoping* to achieve a Plan 9-like level of adaptability with an easier-to-use, more flexible interface. It's going to be an interesting design exercise. 8)

You know, all of this is relative. Wasting CPU time to check for timeouts is nothing relative to the waste of CPU time by object-oriented programming in the 90s, when compilers couldn't inline the method calls which replaced struct accesses. And if you want to see true evil, try writing a computer emulator in LSL, a language with no mutable array or list of any kind and no references to anything, compounded by memory limited to kilobytes. It's unbelievable! I tried implementing a language first, but decided it was better to go down to machine level to more cleanly abstract away the nonsense necessary to avoid duplicating the entire memory space for every write. And here I am clearly recalling the time I imagined immutability was a good thing. m(

Re: To POSIX or not to POSIX

Posted: Fri Jan 17, 2020 9:43 am
by nullplan
eekee wrote:Uhh... I had *some* trouble with blocking IO in Plan 9... Can't remember clearly, but I basically agree with you; checking for timeouts in Plan 9 is a nuisance. It's not Linux, there is no D state, anything waiting for IO can be killed, but that's not quite enough to make it as easy as I'd like.
Yes, there is a D state in Linux, it's just that processes waiting for events don't enter it. The D state is entered when there is a transfer going on, like when a page fault determined that some more of a file has to be loaded. The process has to be set to "uninterruptible" then, because the page being loaded might contain the signal handling code. But in general, whenever a process is waiting for DMA to complete, it is in D state. I have some memories of drivers not adding timeouts to some things and processes hanging forever as a result.

A D state process cannot even be killed with SIGKILL.

Also: Easy way to add a timeout is to call "alarm(30)". Then you will get a SIGALRM in 30 seconds. What could be easier?
rdos wrote:As an example, read() from a socket cannot handle timeouts on the socket, and so might hang-up if the socket is terminated without being closed properly (which seems to be the default today).
The only way I know to end an application with an open socket such that the other side does not get a notification is by pulling the plug, literally. Otherwise, usually the other side gets an RST at least.
rdos wrote:write() on a socket could block because the output buffer is full, and then hangup indefinitely too. The same applies to pipes.
That's... that's the whole point. The UNIX parallelism model was supposed to be that multiple processes run unsynchronized, but they block on a read() of an empty pipe or a write() of a full one. Everything runs in parallel until it is time to share the data. No explicit mutexes or condition variables needed. That was how it was supposed to work.

Of course, then came the 90ies, and then everyone thought they were better than Thompson, Kernighan, and Ritchie, and went on to invent multi-threading, and now, thirty years later, everyone is wondering how their memory got corrupted if they used too few mutexes, or why their code doesn't scale if they used too many.

Re: To POSIX or not to POSIX

Posted: Fri Jan 17, 2020 11:44 am
by eekee
nullplan wrote:A D state process cannot even be killed with SIGKILL.
Yup. When I was using NFS, even though NFS was a stateless protocol, a momentary disconnection of the network connection was sufficient to put processes into the D state.
nullplan wrote:Also: Easy way to add a timeout is to call "alarm(30)". Then you will get a SIGALRM in 30 seconds. What could be easier?
Huh, nice. There was no such thing in Plan 9 when I was scripting it. :) There is now, in 9front, for scripts.
nullplan wrote:
rdos wrote:As an example, read() from a socket cannot handle timeouts on the socket, and so might hang-up if the socket is terminated without being closed properly (which seems to be the default today).
The only way I know to end an application with an open socket such that the other side does not get a notification is by pulling the plug, literally. Otherwise, usually the other side gets an RST at least.
What about suspending a laptop? This wasn't enough to close connections within the last 5 or 6 years. The local library had this problem with their AP running out of memory.
nullplan wrote:
rdos wrote:write() on a socket could block because the output buffer is full, and then hangup indefinitely too. The same applies to pipes.
That's... that's the whole point. The UNIX parallelism model was supposed to be that multiple processes run unsynchronized, but they block on a read() of an empty pipe or a write() of a full one. Everything runs in parallel until it is time to share the data. No explicit mutexes or condition variables needed. That was how it was supposed to work.
Nice summary of what's good about pipes, covers Plan 9's files too. However, it's not a perfect system. I forget the details, but there are some things which don't work out nicely. One thing I do remember: it's not very easy to serve files under Plan 9, and there are some tasks which don't fit the pipe model very well at all.
nullplan wrote:Of course, then came the 90ies, and then everyone thought they were better than Thompson, Kernighan, and Ritchie, and went on to invent multi-threading, and now, thirty years later, everyone is wondering how their memory got corrupted if they used too few mutexes, or why their code doesn't scale if they used too many.
You have a point, but be careful with that hero worship. You know it's a newbie mistake to assume the network is reliable? Plan 9 makes that mistake. :) Ken Thompson worked on Plan 9 until 2000; 30 years after the dawn of Unix, more than that since the start of his career. aan exists to work around this, but it has to be enabled manually and, if I remember right, it wasn't all finished or fixed until 9front took it on in 2012 or so.