/* futex_demo.c Usage: futex_demo [nloops] (Default: 5) Demonstrate the use of futexes in a program where parent and child use a pair of futexes located inside a shared anonymous mapping to synchronize access to a shared resource: the terminal. The two processes each write 'num-loops' messages to the terminal and employ a synchronization protocol that ensures that they alternate in writing messages. */ #include #include #include #include #include #include #include #include #include #include "usg_common.h" #include #include /* For mode constants */ #include /* For O_* constants */ #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \ } while (0) static int *futex1, *futex2, *iaddr; static int futex(int *uaddr, int futex_op, int val, const struct timespec *timeout, int *uaddr2, int val3) { return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr, val3); } /* Acquire the futex pointed to by 'futexp': wait for its value to become 1, and then set the value to 0. */ static void fwait(int *futexp) { int s; sigset_t mask_all, pre; sigfillset(&mask_all); /* __sync_bool_compare_and_swap(ptr, oldval, newval) is a gcc built-in function. It atomically performs the equivalent of: if (*ptr == oldval) *ptr = newval; It returns true if the test yielded true and *ptr was updated. The alternative here would be to employ the equivalent atomic machine-language instructions. For further information, see the GCC Manual. */ while (1) { /* Is the futex available? */ if (__sync_bool_compare_and_swap(futexp, 1, 0)) { sigprocmask(SIG_BLOCK, &mask_all, &pre); break; /* Yes */ } /* Futex is not available; wait */ s = futex(futexp, FUTEX_WAIT, 0, NULL, NULL, 0); if (s == -1 && errno != EAGAIN) errExit("futex-FUTEX_WAIT"); } } /* Release the futex pointed to by 'futexp': if the futex currently has the value 0, set its value to 1 and the wake any futex waiters, so that if the peer is blocked in fpost(), it can proceed. */ static void fpost(int *futexp) { int s; sigset_t mask; sigemptyset(&mask); /* __sync_bool_compare_and_swap() was described in comments above */ if (__sync_bool_compare_and_swap(futexp, 0, 1)) { s = futex(futexp, FUTEX_WAKE, 1, NULL, NULL, 0); sigprocmask(SIG_SETMASK, &mask, NULL); if (s == -1) errExit("futex-FUTEX_WAKE"); } } int main(int argc, char *argv[]) { pid_t childPid; int j, nloops; int opt, fd; mode_t perms; size_t size; bool first = true; setbuf(stdout, NULL); nloops = (argc > 1) ? atoi(argv[1]) : 5; perms = S_IRUSR | S_IWUSR; size = sizeof(int) ; fd = shm_open("futex_test", O_RDWR | O_CREAT | O_EXCL, perms); if (fd == -1 && errno == EEXIST) { fd = shm_open("futex_test", O_RDWR, perms); first = false; } if(fd == -1) { errExit("shm_open"); } if (ftruncate(fd, size) == -1) errExit("ftruncate"); /* Create a shared anonymous mapping that will hold the futexes. Since the futexes are being shared between processes, we subsequently use the "shared" futex operations (i.e., not the ones suffixed "_PRIVATE") */ iaddr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (iaddr == MAP_FAILED) errExit("mmap"); futex1 = iaddr; if(first) { *futex1 = 1; } /* State: available */ fwait(futex1); printf("(%ld) 进入互斥区\n", (long) getpid()); sleep(5); fpost(futex1); exit(EXIT_SUCCESS); }