x86 supports 4 levels of abstraction in hardware. Linux only uses 2 levels so it can get better performance
Apps send data to one another by first sending data to the kernel, which then sends it to the other app
Fork
Fork clones a process - with a few key differences between the processes:
Two processes have different process IDs, and you use getpid() to get the child’s pid
getppid(void) gets the parent's pid
The child gets its own copy of file descriptor tables
The child has its own CPU time info
Only one process will get a pending signal
File locks (eggert advises us to use these) - these are optional. If processes cooperate, it will work fine. But a process like the shell will ignore all file locks.
Execvp
The syntax for execvp is : execvp(char* const, char* const)
The first argument is the file's name
The 2nd argument is argv (which includes the file name in the first argument)
Execvp is almost the opposite of fork() :
Destroys/reuses a process
destroying everything about that process except the key differentiators of fork() mentioned earlier
It destroys all variables, program, registers etc. You also reset all the signal handlers.
The following method is an combination of fork and execvp, and it may be easier as well as a better alternative:
What does *restrict do?
Restrict makes sure that pointers point to different parts of memory. This keeps allocated memory from interfering with allocated memory of a different variable or pointer. It is used in:
The restrict makes sure that pointers point to different parts of memory
This keeps allocated memory from interfering with allocated memory of a different variable/pointer.
This helps optimize the code but unusual bugs may arise.
ASIDE :
While it is a better alternative, people do not use posix_spawnp because they are used to fork() and execvp. These two have been around since 70's.
In comparison, posix_spawnvp has been around for the past 10 years. Additionally, it does take more effort to learn posix_spawnp.
But, this was part of posix because programmers were afraid of poor performance- say you copied 8GB and you need only a bit, posix will let it be fast. fork() is expensive whereas posix is cheap.
ORTHOGONALITY :
In math : independent 90 degree axes. Choosing a dimension(x_0, y_0, z_0), you’ll get a point. choice of an attribute doesn’t effect anything else.
We want to have a similar definition for CS is terms of processes and files running "orthogonally".
In a computer, we find processes and files. Files are slow, unreliable and nonrobust. We want performance and orthogonality for them. We can split files into two categories:
Network, mouse, keyboard :
Spontaneous data generation
Stream devices
Infinite (in principle)
vs
Flash/Disk
Random Access
Deal with by issuing a request and receive a response
Finite
UNIX's big idea: call both kinds of devices the same thing.
open ("/dev/tty", O_RDONLY) this opens terminal keyboard
open ("dev/rdsk/b",O_WRONLY) can boot a different OS with this option
What happens if you try to pass a stream device to lseek?
It will return -1 and set errno to 0. This is because lseek needs to move the fd with a certain offset, but with data coming from a stream like the keyboard, this has no limit.
Exceptions happen when primitives are set to files. This may minimize the number of primitives.
Processes communicate to files, and other processes. This is called Interprocess Communication (IPC)
This is what pipes are for :
Pipes are bouded buffers that don't worry about exhausting memory
Buffers work on a first in first out basis
Bounded pipes work because if you write to it and it is full, kernel will just make you wait.
This:
can be written in shell as: (A & B) | (C & D & E)
Downfalls
There is no way to send this message to just the process you create. This setup is not often done because of this.
If A writes "hello" and B writes "there", you might get an interleaving. In practice, small strings would not interfere in linux, but big ones will.
This is an example of race conditions behavior changes when things get scheduled.
This can be a problem in sort.c. say you want to make a temporary file:
If people are trying to read their own files, this will mess up.
Instead, create a buffer file before opening:
Now, open (name, ...) will set different names.
But this is voluntary – processes could overwrite the names if they aren’t "nice()".
You can fix this with: O_EXCL... But it will fail if the same file name is found.
Instead, you should do a while loop and put a random number in the temp file. Then
leave the loop if it is original (if open succeeds).
Between the two opens there exist a race condition because one file might might be trying to open the file for reading while the other might be opening the file for writing.
You don’t want orthogonality here because in the earlier example of posix, you only affect the child. Here, it is a correctness issue, this affects other stuff outside the child process.