• 検索結果がありません。

Swap(&lock,&key);

ドキュメント内 オペレーティングシステム (ページ 44-59)

critical section lock = false;

remainder section

}

Lock-free と Wait-free を用いた同期

• Wait-free

な実装

すべてのプロセスが、任意の有限ステップで終わる命令を、ほかのプロ セスの実行速度に無関係に実行できることが保証されていること

– つまりクリティカルセクションにおいてtest_and_setやswapな どを用いたLockを行う実装は、Wait-freeではない

• Lock-free

な実装

いくつかのプロセスが、任意の有限ステップで終わる命令を、ほかのプ ロセスの実行速度に無関係に実行できることが保証されていること

– Lock-free

アルゴリズムでは、飢餓状態になるスレッドが発生してもよい

– Wait-free

な実装では

Lock-free

になっているが、その逆は真ではない

2018/4/23 3回 オペレーティングシステム 45

Compare and Swap

• Compare/Swap

命令

対象のアドレスの値をある値と比較し、もしそれが同じなら新たな値 をそのアドレスに書き込み(以下のプログラムを参照)

これを不可分(アトミック)に処理

shared_obj *old_sop, *new_sop, *t_sop;

new_sop = malloc(…);

do {

old_sop = sop;

modify(new_sop, old_sop);

t_sop = CAS(sop, old_sop, new_sop);

shared_obj *old_sop, *new_sop, *t_sop;

new_sop = malloc(…);

do {

old_sop = sop;

modify(new_sop, old_sop);

t_sop = CAS(sop, old_sop, new_sop);

shared_obj *sop = malloc(…);

int CAS (int *target, int old, int new) { int temp = *target;

if (temp == old) *target = new;

return temp;

}

ABA 問題

• ABA

は略語ではない

• ABA

はある変数の値

• ABA

は、ある変数の値が

A

から

B

に変更され、再び

A

に戻ること

2018/4/23 3回 オペレーティングシステム 47

shared_obj *old_sop, *new_sop, *t_sop;

new_sop = malloc(…);

do {

old_sop = sop;

modify(new_sop, old_sop);

t_sop = CAS(sop, old_sop, new_sop);

} while (t_sop != old_sop); /* modified by others */

free(old_sop);

new_sop = malloc(…);

do {

old_sop = sop;

modify(new_sop, old_sop);

t_sop = CAS(sop, old_sop, new_sop);

} while (t_sop != old_sop); /* modified by others */

shared_obj *old_sop, *new_sop, *t_sop;

new_sop = malloc(…);

do {

old_sop = sop;

modify(new_sop, old_sop);

t_sop = CAS(sop, old_sop, new_sop);

} while (t_sop != old_sop); /* some process has modified */

同じメモリ領域が割 り当てられている

ABA 問題

• 回避策

– “tag” ビットの導入

例:ある共有オブジェクトが常に

32

バイトアラインさ れているとき、下位

5

ビットは常に

0

それを

tag

として活用

この場合の

tag

ビットが扱える数は有限なので、

ABA

問 題を完全には解決できていない

RCU (Read-Copy Update)

• 方法

– RCU

参照側クリティカルセクションでは、共有メモリにはポイ

ンタ経由でアクセス

システムはそのポインタの使用状況をすべて追跡

変更が加わるたびにコピーが作られ、それに対して変更を適用

変更前のデータ構造の読み込みがすべて終了した段階で、ポイ

ンタを新たに作成したコピーに対するものに変更

2018/4/23 3回 オペレーティングシステム 49

RCU (Read-Copy Update)

‒ rcu_read_lock()

Readerが参照側クリティカルセクションに入る前

に呼び出し

‒ rcu_read_unlock()

参照側クリティカルセクションから抜けるときに 呼び出し

‒ synchronize_rcu() / call_rcu()

Updaterコードの終わりとreclaimerコードの終わり をマーク

すべての参照側クリティカルセクションの完了を まつ

なお、後続のRCU参照側クリティカルセクション の終了を待つ必要なし

‒ rcu_assign_pointer()

Updaterはこの関数を利用することで、RCUで保

護されたポインタに新たな値を代入

‒ rcu_dereference()

Readerはこの関数を利用して、RCUで保護された

Updater

Reader

Reclaimer rcu_read_lock()

rcu_read_unlock() rcu_dereference()

rcu_assign()

synchronize_rcu() call_rcu()

updates into

removal

and "reclamation" phases

• 特徴

– Wait-free

な読み込みが可能

– RCU

は、古い状態のデータ構造を一定期間そのまま残す必

要があるため、大量のメモリを使用する可能性あり

2018/4/23 3回 オペレーティングシステム 51

RCU (Read-Copy Update)

Transactional Memory

• Locking の問題点

メニーコア環境下で効率的に処理を行うことは困難

• Compare_and_Swap の問題点

複数の変更がある場合、アルゴリズムが複雑化

• Transactional Memory

一部のメモリ領域に対して、シリアライズされた「読み込 みー変更ー書き込み」という操作が可能

– Lock-free

pthread_mutex_lock(pthread_mutex_t

*mutex)

• The mutex object referenced by mutex shall be locked by calling pthread_mutex_lock(). If the mutex is already locked, the calling thread shall block until the mutex becomes available. This operation shall return with the mutex object referenced by mutex in the locked state with the calling thread as its owner.

• If the mutex type is PTHREAD_MUTEX_NORMAL, deadlock detection shall not be provided.

Attempting to relock the mutex causes deadlock. If a thread attempts to unlock a mutex that it has not locked or a mutex which is unlocked, undefined behavior results.

• If the mutex type is PTHREAD_MUTEX_ERRORCHECK, then error checking shall be provided. If a thread attempts to relock a mutex that it has already locked, an error shall be returned. If a thread attempts to unlock a mutex that it has not locked or a mutex which is unlocked, an error shall be returned.

• If the mutex type is PTHREAD_MUTEX_RECURSIVE, then the mutex shall maintain the concept of a lock count. When a thread successfully acquires a mutex for the first time, the lock count shall be set to one. Every time a thread relocks this mutex, the lock count shall be incremented by one. Each time the thread unlocks the mutex, the lock count shall be decremented by one. When the lock count reaches zero, the mutex shall become available for other threads to acquire. If a thread attempts to unlock a mutex that it has not locked or a mutex which is unlocked, an error shall be returned.

• If the mutex type is PTHREAD_MUTEX_DEFAULT, attempting to recursively lock the mutex results in undefined behavior. Attempting to unlock the mutex if it was not locked by the calling thread results in undefined behavior. Attempting to unlock the mutex if it is not locked results in undefined behavior.

2018/4/23 3回 オペレーティングシステム 53

pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)

• The pthread_cond_wait() functions shall block on a condition variable. They shall be called with mutex locked by the calling thread or undefined behavior results.

• These functions atomically release mutex and cause the calling thread to block on the

condition variable cond; atomically here means "atomically with respect to access by another thread to the mutex and then the condition variable". That is, if another thread is able to

acquire the mutex after the about-to-block thread has released it, then a subsequent call to pthread_cond_broadcast() or pthread_cond_signal() in that thread shall behave as if it were issued after the about-to-block thread has blocked.

• Upon successful return, the mutex shall have been locked and shall be owned by the calling thread.

• The pthread_cond_signal() function shall unblock at least one of the threads that are blocked

pthread_cond_signal(pthread_cond_t *cond)

Pthread (1/2)

2018/4/23 3回 オペレーティングシステム 55

• int pthread_create(pthread_t *thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg);

• void pthread_exit(void *retval);

• int pthread_join(pthread_t th, void **thread_return);

• int pthread_detach(pthread_t th);

• pthread_t pthread_self(void);

• int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex‐attr_t *mutexattr);

