○セマフォ (semaphore)
プロセス間の排他制御、同期制御ができます

★セマフォの動作原理------------------------------
セマフォから値を引いた結果が0以下になるとそのプロセスがスリープします、結果が0または0以上になると自動的にスリープから脱します
セマフォのロックは値から-1をします
セマフォのロック解除は値に+1をします
------------------------------------------------

■forkしたプロセス間の排他処理を行います
3つのプロセスを作成して排他処理を行い、文字を出力します

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/sem.h>

//排他処理の開始するためのロック
//自分よりも先にロックされていた場合にはロック解除されるまでスリープします
void lock(int semid){
	struct sembuf sb[1];
	sb[0].sem_num=0;
	sb[0].sem_op=-1;
	sb[0].sem_flg=0;
	if(EOF==semop(semid,sb,1)){
		printf("%s\n","lock ERR");
		exit(1);
	}
}

//排他処理の終了のためのロック解除
void unlock(int semid){
	struct sembuf sb[1];
	sb[0].sem_num=0;
	sb[0].sem_op=1;
	sb[0].sem_flg=0;
	if(EOF==semop(semid,sb,1)){
		printf("%s\n","unlock ERR");
		exit(1);
	}
}


main(){
	//セマフォを割り付けます
	int semid=semget(IPC_PRIVATE,1,0666);
	if(semid==EOF){
		printf("%s\n","ERR");
		exit(0);
	}
	//セマフォに初期値1を書き込みます
	if((semctl(semid,0,SETVAL,1))==EOF){
		printf("%s\n","ERR");
		exit(0);
	}
	
	//排他処理をするプロセスの開始
	int pid1;		
	if(0==(pid1=fork())){
		lock(semid);//ロック
		
		int i;
		for(i=0;i<10;i++){
			printf("A");
			fflush(stdout);
			sleep(1);
		}
		
		unlock(semid);//ロック解除
		exit(0);
	}

	//排他処理をするプロセスの開始
	int pid2;
	if(0==(pid2=fork())){
		lock(semid);
		
		int i;
		for(i=0;i<10;i++){
			printf("%s","B");
			fflush(stdout);
			sleep(1);
		}
		
		unlock(semid);
		exit(0);
	}

	//排他処理をするプロセスの開始
	int pid3;
	if(0==(pid3=fork())){
		lock(semid);
		
		int i;
		for(i=0;i<10;i++){
			printf("%s","C");
			fflush(stdout);
			sleep(1);
		}
		
		unlock(semid);
		exit(0);
	}

	//全てのプロセスが終了するまで待機(先にセマフォを削除すると排他処理ができなくなるため)
	wait(&pid1);
	wait(&pid2);
	wait(&pid3);
	
	//セマフォの削除
	if((semctl(semid,0,IPC_RMID,0))==EOF){
		printf("%s\n","ERR");
		exit(0);
	}
		
}


■無関係なプロセス間で排他処理を行います

▼セマフォの生成・初期化・削除を管理するプログラム

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/sem.h>

main(){
	//適当なファイルをキーにする
	key_t key=ftok("/bin/cp",1);
	int semid=semget(key,1,0666 | IPC_CREAT);
	if(semid==EOF){
		printf("%s\n","ERR");
		exit(0);
	}
	if((semctl(semid,0,SETVAL,1))==EOF){
		printf("%s\n","ERR");
		exit(0);
	}
	
	printf("%s\n","Enterが押されたらセマフォを削除します");
	getchar();
	
	if((semctl(semid,0,IPC_RMID,0))==EOF){
		printf("%s\n","ERR");
		exit(0);
	}
		
}

▼上のプログラムにより作成されたセマフォを使って排他処理を行うプロセス
キーが同じならば複数起動させても排他処理が行われます

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/sem.h>

void lock(int semid){
	struct sembuf sb[1];
	sb[0].sem_num=0;
	sb[0].sem_op=-1;
	sb[0].sem_flg=0;
	if(EOF==semop(semid,sb,1)){
		printf("%s\n","lock ERR");
		exit(1);
	}
}

void unlock(int semid){
	struct sembuf sb[1];
	sb[0].sem_num=0;
	sb[0].sem_op=1;
	sb[0].sem_flg=0;
	if(EOF==semop(semid,sb,1)){
		printf("%s\n","unlock ERR");
		exit(1);
	}
}

main(){
	//適当なファイルをキーにする
	key_t key=ftok("/bin/cp",1);
	int semid=semget(key,1,0666 | IPC_CREAT);
	if(semid==EOF){
		printf("%s\n","ERR");
		exit(0);
	}

	lock(semid);
	
	int i;
	for(i=0;i<10;i++){
		printf("A");
		fflush(stdout);
		sleep(1);
	}
		
	unlock(semid);
		
}


■同期
自らロックした子プロセスを親プロセスがロックを解除します
セマフォの初期値を0に設定しているところがミソです

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/sem.h>

void lock(int semid){
	struct sembuf sb[1];
	sb[0].sem_num=0;
	sb[0].sem_op=-1;
	sb[0].sem_flg=0;
	if(EOF==semop(semid,sb,1)){
		printf("%s\n","lock ERR");
		exit(1);
	}
}

void unlock(int semid){
	struct sembuf sb[1];
	sb[0].sem_num=0;
	sb[0].sem_op=1;
	sb[0].sem_flg=0;
	if(EOF==semop(semid,sb,1)){
		printf("%s\n","unlock ERR");
		exit(1);
	}
}

main(){
	int semid=semget(IPC_PRIVATE,1,0666);
	if(semid==EOF){
		printf("%s\n","ERR");
		exit(0);
	}

	//初期値を0に設定
	if((semctl(semid,0,SETVAL,0))==EOF){
		printf("%s\n","ERR");
		exit(0);
	}

	int pid=fork();
	if(pid==0){
		lock(semid);//自分にロックを掛ける
		printf("%s\n","子:ロックが解除されました");
		sleep(1);
		exit(0);
		
	}else{
		sleep(2);
		printf("%s\n","親:子プロセスのロックを解除する");
		unlock(semid);//ロック解除
		wait(pid);
	}
	
	if((semctl(semid,0,IPC_RMID,0))==EOF){
		printf("%s\n","ERR");
		exit(0);
	}	
}





▲トップページ > Linux と C