To overcome the need for busy waiting the wait and signal semaphore operations are used. When a process executes the wait operation and finds that the semaphore value is not positive, it must wait. However, rather than busy waiting, the process can block itself. The block operation places a process into a waiting queue associated with the semaphore, and the state of the process is switched to the waiting state. Then, control is transferred to the CPU scheduler, which selects another process to execute.
            A process that is blocked, waiting on a semaphore S, should be restarted when some other process executes a signal operation. The process is restarted by a wakeup operation, which changes the process from the waiting state to the ready state. The process is then placed in the ready queue.(The CPU may or may not be switched from the running process to the newly ready process, depending on the CPU-scheduling algorithm.)
            Each semaphore has an integer value and a list of processes. When a process must wait on a semaphore, it is added to the list of processes. A signal operation removes one process from the list of waiting processes and awakens that process.

The wait semaphore operation can be defined as

            void wait (semaphore S){
                                    add this process to S.L;
The signal semaphore operation can now be defined as

            void signal (semaphore S){
                        if(S.value <= 0){
                        remove a process P from S.L;
The block of operation suspends the process that invokes it. The wakeup (P) operation resumes the execution of a blocked process P. These two operations are provided by the operating system as basic system calls. Although under the classical definition of semaphores with busy waiting the semaphore value is never negative, this implementation may have negative semaphore values. If the semaphore value is negative, its magnitude is the number of process waiting on that semaphore.