Operating System: Three Easy Pieces --- Load-Linked and Store-Conditional (Note)

Some platforms provide a pair of instructions that work in concert to help build critical sections.

On the MIPS architecture, for example, the load-linked and store-conditional instructions can be

used in tandem to build locks and other concurrent structures. The C pseudocode for these

instructions is as found in figure 28.5. Alpha, PowerPC, and ARM provide similar instructions.

int LoadLinked(int* ptr) {
       return *ptr;
}

int StoreConditional(int* ptr, int value) {
       if (no one has updated *ptr since the LoadLined to this address) {
                 *ptr = vaule;
                 return 1;
       } else {
                 return 0;   
       }
}

The load-linked operates much like a typical load instruction, and simply fetches a value from

memory and places it in a register. The key difference comes with the store-conditional, which

only succeeds and updates the value stored at the address just load-linked from if no intervening

store to the address has taken place. In the case of success, the store-conditional returns 1 and

updates the value at ptr to value; if it fails, the value at ptr is not updated and 0 is returned.

 As a challenge to yourself, try thinking  about how to build a lock using load-linked and store-

conditional. Then, when you are finished, look at the code below which provides one simple

solution. Do it! The solution is in Figure 28.6.

The lock() code is the only interesting piece. First, a thread spins waiting for the flag to be set to

0 (and this indicates the lock is not held). Once so, the thread tries to acquire the lock via the

store-conditional; if it succeeds, the thread has atomically changed the flag's value to 1 and thus

can procceed into the critical section.

              Tip: Less Code Is Better Code (Lauer's Law)

Programmers tend to brag about how much code they wrote to do something. Doing so is

fundamentally broken. What one should brag about, rather, is how little code one wrote to

accomplish a given task. Short, concise code is always preferred; it is likely easier to understand

and has fewer bugs. As Hugh Lauer said, when discussing the construction of the Pilot operating

system: "If the same people has twice as much time, they could produce as good of a system in

half of the code." We will call this Lauer's Law, and it is well worth remembering. So next time you

are bragging about how much code you wrote to finish the assignment, think again, or better yet,

go back, rewrite, and make the code as clear and concise as possible.

Note how failure of the store-conditional might arise. One thread calls lock() and executes the load-

linked, returning 0 as the lock is not held. Before it can attempt the store-conditional, it is interrupted

and another thread enters the lock code, also executing the load-linked instruction, and also getting

a 0 and continuing. At this point, two threads have each executed the load-lined and each are about

to attempt the store-conditional. The key feature of these instructions is that only one of these threads

will succeed in updating the flag to 1 and thus acquire the lock; the second thread to attempt the store-

conditional will fail because the other thread updated the value of flag between its load-linked and

store-conditional and thus have to try to acquire the lock again.

void lock(lock_t* lock) {
       while (1) {
              while (LoadLinked(&lock->flag) == 1) 
                             ;
              if (StoreConditional(&lock->flag, 1) == 1) 
                      return;
       }
}

void unlock(lock_t* lock) {
      lock->flag = 0;
}
原文地址:https://www.cnblogs.com/miaoyong/p/4999514.html