Type Here to Get Search Results !

2 - Use and Examples of CAS Synchronization for Shared Memory:__sync_val_compare_and_swap - 공유 메모리(Shared Memory)에 대한 CAS 동기화 사용 및 예제

이 글에서는 에 대해서 설명합니다. 

  • Whether the CAS can be synchronized for shared memory
    • 공유 메모리(Shared Memory)에 대한 CAS 동기화 가능 여부
  • Test CAS synchronization for Shared Memory
    • 공유 메모리(Shared Memory)에 대한 CAS 동기화 테스트


Whether the CAS can be synchronized for shared memory공유 메모리(Shared Memory)에 대한 CAS 동기화 가능 여부


CAS(Compare And Swap)는 일반적으로 단일 프로세스 내의 스레드 간 동기화에 사용되는 동시성 제어 작업입니다. CAS를 사용하면 스레드가 공유 메모리 위치를 원자적으로 읽고 예상 값과 비교하고 위치 값을 새 값으로 조건부로 교환할 수 있습니다. 모두 단일 원자 작업에서 가능합니다. 

CAS (Compare And Swap) is a concurrency control operation that is typically used for synchronization between threads within a single process. CAS allows a thread to atomically read a shared memory location, compare it to an expected value, and conditionally swap the value of the location with a new value, all in a single, atomic operation


Test CAS synchronization for Shared Memory
공유 메모리(Shared Memory)에 대한 CAS 동기화 테스트


다음은 공유 메모리를 사용하는 프로세스 간에 비교 및 ​​교환을 사용하는 예입니다.

here is an example of using compare and swap between processes using shared memory:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/mman.h>

#define SHARED_MEM_SIZE 4096 // Size of shared memory region
#define COUNTER_OFFSET 0     // Offset of counter in shared memory

typedef struct shared_mem {
    int counter;
    // Other shared variables can be added here
} shared_mem;

int main() {
    int i = 0;

    // Create shared memory region
    shared_mem *shm = mmap(NULL, SHARED_MEM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (shm == MAP_FAILED) {
        perror("mmap");
        exit(1);
    }
    shm->counter = 0; // Initialize counter to 0

    // Fork a child process
    pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
        exit(1);
    }

    // Child process
    if (pid == 0) {
        // Increment counter by 1
        int expected = 0;
        int new_value = 1;
        bool success = false;
        for ( i = 0; i < 1000; i++)
        {
            success = false;
            while (!success) {
                // Read current value of counter
                expected = shm->counter;
                new_value = expected + 1;
                sched_yield();
                int current = __sync_val_compare_and_swap(&(shm->counter), expected, new_value);
                if (current == expected) {
                    // CAS succeeded, counter has been incremented
                    success = true;
                } else {
                    // CAS failed, another process modified the counter
                    expected = current;
                    new_value = current + 1;
                }
                
            }
            
        }
            printf("Child process: Counter value = %d\n", shm->counter);        
        exit(0);
    }
    else 
    {
        // Increment counter by 1
        int expected = 0;
        int new_value = 1;
        struct timespec tim, tim2;
        tim.tv_sec = 0;
        tim.tv_nsec = 100000000L;
        bool success = false;

        for ( i = 0; i < 1000; i++)
        {
            success = false;
            while (!success) {
                // Read current value of counter
                expected = shm->counter;
                new_value = expected + 1;
                sched_yield();
                int current = __sync_val_compare_and_swap(&(shm->counter), expected, new_value);
                if (current == expected) {
                    // CAS succeeded, counter has been incremented
                    success = true;
                } else {
                    // CAS failed, another process modified the counter
                    expected = current;
                    new_value = current + 1;
                }
            }

        }
            printf("Parent process: Counter value = %d\n", shm->counter);        
    }

    // Parent process
    // Wait for child process to finish
    int status;
    waitpid(pid, &status, 0);
    if (WIFEXITED(status)) {
        printf("Parent process: Counter value = %d\n", shm->counter);
    }

    // Cleanup
    munmap(shm, SHARED_MEM_SIZE);
    return 0;
}

공유 메모리에 있는 "카운터"라는 공유 변수에 액세스하고 수정해야 하는 두 개의 프로세스 P1과 P2가 있다고 가정합니다. 한 번에 하나의 프로세스만 카운터를 수정할 수 있도록 하기 위해 CAS(비교 및 교환) 작업을 기반으로 하는 동기화 메커니즘을 사용할 수 있습니다.

