RCU

최승혁·2022년 6월 29일
0

RCU (Read-Copy Update)

정의

리눅스 커널 v2.5.43부터 추가된 동기화 방식으로, 다른 동기화 기법에 비해 읽기 작업이 매우 효율적이다. 오버헤드 없이 읽기 작업을 동시에 수행할 수 있는 장점이 있다.

Pub-Sub 기법

RCU는 데이터가 수정되고 있는 동안에도 안전하게 데이터를 처리할 수 있다. 이것을 구현하기 위해, Pub-Sub 기법을 도입했는데, 아래 예제 소스를 통해 이해해보자.

  • publish
struct foo {
    int a;
    int b;
    int c;
};

struct foo *gp = NULL;

struct foo *p;
p = kmalloc(sizeof(*p), GFP_KERNEL);

// 컴파일러 최적화
p->a = 1;
p->b = 2;
p->c = 3;
gp = p;

위의 코드에서 컴파일러 최적화라고 명시된 부분 아래의 행들은 컴파일러 최적화에 의해서 실행 순서가 바뀔 수 있다. gp에 p 값을 제대로 할당하기 위해서는 실행 순서가 바뀌면 안된다. 이를 위해서 실행 순서를 명확히 지키기 위해서 메모리 배리어 기법을 사용한다. 하지만 이는 사용하기에 어렵다. 따라서 RCU에서는 rcu_assign_pointer() API 함수를 사용하여 실행 순서를 확실히 지키도록 한다. 이 기법을 publish라고 한다.

p->a = 1;
p->b = 2;
p->c = 3;
rcu_assign_pointer(gp, p);

위와 같이 하면 p 구조체 멤버 변수에 1, 2, 3이 순서대로 할당된 후에 p 포인터가 gp에 연결된다. 이처럼 RCU에서 publish는 할당(대입, 쓰기)에 해당하는 실행 순서를 명확히 지키도록 해주는기법이다.

  • subscribe
rcu_read_lock();
p = rcu_dereference(gp);
if (p != NULL) {
	do_something_with(p->a, p->b, p->c);
}
rcu_read_unlock();

위와 같이 하면 읽기 작업이 순서대로 명확하게 실행 된다. 1행의 rcu_read_lock()과 6행의 rcu_read_unlock() 사이의 코드들은 RCU에 의해서 보호되는 Critical Section에 해당한다.

RCU 기본 API 함수들

구분Publish(쓰기)Retract(삭제)Subscribe(읽기)
Pointersrcu_assign_pointer()rcu_assign_pointer(..., NULL)rcu_dereference()
Listslist_add_rcu()
list_add_tail_rcu()
list_replace_rcu()
list_del_rcu()list_for_each_entry_rcu()
Hash Listshlist_add_after_rcu()
hlist_add_before_rcu()
hlist_add_head_rcu()
hlist_replace_rcu()
hlist_del_rcu()hlist_for_each_entry_rcu()

Grace Period

Grace Period는 데이터 변경 작업(Removal)이 마무리되는 시점에 읽기(Reader)가 진행 중이었다면 이것이 마무리(완료)될 때까지 기다리는 구간이다.

이렇게 period 구간 동안 대기를 해야 다음 데이터를 읽을 때 변경된 데이터가 안전하게 읽혀진다. (동기화) RCU에서는 Grace Period 구간 동안 대기하는 API 함수를 synchronize_rcu()라는 함수명으로 작성했다.

  • 예제
struct foo {
    struct list_head list;
    int a;
    int b;
    int c;
};
LIST_HEAD(head);

p = search(head, key);
if (p == NULL) {
    /* Take appropriate action, unlock, and return */
}
q = kmalloc(sizeof(*p), GFP_KERNEL);
*q = *p;
q->b = 2;
q->c = 3;
list_replace_rcu(&p->list, &q->list);
synchrosize_rcu();
kfree(p);

위에서 synchronize_rcu() 함수는 앞 행에서 진행한 변경(쓰기) 작업들에서 RCU 읽기 작업이 있었다면 이것이 마무리 될 때까지 대기하여 데이터 동기화가 이루어지도록 한다.






참고자료: 커널 연구회
profile
그냥 기록하는 블로그

0개의 댓글