On 6/10/2021 8:32 AM, Dimiter_Popoff wrote:
> On 6/10/2021 16:55, Don Y wrote:
>> On 6/10/2021 3:45 AM, Dimiter_Popoff wrote:
>>
>> [attrs elided]
> >
> Don, this becomes way too lengthy and repeating itself.
>
> You keep on saying that a linear 64 bit address space means exposing
> everything to everybody after I explained this is not true at all.
Task A has built a structure -- a page worth of data residing
at 0x123456. It wants to pass this to TaskB so that TaskB can perform
some operations on it.
Can TaskB acccess the data at 0x123456 *before* TaskA has told it
to do so?
Can TaskB access the data at 0x123456 WHILE TaskA is manipulating it?
Can TaskA alter the data at 0x123456 *after* it has "passed it along"
to TaskB -- possibly while TaskB is still using it?
> You keep on claiming this or that about how I do things without
> bothering to understand what I said - like your claim that I use the MMU
> for "protection only".
I didn't say that YOU did that. I said that to be able to ignore
the MMU after setting it up, you can ONLY use it to protect
code from alteration, data from execution, etc. The "permissions"
that it applies have to be invariant over the execution time of
ALL of the code.
So, if you DON'T use it "for protection only", then you are admitting
to having to dynamically tweek it.
*THIS* is the cost that the OS incurs -- and having a flat address
space doesn't make it any easier! If you aren't incurring that cost,
then you're not protecting something.
> NO, this is not true either. On 32 bit machines - as mine in
> production are - mapping 4G logical space into say 128M of physical
> memory goes all the way through page translation, block translation
> for regions where page translation would be impractical etc.
> You sound the way I would have sounded before I had written and
> built on for years what is now dps. The devil is in the detail :-).
>
> You pass "objects", pages etc. Well guess what, it *always* boils
> down to an *address* for the CPU. The rest is generic talk.
Yes, the question is "who manages the protocol for sharing".
Since forever, you could pass pointers around and let anyone
access anything they wanted. You could impose -- but not
ENFORCE -- schemes that ensured data was shared properly
(e.g., so YOU wouldn't be altering data that *I* was using).
[Monitors can provide some structure to that sharing but
are costly when you consider the number of things that may
potentially need to be shared. And, you can still poke
directly at the data being shared, bypassing the monitor,
if you want to (or have a bug)]
But, you had to rely on programming discipline to ensure this
worked. Just like you have to rely on discipline to ensure
code is "bugfree" (how's that worked for the industry?)
> And if you choose to have overlapping address spaces when you
> pass a pointer from one task to another the OS has to deal with this
> at a significant cost.
How does your system handle the above example? How do you "pass" the
pointer from TaskA to TaskB -- if not via the OS? Do you expose a
shared memory region that both tasks can use to exchange data
and hope they follow some rules? Always use synchronization
primitives for each data exchange? RELY on the developer to
get it right? ALWAYS?
Once you've passed the pointer, how does TaskB access that data
WITHOUT having to update the MMU? Or, has TaskB had access to
the data all along?
What happens when B wants to pass the modified data to C?
Does the MMU have to be updated (C's tables) to grant that
access? Or, like B, has C had access all along? And, has
C had to remain disciplined enough not to go mucking around
with that region of memory until A *and* B have done modifying
it?
I don't allow anyone to see anything -- until the owner of that thing
explicitly grants access. If you try to access something before it's
been made available for your access, the OS traps and aborts your
process -- you've violated the discipline and the OS is going to
enforce it! In an orderly manner that doesn't penalize other
tasks that have behaved properly.
> In a linear address space, you pass the pointer *as is* so the OS does
> not have to deal with anything except access restrictions.
> In dps, you can send a message to another task - the message being
> data the OS will copy into that tasks memory, the data being
> perfectly able to be an address of something in another task's
So, you don't use the MMU to protect TaskA's resources from TaskB
(or TaskC!) access. You expect LESS from your OS.
> memory. If a task accesses an address it is not supposed to
> the user is notified and allowed to press CR to kill that task.
What are the addresses "it's not supposed to?" Some *subset* of
the addresses that "belong" to other tasks? Perhaps I can
access a buffer that belongs to TaskB but not TaskB's code?
Or, some OTHER buffer that TaskB doesn't want me to see? Do
you explicitly have to locate ("org") each buffer so that you
can place SOME in protected portions of the address space and
others in shared areas? How do you change these distinctions
dynamically -- or, do you do a lot of data copying from
"protected" space to "shared" space?
> Then there are common data sections for groups of tasks etc.,
> it is pretty huge really.
Again, you expose things by default -- even if only a subset
of things. You create shared memory regions where there are
no protections and then rely on your application to behave and
not access data (that has been exposed for its access) until
it *should*.
Everybody does this. And everyone has bugs as a result. You
are relying on the developer to *repeatedly* implement the sharing
protocol -- instead of relying on the OS to enforce that for you.
It's like putting tons of globals in your application -- to
make data sharing easier (and, thus, more prone to bugs).
You expect less of your OS.
My tasks are free to do whatever they want in their own protection domain.
They KNOW that nothing can SEE the data they are manipulating *or*
observe HOW they are manipulating it or *influence* their manipulation
of it.
Until they want to expose that data. And, then, only to those entities
that they think SHOULD see it.
They can give (hand-off) data to another entity -- much like call-by-value
semantics -- and have the other entity know that NOTHING that the
original "donor" can do AFTER that handoff will affect the data that
has been "passed" to them.
Yet, they can still manipulate that data -- update it or reuse that
memory region -- for the next "client".
The OS enforces these guarantees. Much more than just passing along
a pointer to the data! Trying to track down the donor's alteration
of data while the recipient is concurrently accessing it (multiple
tasks, multiple cores, multiple CPUs) is a nightmare proposition.
And, making an *unnecessary* copy of it is a waste of resources
(esp if the two parties actually ARE well-behaved)
> The concept "one entire address space to all tasks" is from the 60-s
> if not earlier (I just don't know and don't care to check now) and it
> has done a good job while it was necessary, mostly on 16 bit CPUs.
> For today's processors this means just making them run with the
> handbrake on, *nothing* is gained because of that - no more security
> (please don't repeat that "expose everything" nonsense), just
> burning more CPU power, constantly having to remap addresses etc.
Remapping is done in hardware. The protection overhead is a
matter of updating page table entries. *You* gain nothing by creating
a flat address space because *you* aren't trying to compartmentalize
different tasks and subsystems. You likely protect the kernel's
code/data from direct interference from "userland" (U/S bit) but
want the costs of sharing between tasks to be low -- at the expense
of forfeiting protections between them.
*Most* of the world consists of imperfect coders. *Most* of us have
to deal with colleagues (of varying abilities) before, after and
during our tenure running code on the same CPU as our applications.
"The bug is (never!) in my code! So, it MUST be in YOURS!"
You can either stare at each other, confident in the correctness
of your own code. Or, find the bug IN THE OTHER GUY'S CODE
(you can't prove yours is correct anymore than he can; so you have to
find the bug SOMEWHERE to make your point), effectively doing his
debugging *for* him.
Why do you think desktop OS's go to such lengths to compartmentalize
applications? Aren't the coders of application A just as competent
as those who coded application B? Why would you think application
A might stomp on some resource belonging to application B? Wouldn't
that be a violation of DISCIPLINE (and outright RUDE)?
You've been isolated from this for far too long. So, don't see
what it's like to have to deal with another(s)' code impacting
the same product that *you* are working on.
Encapsulation and opacity are the best ways to ensure all interactions
to your code/data are through permitted interfaces.
"Who overwrote my location 0x123456? I know *I* didn't..."
"Who turned on power to the motor? I'm the only one who should do so!"
"Who deleted the log file?"
There's a reason we eschew globals!
I can ensure TaskB can't delete the log file -- by simply denying him
access to logfile.delete(). But, letting him use logfile.append()
as much as he wants! At the same time, allowing TaskA to delete or
logfile.rollover() as it sees fit -- because I've verified that
TaskA does this appropriately as part of its contract. And, there's
no NEED for TaskB to ever do so -- it's not B's responsibility
(so why allow him the opportunity to ERRONEOUSLY do so -- and then
have to chase down how this happened?)
If TaskB *tries* to access logfile.delete(), I can trap to make his
violation obvious: "Reason for process termination: illegal access"
And, I don't need to do this with pointers or hardware protection
of the pages in which logfile.delete() resides! I just don't let
him invoke *that* method! I *expect* my OS to provide these mechanisms
to the developer to make his job easier AND the resulting code more robust.
There is a cost to all this. But, *if* something misbehaves, it leaves
visible evidence of its DIRECT actions; you don't have to wonder WHEN
(in the past) some datum was corrupted that NOW manifests as an error
in some, possibly unrelated, manner.
Of course, you don't need any of this if you're a perfect coder.
You don't expose the internals of your OS to your tasks, do you?
Why? Don't you TRUST them to observe proper discipline in their
interactions with it? You trust them to observe those same
disciplines when interacting with each other... Why can't TaskA
see the preserved state for TaskB? Don't you TRUST it to
only modify it if it truly knows what it's doing? Not the result
of resolving some errant pointer?
Welcome to the 70's!