Suppose we have two processes, P1 and P2, that need to access and modify a shared variable called "counter" located in shared memory. To ensure that only one process can modify the counter at a time, we can use a synchronization mechanism based on the Compare-and-Swap (CAS) operation.

초기화: 먼저 공유 메모리에서 공유 변수 "카운터"를 초기 값 0으로 초기화합니다.

Initialization: We first initialize the shared variable "counter" to an initial value of 0 in shared memory.

프로세스 P1(부모): 프로세스 P1은 "카운터"의 값을 1씩 증가시키려고 합니다. 이를 위해 먼저 공유 메모리에서 "카운터"의 현재 값을 읽습니다. 그런 다음 CAS 작업을 사용하여 "카운터" 값을 현재 값(0이라고 가정함)에서 새 값(1이라고 가정함)으로 교체하려고 시도합니다. CAS 연산이 성공하면 P1이 "카운터"에 처음으로 접근한 프로세스이고 이제 카운터가 1로 증가했다는 의미입니다. CAS 연산이 실패하면 이미 다른 프로세스(이 경우 P2)가 "카운터"의 값을 수정했으며 P1은 작업을 다시 시도해야 합니다.

Process P1(Parent): Process P1 wants to increment the value of "counter" by 1. To do this, it first reads the current value of "counter" from shared memory. It then uses the CAS operation to attempt to swap the value of "counter" from its current value (let's say 0) to the new value (let's say 1). If the CAS operation succeeds, it means that P1 was the first process to access "counter", and the counter has now been incremented to 1. If the CAS operation fails, it means that another process (in this case, P2) has already modified the value of "counter", and P1 needs to retry the operation.

프로세스 P2(자식): 프로세스 P2도 "카운터" 값을 1씩 증가시키려고 합니다. P1과 동일한 단계를 따라 공유 메모리에서 "카운터"의 현재 값 읽기를 시도하고 CAS 작업을 사용하여 값을 교환합니다. of "counter" to 1. 그러나 P1이 이미 "counter"의 값을 수정했기 때문에 CAS 작업은 실패하고 P2는 작업을 다시 시도해야 합니다.

Process P2(Child): Process P2 also wants to increment the value of "counter" by 1. It follows the same steps as P1, attempting to read the current value of "counter" from shared memory and using the CAS operation to swap the value of "counter" to 1. However, since P1 has already modified the value of "counter", the CAS operation will fail, and P2 needs to retry the operation.

프로세스 P1과 P2는 각각 공유 변수 "카운터"를 1000번씩 경쟁하면 증가시킵니다. 마지막으로 부모 프로세스 P1은 증가한 값을 출력합니다. 증가한 "카운터" 값은 항상 2000이 되어야 합니다. 

Processes P1 and P2 each increment their shared variable "counter" by 1000 races. Finally, the parent process P1 outputs the incremented value. The incremented "counter" value should always be 2000.

이는 공유 변수에 대한 액세스를 동기화하기 위해 공유 메모리를 사용하는 프로세스 간에 CAS가 어떻게 사용될 수 있는지에 대한 간단한 예일 뿐입니다. 실제로는 더 복잡한 동기화 요구 사항이 있을 수 있으며 세마포어 또는 뮤텍스와 같은 다른 동기화 메커니즘도 사용될 수 있습니다.

This is just a simple example of how CAS can be used between processes using shared memory to synchronize access to a shared variable. In practice, there may be more complex synchronization requirements, and other synchronization mechanisms such as semaphores or mutexes may also be used.

아래 결과를 참고하십시오

See results below

% ./cas
Child process: Counter value = 1268
Parent process: Counter value = 2000
Parent process: Counter value = 2000

% ./cas
Parent process: Counter value = 1018
Child process: Counter value = 2000
Parent process: Counter value = 2000

% ./cas
Parent process: Counter value = 1043
Child process: Counter value = 2000
Parent process: Counter value = 2000

% ./cas
Parent process: Counter value = 1271
Child process: Counter value = 2000
Parent process: Counter value = 2000

% ./cas
Parent process: Counter value = 1225
Child process: Counter value = 2000
Parent process: Counter value = 2000

% ./cas
Parent process: Counter value = 1093
Child process: Counter value = 2000
Parent process: Counter value = 2000

% ./cas
Child process: Counter value = 1400
Parent process: Counter value = 2000
Parent process: Counter value = 2000
[bdw-ex-altibase] lswhh: ~/tmp