Draw Process Tree F Denote P1 and P2
Figure 3.20 - Communication using sockets
Figure 3.21 and Figure 3.22
Figure 3.23 - Execution of a remote procedure call ( RPC ).
Any time there are two or more processes or threads operating concurrently, there is potential for a particularly difficult class of problems known as race conditions. The identifying characteristic of race conditions is that the performance varies depending on which process or thread executes their instructions before the other one, and this becomes a problem when the program runs correctly in some instances and incorrectly in others. Race conditions are notoriously difficult to debug, because they are unpredictable, unrepeatable, and may not exhibit themselves for years.
Here is an example involving a server and a client communicating via sockets:
1. First the server writes a greeting message to the client via the socket:
const int BUFFLENGTH = 100; char buffer[ BUFFLENGTH ]; sprintf( buffer, "Hello Client %d!", i ); write( clientSockets[ i ], buffer, strlen( buffer ) + 1 );
2. The client then reads the greeting into its own buffer. The client does not know for sure how long the message is, so it allocates a buffer bigger than it needs to be. The following will read all available characters in the socket, up to a maximum of BUFFLENGTH characters:
const int BUFFLENGTH = 100; char buffer[ BUFFLENGTH ]; read( mysocket, buffer, BUFFLENGTH ); cout << "Client received: " << buffer << "\n";
3. Now the server prepares a packet of work and writes that to the socket:
write( clientSockets[ i ], & wPacket, sizeof( wPacket ) );
4. And finally the client reads in the work packet and processes it:
read( mysocket, & wPacket, sizeof( wPacket ) );
The Problem: The problem arises if the server executes step 3 before the client has had a chance to execute step 2, which can easily happen depending on process scheduling. In this case, when the client finally gets around to executing step 2, it will read in not only the original greeting, but also the first part of the work packet. And just to make things harder to figure out, the cout << statement in step 2 will only print out the greeting message, since there is a null byte at the end of the greeting. This actually isn't even a problem at this point, but then later when the client executes step 4, it does not accurately read in the work packet because part of it has already been read into the buffer in step 2.
Solution I: The easiest solution is to have the server write the entire buffer in step 1, rather than just the part filled with the greeting, as:
write( clientSockets[ i ], buffer, BUFFLENGTH );
Unfortunately this solution has two problems: (1) It wastes bandwidth and time by writing more than is needed, and more importantly, (2) It leaves the code open to future problems if the BUFFLENGTH is not the same in the client and in the server.
Solution II: A better approach for handling variable-length strings is to first write the length of the string, followed by the string itself as a separate write. Under this solution the server code changes to:
sprintf( buffer, "Hello Client %d!", i ); int length = strlen( buffer ) + 1; write( clientSockets[ i ], &length, sizeof( int ) ); write( clientSockets[ i ], buffer, length );
and the client code changes to:
int length; if( read( mysocket, &length, sizeof( int ) ) != sizeof( int ) ) { perror( "client read error: " ); exit( -1 ); } if( length < 1 || length > BUFFLENGTH ) { cerr << "Client read invalid length = " << length << endl; exit( -1 ); } if( read( mysocket, buffer, length ) != length ) { perror( "client read error: " ); exit( -1 ); } cout << "Client received: " << buffer << "\n";
Note that the above solution also checks the return value from the read system call, to verify that the number of characters read is equal to the number expected. ( Some of those checks were actually in the original code, but were omitted from the notes for clarity. The real code also uses select( ) before reading, to verify that there are characters present to read and to delay if not. )
Note also that this problem could not be ( easily ) solved using the synchronization tools covered in chapter 6, because the problem is not really one of two processes accessing the same data at the same time.
Source: https://www.cs.uic.edu/~jbell/CourseNotes/OperatingSystems/3_Processes.html
0 Response to "Draw Process Tree F Denote P1 and P2"
Post a Comment