RCU (Read-Copy Update)
리눅스 커널 v2.5.43부터 추가된 동기화 방식으로, 다른 동기화 기법에 비해 읽기 작업이 매우 효율적이다. 오버헤드 없이 읽기 작업을 동시에 수행할 수 있는 장점이 있다.
RCU는 데이터가 수정되고 있는 동안에도 안전하게 데이터를 처리할 수 있다. 이것을 구현하기 위해, Pub-Sub 기법을 도입했는데, 아래 예제 소스를 통해 이해해보자.
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는 할당(대입, 쓰기)에 해당하는 실행 순서를 명확히 지키도록 해주는기법이다.
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에 해당한다.
구분 | Publish(쓰기) | Retract(삭제) | Subscribe(읽기) |
---|---|---|---|
Pointers | rcu_assign_pointer() | rcu_assign_pointer(..., NULL) | rcu_dereference() |
Lists | list_add_rcu() list_add_tail_rcu() list_replace_rcu() | list_del_rcu() | list_for_each_entry_rcu() |
Hash Lists | hlist_add_after_rcu() hlist_add_before_rcu() hlist_add_head_rcu() hlist_replace_rcu() | hlist_del_rcu() | hlist_for_each_entry_rcu() |
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 읽기 작업이 있었다면 이것이 마무리 될 때까지 대기하여 데이터 동기화가 이루어지도록 한다.