重要なリソース:同時に1つのプロセスまたはスレッドからのみアクセスが許可されるリソース
重要な領域
:ショッピングモールの試着室、交差点の
同期などの重要なリソースにアクセスするコードセグメント:プログラム実行の正確性を確保するための制御、セマフォを使用する
セマフォは特別な変数であり、通常は正の値を取ります。その値は、アクセスが許可されているリソースの数を表します。リソースを取得するときは、セマフォの値を1つアトミックに減らす必要があります。この操作はP操作と呼ばれます。
セマフォ値が0の場合、使用可能なリソースがなく、P操作がブロックされることを意味します。
リソースを解放するときは、セマフォの値に1をアトミックに追加する必要があります。この操作はV操作と呼ばれます。
セマフォは主にプロセスを同期するために使用されます。セマフォの値が0、1のみである場合、それはバイナリセマフォと呼ばれます。
セマフォの値が1より大きい場合、それはカウントセマフォと呼ばれます。たとえば、3人用の試着室が3つあります。4人目は、
PV操作がリソースの取得と解放の
原子性保証を通過するのを待つ必要があり、問題はありません。
例:
nは列車のチケットの残りの数を表します。
全員がチケットを購入し、n-1
2人
がチケットを購入します。チケットの購入は並行プロセスであるため、実際にはn-2である必要があり
ます。 nの値を1減らすために1つ必要です。プロセス中に、n = 5はプロセッサに読み取り、完了していない1だけデクリメントする準備をします。同時に、別の訪問者もn = 5を読み取ります。プロセッサは1をデクリメントする準備をし、最後の2つのプロセスは4を
n = 4に書き戻します。
この結果4は明らかに問題がある
ため、nを同時に制御するのではなく、制御
する必要があります。この問題を解決するには、アトミック操作のセマフォを使用する必要があります
。PV操作を通じて0,1
例2:
プリンタへのアクセス
はセマフォで制御する必要があります。
最初に、AはP演算を実行してセマフォの値(1)を取得し、1を減算すると、0
になります。Vが実行される前に、Aが使用されています。このとき、Bアクセス、p操作、セマフォ0の検出、ブロック
同じ端末で同時にviac
vi bc
次に、セマフォを使用して次のように問題を解決します。
操作セマフォのインターフェース導入:
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/ipc.h>
/*
semget()创建或者获取已存在的信号量
semget()成功返回信号量的 ID, 失败返回-1
key:两个进程使用相同的 key 值,就可以使用同一个信号量
nsems:内核维护的是一个信号量集,在新建信号量时,其指定信号量集中信号
量的个数
semflg 可选: IPC_CREAT IPC_EXCL
*/
int semget(key_t key, int nsems, int semflg);//创建,获取信号量
/*
semop()对信号量进行改变,做 P 操作或者 V 操作
semop()成功返回 0,失败返回-1
struct sembuf
{
unsigned short sem_num; //指定信号量集中的信号量下标
short sem_op; //其值为-1,代表 P 操作,其值为 1,代表 V 操作
short sem_flg; //SEM_UNDO
};
*/
int semop(int semid, struct sembuf *sops, unsigned nsops);//初始化
/*
semctl()控制信号量
semctl()成功返回 0,失败返回-1
cmd 选项: SETVAL IPC_RMID
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array
struct seminfo *_buf;
}; */
int semctl(int semid, int semnum, int cmd, ...);
セマフォをカプセル化するためのインターフェース:
vi sem.h.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/sem.h>
union semun{
int val;//初始值
};
void sem_init();//创建/或者已存在的信号量
void sem_p();//p 减一
void sem_v();//v 加一
void sem_destroy();//销毁
あなたsem.c.
#include "sem.h"
static int semid = -1;
void sem_init()//创建/或者已存在的信号量
{
semid = semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);//约定都使用这个key_t值,1个信号量,全新创建
if ( semid == -1 )//已存在
{
semid = semget((key_t)1234,1,0600);//存在就直接获取
}
else
{
union semun a;
a.val = 1;//信号量的初始值
if ( semctl(semid,0,SETVAL,a) == -1 )//下标为0,初始化失败
{
perror("semctl error");
}
}
}
void sem_p()//p 减一
{
struct sembuf buf;
buf.sem_num = 0;//成员下标
buf.sem_op = -1;//p
buf.sem_flg = SEM_UNDO;
if ( semop(semid,&buf,1) == -1 )
{
perror("semop p error");
}
}
void sem_v()//v 加一
{
struct sembuf buf;
buf.sem_num = 0;//成员下标
buf.sem_op = 1;//v
buf.sem_flg = SEM_UNDO;
if ( semop(semid,&buf,1) == -1 )
{
perror("semop v error");
}
}
void sem_destroy()//销毁
{
if ( semctl(semid,0,IPC_RMID) == -1 )
{
perror("semctl del error");
}
}
vi ac
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
int main()
{
sem_init();//初始化1
int i = 0;
for( ;i < 10; i++ )
{
sem_p();//如果检测到1,通过,减1
printf("a");
fflush(stdout);
int n = rand() % 3;
sleep(n);
printf("a");
fflush(stdout);
sem_v();
n = rand() % 3;
sleep(n);
}
}
vi bc
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
int main()
{
sem_init();
int i = 0;
for( ;i < 10; i++ )
{
sem_p();
printf("b");
fflush(stdout);
int n = rand() % 3;
sleep(n);
printf("b");
fflush(stdout);
sem_v();
n = rand() % 3;
sleep(n);
}
}
コンパイルして実行する
このセマフォは、カーネルに通知することによって作成され、カーネルは
それを維持します。終了した後も、カーネルは維持されて
おり、セマフォが使用されている可能性があります。
一度だけ削除できます
使用後は削除する必要があります。削除しないと、1か0(前回)かわかりません。
コマンドを使用して削除してください。次の質問を見てください。
3つのプロセスa、b、およびcは、それぞれ「A」、「B」、および「C」を入力し、出力結果は「ABCABCABC ...」である必要があります。
3つのセマフォのうち最初のセマフォは1に初期化され、最後の2つは0に初期化されます。ps1は初めて通過し、ps2ps3は通過し
ません
vi ac
vi bc
vi cc
run