#include #include #include #include #include #include #include "forkthis.h" using namespace std; int main (int argc, char **argv) { int pid = fork(); fstream hello ("/mnt/c/Users/apoe/Desktop/cs444-01-25f/forkthis/hello.txt",ios::binary|ios::in|ios::out); //"binary" means read the file exactly as it appears on the disk; do not interpret it or translate it //in any way. The number one reason you would want to translate the file is to make the newline character //compatible with your system. On Linux and Mac, the newline is ASCII 10 (or 013 or 0xA or \n). //On Windows, the newline is a two-byte sequence: ASCII 13 10 (or 015 013 or 0xC 0xA or \r\n. If you //are expecting one, and the other is present, your program might not work, so on Windows systems, turning //off binary mode will "translate" a Linux newline to a Windows newline. In Linux, binary mode is always on. // hello.seekp(0); //Jump to position 0 in the file for writing /* fork() creates a "nearly" identical process to the one currently running. It is running the same program at the same point in the program, but it is a DIFFERENT process and can do things differently. One of the differences between the original and the forked process is that they have different pids. The original is called the "parent". The forked process is called the "child." When the fork() command is called, suddenly there are now two processes both in the "middle" of the fork() command. The parent process's fork() command returns the pid of the child. The child process's fork() returns zero! */ if (pid==0) { cout << "CHILD" << endl; // sleep (1); cout << "Child seek" << endl; hello.seekp (0); cout << "Child write" << endl; hello << "Y"; hello.flush(); cout << "Child done" << endl; } else { cout << "PARENT" << endl; cout << "Parent seek" << endl; hello.seekp (0); cout << "Parent write" << endl; hello << "J"; hello.flush(); cout << "Parent done" << endl; waitpid (pid,NULL,0); //waits for the child to die. In the ideal world of linux forking, the child should always die before the parent. } //hello.close (); return 0; } /* Don't confuse processes with threads. A process is a piece of code with its own section of memory running on the machine. Processes each run their own program. A thread is an execution within a process. A process can run any number of threads. They run independently of each other but they're running the same program and sharing the same memory. */ /* Random access file vs. sequential access file A sequential access file is one where you read from the beginning, continue reading to the end and stop when you reach end of file. For writing, a sequential access file either starts from an empty file or appends to the end of an existing file and, again, keeps writing forwards until the program ends. A random access file allows you to read and write to the same file and specify the location within the file you wish to read from or write to. */ /* When a file is opened, a data structure is created by Linux (the operating system), this data structure contains certain diagnostic information, including the file pointer (where you are in the file). A pointer to this data structure is then returned to your program. When your program accesses a file, it follows the pointer to the data structure, and then uses pointers within that data structure to the find the location on the disk you're accessing. When you fork, both parent and child receive a pointer to that data structure, but the data structure itself it not duplicated. There is still only data structure. So, for example, if the parent writes to the file that advances the file pointer, but the child is using the same data structure, so the child's file pointer advances as well. Now, the C++ file buffers are a language feature of C++; the operating system doesn't even know about them. When you use << in C++, that writes information to a buffer without using the file or its data structure at all. Then, when C++ gets around to flushing the buffer, THEN the data structure is updated and the is modified or whatever. So, this can cause race conditions, too. If you fork when there's something in a C++ buffer, now you have two copies of that buffer. The easiest way is avoid (or minimize) this problem is to fork () BEFORE opening the file. That way, parent and child have their own data structure, and while there still may be other problems, at least the parent is not messing around with the child's file pointer. These race conditions are a real problem in parallel programming. Do you get a different when things happen in a different order. An "atomic" piece of code, is a code that is guaranteed to run in its entirety without any parallel code also running (and messing it up). */