In the kernel, the TCP code executes in three contexts: in user context when a user process makes a system call, in the context of a network interrupt handler when a TCP packet is received, and in the context of a timer interrupt handler when a TCP timer fires. To mimic this behavior a CMC process running the Linux TCP implementation contains the following three threads:
|
These threads once triggered can either execute to completion or can block. These threads block by yielding control to the kernel scheduler. In the real kernel, the scheduler would then execute the scheduling algorithm to determine the thread that is scheduled next. In our model, the scheduler is modified to immediately transfer control to CMC. This enables CMC to nondeterministically choose the thread that is executed next, and explore multiple thread schedules from a given state.
Similarly, the kernel timer routine is modified to allow CMC to choose the timer that fires next when multiple timers are enabled. Optionally, CMC can fire timers out of order irrespective of their expiration times. While this can lead to some behaviors not possible in a real implementation, it has the benefit of making the implementation behavior independent of the actual values of the timers.
The two kernels communicate through a network, modeled as a list of messages in the shared memory. The network model can lose, duplicate, reorder and corrupt messages. Each kernel communicates with the network through an appropriate network device driver.