• int pthread_mutex_lock(pthread_mutex_t *mutex));

• int pthread_mutex_trylock(pthread_mutex_t *mutex);

• int pthread_mutex_unlock(pthread_mutex_t *mutex);

• int pthread_mutex_destroy(pthread_mutex_t *mutex);

• int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);

• Int pthread_cond_signal(pthread_cond_t *cond);

• int pthread_cond_broadcast(pthread_cond_t *cond);

• Int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

• int pthread_cond_timedwait(pthread_cond_t *cnd, pthread_mutex_t *mtx, const struct timespec *at);

• int pthread_cond_destroy(pthread_cond_t *cond);

• int pthread_barrier_init ( pthread_barrier_t * barri, const pthread_barrierattr_t * attr, unsigned int cnt );

• int pthread_barrier_destroy ( pthread_barrier_t * barrier );

• int pthread_barrier_wait ( pthread_barrier_t * barrier );

Pthread (2/2)

• ipthread_attr_init, pthread_attr_destroy, pthread_attr_setdetachstate, pthread_attr_getdetachstate, pthread_attr_setschedparam, pthread_attr_getschedparam, pthread_attr_setschedpolicy,

pthread_attr_getschedpolicy, pthread_attr_setinheritsched, pthread_attr_getinheritsched,

pthread_attr_setscope, pthread_attr_getscope pthread_attr_init, pthread_setschedparam,

pthread_getschedparam, pthread_equal

Bounded Buffer using pthread (1/3)

2018/4/23 3回 オペレーティングシステム

#include <stdlib.h>

#include <stdio.h>

#include <pthread.h>

#define N_ITEM 8

#define N_PRODUCER 4

#define N_CONSUMER 4

#define BSIZE 8

int buf[BSIZE];

int count;

int in, out;

int waiting;

pthread_mutex_t bbmutex;

pthread_cond_t bbcond;

pthread_barrier_t pbarrier;

void bbinit() {

pthread_mutex_init(&bbmutex, NULL);

pthread_cond_init(&bbcond, NULL);

in = out = waiting = 0;

}

main() {

int i, cc;

pthread_t pth[N_PRODUCER+N_CONSUMER];

void *retval;

pthread_barrier_init(&pbarrier, 0, N_PRODUCER+N_CONSUMER);

for (i = 0; i < N_PRODUCER; i++) {

cc = pthread_create(&pth[i], NULL, producer, 0);

if (cc != 0) { perror("main"); exit(-1); } }

for (i = 0; i < N_CONSUMER; i++) {

cc = pthread_create(&pth[i + N_PRODUCER], NULL, consumer, 0);

if (cc != 0) { perror("main"); exit(-1); } }

for (i = 0; i < (N_PRODUCER + N_CONSUMER); i++) { pthread_join(pth[i], &retval);

pthread_detach(pth[i]);

if (cc != 0) { perror("main"); exit(-1); } }

exit(0);

}

57

Bounded Buffer using pthread (2/3)

void *producer(void *arg) {

int ret = 0;

int i;

pthread_barrier_wait(&pbarrier);

for (i = 0; i < (N_CONSUMER*N_ITEM)/N_PRODUCER; i++) put(i);

pthread_exit(&ret);

return 0;

}

void *consumer(void *arg) {

int ret = 0;

int val;

int i;

pthread_barrier_wait(&pbarrier);

for (i = 0; i < N_ITEM; i++) { val = get(i);

}

pthread_exit(&ret);

return 0;

ドキュメント内 オペレーティングシステム (ページ 44-59)

関連したドキュメント