Page 2 of 2
Re:RPC message size, handling oversized messages
Posted: Sun Oct 02, 2005 2:00 am
by Cjmovie
The 32 MB limit was chosen to allow an entire screen full of video data to be sent/received in one transaction. For my OS, I can't use shared memory because it's intended to be a distributed OS, where the sender and receiver may be running on different computers without their knowledge (without the programmer needing to care or allow for it).
32mb of full-screen video data? What resolution are you at?
A 1280x1024 32-bit screen buffer is only 3-4mb.
Also, I would like say a few things on how I plan to (finish) my messaging system:
For 0 to 512-bytes (hopefully, nobody will send 0 bytes, but you've got to consider....) I copy it into the message buffer along with the message header.
For 512-bytes to 256K, I simply map the page into the receiver's memory space. I also push the message header onto the message stack of the receiving process.
For 256K onward, I map it just the same, but I also pre-empt the current task and go directly to the receiver's message-handling procedure. This way I have less huge chunks of memory sitting around cross-addressed. I also don't have to push the message header, I simply pass it as an argument to the procedure.
Re:RPC message size, handling oversized messages
Posted: Sun Oct 02, 2005 3:23 am
by Brendan
Hi,
Cjmovie wrote:
The 32 MB limit was chosen to allow an entire screen full of video data to be sent/received in one transaction. For my OS, I can't use shared memory because it's intended to be a distributed OS, where the sender and receiver may be running on different computers without their knowledge (without the programmer needing to care or allow for it).
32mb of full-screen video data? What resolution are you at?
A 1280x1024 32-bit screen buffer is only 3-4mb.
1280 * 1024 with 32 bpp is actually 5 MB...
There's video cards available now that support 2048 * 1536, which at 32 bpp adds up to 12 MB. I allowed up to 16 MB in case video card manufacturers decide to support slightly higher resolutions. Then I doubled it to account for either a 32 bit "depth" for each pixel (for 3D layered LCD screens) or a second set of video data (for stereoscopic 3D - a left image and a right image).
[EDIT]
Doh! I did some checking and it seems 3DLabs have "Wildcat" video cards that already support 2048 * 2048 * 32 bpp. I'm starting to wonder how long it'll be before 32 MB is too small.
[/EDIT]
Cjmovie wrote:Also, I would like say a few things on how I plan to (finish) my messaging system:
For 0 to 512-bytes (hopefully, nobody will send 0 bytes, but you've got to consider....) I copy it into the message buffer along with the message header.
For 512-bytes to 256K, I simply map the page into the receiver's memory space. I also push the message header onto the message stack of the receiving process.
For 256K onward, I map it just the same, but I also pre-empt the current task and go directly to the receiver's message-handling procedure. This way I have less huge chunks of memory sitting around cross-addressed. I also don't have to push the message header, I simply pass it as an argument to the procedure.
If a task is in the middle of processing a large message and a device driver decides to send the task another large message, then where will the second message be stored and how will you avoid re-entrancy problems (calling the thread's "handle huge message" code directly while it's already running this code)?
For 256K onward, pre-empting the current task by doing a task switch to the receiver is a good idea, but I can't see how you'd go directly to the receiver's message-handling procedure (unless your lucky enough for the receiver to be waiting for a message anyway).
Cheers,
Brendan
Re:RPC message size, handling oversized messages
Posted: Wed Oct 05, 2005 1:43 pm
by mystran
I'm not reading the forum that frequently anymore, since I have other things that eat too much time for me to do any serious OS development myself, but.. I'll talk anyway, because I like talking.
As far as I can see, you have only a few options:
- negotiate message size beforehand (not stateless)
- automatically alloc sufficient place (not easy)
- truncate too large messages, with proper status flag
- simply fail too large transfers
- buffer (too large?) message with a trusted agent (need not be kernel, but no single copy)
- use pipe semantics, blocking the sender until enough has been read
- kludge specific protocols directly
You could also make combinations of the above to find something interesting, but that's about it.
Personally, I think it's enough to provide reasonable finite maximum message size (4k should be fine) and combine that with a safe shared memory system (other process should not be able to cause page faults or unavoidable deadlocks). If you need to transfer lots of data, a shared memory block will be zero copy locally after setup (ignoring caches on SMP ofcourse, and possible COW requirements to avoid page faults from misbehaviour), and with efficient message passing for coordination it's not that much harder to use such a system. Shared memory alone is a pain, but not so if it's just a bulk channel for otherwise message-passing system.
As for remote message passing of large messages, you can just as well send them within some stream protocol like TCP/IP, and this gets the trusted agent for free (assuming you trust your network stack). For local streaming a circular buffer in shared memory works just as well. If you want to abstract local/network from programmers, you can use distributed shared memory, which can then automatically deal with synchronizing only those blocks that have been changed (even with compressed diffs, or whatever).
And the mentioned problem with sending whole framebuffer is just stupid. It's not going to be any faster than streaming the data, you should only ever send the changed parts, and the transaction semantics required for this is more or less "please display the data you have now" within the updated regions stream. Even for more complicated transaction semantics, you can still stream the data, and then use short messages to coordinate the actual commit. You probably won't have any transaction semantics for large messages on the network-level (or bus-level or whatever) anyway, and the basic problems are exactly the same, whether data is part of the message transfer, or a separate stream.
Basicly, any protocol is going to be fine from the functionality side, as long as only those parties that care about the success of the operation need to trust the other party. Naturally it makes no sense to care about the operation, but not trust the other party. For untrusted trade, we have to use a trusted middle-man, which avoids the problem; the middle man need not trust anyone, but both parties need to trust the middle man.
A file server generally doesn't care whether it's client can read or write a file, as long as the client has permission to do so. It just promises a best effort for properly functioning clients. It's like a bank not trusting you just because you want to store something there. Therefore the file server should not need to trust the client. The client on the other hand needs to trust the server anyway for the data, so as long as it has a chance to give up (it's like moving on with other things after the banker sneaked away with your money), by timeout and user cancellation(!!), there is no problem. Any protocol that avoids harming the server because of client misbehaviour is fine.
The user cancellation of operations is actually why I personally dumped any synchronous message passing. It's just ugly if you have to wait the server to reply even if user hit the "cancel" button. Applications that work this way seem totally user hostile. Avoiding this without asynchronous communications, or threads emulating asynchronous communication, makes the system much uglier that any asynchronous system, and more or less requires asynchronous core in kernel anyway.
Ofcourse the whole problem of fitting messages into buffers becomes even harder, once you allow asynchronous messaging (without sender block, ofcourse). Oh well. My solution here would be sacrifice functionality for security, and just fail (with status, ofcourse) messages until the message buffer is cleared.
There's a DoS problem here when sender has higher priority, ofcourse. For equal priority threads it doesn't happen if every sent message (success or failure) is paid from the sender's CPU time (shortening it's time share by some reasonable amount), and that time is given to the receiver to deal with the message.