Operating Systems 2014F Lecture 6
Library - code you did not write but it is sitting in your address space. The routines in there can be called, but to a first look, it is opaque. I am going to call it to do something, and it should do whatever I ask. On older systems, that's what the operating system does, they are just routines, you might call them directly, or you might call a dispatcher. Let's say you don't like what this operating system does, you just write your own routine to do it.
What happens if the operating system changes? Your code becomes tied to the exact version of the operating system.
When you call a library you call an API. If accessing private functions / data structures, you can do it, it will work, but when the api changes your code will break.
When you used an Apple 2 or commodore 64, you wouldn't normally have a hard disk with everything you are running, you would load off of a floppy. There might be multiple programs on that disk. It had the operating system on the disk as well. It would first load the OS and then the program would load off the disk. Every bootable floppy would have a copy of the OS. Stick in the first floppy of the program, and at some point the program would request you to put in another disk.
What would you do for copy protection? You would modify the operating system on the disk. The operating system had such low level control. Take one, create one from scratch or modify one. On these systems, the OS is just a library. A library that can do I/O stuff.
If you don't like it, you want to mess with it, you can. You can overwrite the OS. It's just routines to help you. This is good when your program essentially controls the machine. Lets' say I want to run more than 1 program. Instead of code and data..
Every time you see something new, it is built upon something older. An operating system, code and data, all sitting in one address space. We'll give everyone their own address space. Everyone will have their code and data.
A process cannot see the other processes running. Address spaces, with some number of virtual cpu attached. Single threaded processes have 1 virtual cpu. Library, code that is linked into your address space. This code knows how to access the larger operating system. You want a clean api that takes care of the mess so you don't have to. Which of the libraries are part of the operating system, and which aren't?
No one talks directly to the kernel. Something in the libraries has to know something about the kernel. What abstracts the kernel? What services does this os kernel provide? Provide access to disk, memory, GPU / video hardware. All different hardware it can provide access to. at what level of abstraction does this stop? Ok the OS provides access to graphics to the screen as a simple memory frame buffer, or is it a higher level library that provides windows, widgets. Does it provide compositing? These are all libraries that are provided in some way. They may in fact delegate some tasks to a special process. Why do you do this? because the kernel gets too big. You may want to provide some high level functionality so you dump it into another process. At the end of the day it is a bit arbitrary, where do you say this functionality is in the operating system, and this is not. Modern mobile operating systems provide an embeddable web view object, that provides a standard service (windows OS, IOS, Android) you can't easily change it, you can install alternate browsers on those systems. Are we saying the web browser is part of the operating system now? It kind of is. It's big, doesn't look like the other stuff we're talking about.
What defines the operating system changes over time depending on what you are expecting of it. Why don't we optimize that old stuff, and simplify it. There's still code that assumes the old ways. The only time you break those old systems, is when you bring a new operating system out. Most of the code, is code from older operating systems. Only changed relatively few things.
Traps do not work like regular function calls because they have to change the state. CPU has to change modes. Going from user mode to supervisor mode. In Supervisor mode, the CPU allows everything to be accessed. In user mode, access is restricted according to what was set in supervisor mode. Just remember the first abstraction for creating , it was a thing embodied in hardware. People tried to run multiple programs in that address space, and realized it was unpleasant, so they built hardware. How this happens, there are a lot of details to figure out. How the CPU is switched between things. Here we are talking about how memory is switched.
How do you say which memory is going to be active? Two basic ways. More general mechanism.
1) mmap -
When you access memory that you have not specifically asked for. It sends an error to your process (segmentation fault) If you don't catch that error, your process will terminate.
The library is a file on disk. This also happens for your executable. This isn't done directly through an mmap. THis is what execve it maps, as the program is accessed. If you only use 10% of the code in your executable, only 10% of the code will be loaded. When you think of a map, think of it as a reference, if you access this byte range in memory, go to this address and load , do it as needed. Wait if I only do that on every memory access, wouldn't that be slow. Prelinking. yes it can be slow, this is one way of getting things into your address space. It happens to all of the libraries using mmap. For the most part those are loaded read-only. On modern systems, you normally do not do self modifying code. We avoid it, except in specialized situations.
You have in the data area from the top of memory:
1) the stack 2) the heap
Does it have to be done this way? nope. but it turns out these things have distinct uses. This is for the function call stack. This is when you are in function A, function a calls b, b calls c, when function d finishes, how do you resume c from where it was? That's a stack, when you enter a function you push it to the stack, when you exit a function, you pop it from the stack. Since you are already having to push return addresses on and off of the stack, what also gets pushed are arguments and local variables.