Swoole에서 세마포어는 한 번에 하나의 프로세스만 있도록 공유 리소스를 보호하는 데 주로 사용됩니다. 세마포어의 값이 양수이면 테스트 중인 스레드를 잠글 수 있습니다. 세마포어가 0이면 테스트된 스레드가 절전 대기열에 들어가 깨어나기를 기다립니다.
이 튜토리얼의 운영 환경: Windows 10 시스템, Swoole 4 버전, DELL G3 컴퓨터
세마포어의 사용은 주로 공유 리소스를 보호하는 데 사용되므로 해당 리소스는 한 번에만 사용할 수 있습니다. 프로세스(스레드)가 소유합니다
. 세마포어의 값이 양수이면 유휴 상태임을 의미합니다. 테스트 중인 스레드는 사용 중에 잠길 수 있습니다. 0이면 점유되었음을 의미하며 테스트 스레드는 슬립 큐에 들어가 깨어나기를 기다립니다.
Linux는 두 가지 유형의 세마포를 제공합니다.
(1) 커널 제어 경로에서 사용되는 커널 세마포
(2) POSIX 세마포와 SYSTEM
V 세마포로 구분되는 사용자 모드 프로세스에서 사용되는 세마포 .
POSIX 세마포어는 명명된 세마포어와 명명되지 않은 세마포어로 구분됩니다.
세마포어라고 명명되었으며, 그 값이 파일에 저장되므로 스레드 및 프로세스 간 동기화에 사용할 수 있습니다. 값이 메모리에 저장되는 이름 없는
세마포입니다.
커널 세마포
커널 세마포 구성
커널 세마포는 잠금이 닫힐 때 커널 제어 경로가 계속되는 것을 허용하지 않는다는 점에서 스핀 잠금과 유사합니다. 그러나
커널 제어 경로가 커널 세마포어 잠금으로 보호되는 사용 중인 리소스를 획득하려고 시도하면 해당 프로세스가 일시 중지됩니다. 리소스가 해제된 경우에만 프로세스가 다시 실행 가능해집니다.
잠자기 기능만 커널 세마포를 얻을 수 있습니다. 인터럽트 핸들러나 지연 가능한 함수는 커널 세마포를 사용할 수 없습니다.
커널 세마포어는 struct semaphore 유형의 객체입니다.
#include <pthread.h> #include <semaphore.h> #include <sys/types.h> #include <stdio.h> #include <unistd.h> int number; // 被保护的全局变量 sem_t sem_id; void* thread_one_fun(void *arg) { sem_wait(&sem_id); printf("thread_one have the semaphore\n"); number++; printf("number = %d\n",number); sem_post(&sem_id); } void* thread_two_fun(void *arg) { sem_wait(&sem_id); printf("thread_two have the semaphore \n"); number--; printf("number = %d\n",number); sem_post(&sem_id); } int main(int argc,char *argv[]) { number = 1; pthread_t id1, id2; sem_init(&sem_id, 0, 1); pthread_create(&id1,NULL,thread_one_fun, NULL); pthread_create(&id2,NULL,thread_two_fun, NULL); pthread_join(id1,NULL); pthread_join(id2,NULL); printf("main,,,\n"); return 0; }
위의 루틴에서 어떤 스레드가 세마포어 리소스에 먼저 적용되는지는 무작위입니다. 특정 순서를 원하는 경우 2개의 세마포어를 사용하여 이를 달성할 수 있습니다. 예를 들어, 다음 루틴에서는 스레드 1이 먼저 실행을 마친 다음 스레드 2가 끝날 때까지 계속 실행됩니다.
int number; // 被保护的全局变量 sem_t sem_id1, sem_id2; void* thread_one_fun(void *arg) { sem_wait(&sem_id1); printf(“thread_one have the semaphore\n”); number++; printf(“number = %d\n”,number); sem_post(&sem_id2); } void* thread_two_fun(void *arg) { sem_wait(&sem_id2); printf(“thread_two have the semaphore \n”); number–; printf(“number = %d\n”,number); sem_post(&sem_id1); } int main(int argc,char *argv[]) { number = 1; pthread_t id1, id2; sem_init(&sem_id1, 0, 1); // 空闲的 sem_init(&sem_id2, 0, 0); // 忙的 pthread_create(&id1,NULL,thread_one_fun, NULL); pthread_create(&id2,NULL,thread_two_fun, NULL); pthread_join(id1,NULL); pthread_join(id2,NULL); printf(“main,,,\n”); return 0; }
(b) 관련 프로세스 간 이름 없는 세마포 동기화
이 프로그램에는 두 개의 프로세스가 있고 그 중 하나는 다른 하나의 하위 프로세스이므로 관련 프로세스라고 합니다(
fork
에서 생성됨). .
원래 포크의 경우 하위 프로세스는 상위 프로세스의 코드 복사본만 상속합니다. Mutex는 상위 및 하위 프로세스에서 두 개의 독립 변수여야 합니다
. 그러나 뮤텍스를 초기화할 때 pshared = 1은
을 참조합니다. 해당 뮤텍스는 공유 메모리 영역에 있으므로 이때 뮤텍스는 부모 프로세스와 자식 프로세스가 공유하는 변수가 됩니다. 이때 관련 프로세스를 동기화하기 위해 mutex를 사용할 수 있습니다.
#include <semaphore.h> #include <stdio.h> #include <errno.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> int main(int argc, char **argv) { int fd, i,count=0,nloop=10,zero=0,*ptr; sem_t mutex; //open a file and map it into memory fd = open("log.txt",O_RDWR|O_CREAT,S_IRWXU); write(fd,&zero,sizeof(int)); ptr = mmap( NULL,sizeof(int),PROT_READ | PROT_WRITE,MAP_SHARED,fd,0 ); close(fd); /* create, initialize semaphore */ if( sem_init(&mutex,1,1) < 0) // { perror("semaphore initilization"); exit(0); } if (fork() == 0) { /* child process*/ for (i = 0; i < nloop; i++) { sem_wait(&mutex); printf("child: %d\n", (*ptr)++); sem_post(&mutex); } exit(0); } /* back to parent process */ for (i = 0; i < nloop; i++) { sem_wait(&mutex); printf("parent: %d\n", (*ptr)++); sem_post(&mutex); } exit(0); }
네임드 세마포의 특징은 세마포의 값을 파일에 저장한다는 것입니다.
이는 매우 광범위한 용도를 가지고 있음을 결정합니다. 스레드, 관련 프로세스 및 관련 없는
프로세스에도 사용할 수 있습니다. (a) 프로세스 간에 네임드 세마포어를 공유할 수 있는 이유
네임드 세마포어의 값이 파일에 저장되므로 관련 프로세스에 대해서는 하위 프로세스가 상위
프로세스의 파일 디스크립터를 상속받습니다. 그러면 자식 프로세스가 상속한 파일 디스크립터가 가리키는 파일은 부모 프로세스와 동일합니다. 물론 파일에 저장된 명명된 세마포어 값은 공유됩니다.
(b) 명명된 세마포 관련 함수 설명
명명된 세마포를 사용하는 경우에는 명명되지 않은 세마포와 sem_wait 및 sem_post 함수를 공유합니다.
차이점은 명명된 세마포어는 sem_init 대신 sem_open을 사용한다는 점입니다. 그리고 마지막에는 명명된 세마포어를 파일
처럼 닫아야 합니다.
(1) 기존 명명된 세마포를 열거나 명명된 세마포를 생성하고 초기화합니다. 단일 호출로 세마포어의 생성, 초기화 및 권한 설정이 완료됩니다.
sem_t *sem_open(const char *name, int oflag, mode_t mode, int value)
name은 파일의 경로 이름입니다.
Oflag에는 O_CREAT 또는 O_CREAT|EXCL의 두 가지 값이 있습니다. 세마포어의 새로운 액세스 권한
값은 세마포어의 초기화 값을 지정합니다.
참고:
여기에 이름을 /tmp/aaa.sem 형식으로 쓸 수 없습니다. Linux에서는 sem이 /dev/shm 디렉터리에
생성되기 때문입니다. 이름은 "/mysem" 또는 "mysem"으로 작성하시면 되며, 생성되는 파일은 "/dev/shm/sem.mysem"이 됩니다. 또한 "/tmp/mysem"이나 이와 유사한 것을 쓰지 마십시오.
oflag = O_CREAT일 때 name으로 지정한 세마포어가 존재하지 않으면 생성되며, 다음
mode 및 value 매개변수가 유효해야 합니다. name으로 지정된 세마포가 이미 존재하는 경우 세마포를 직접 열고 모드 및 값 매개변수를 무시합니다.
oflag = O_CREAT|O_EXCL일 때 name으로 지정된 세마포가 이미 존재하는 경우 함수는
error를 직접 반환합니다.
(2) 세마포어를 사용한 후에는 이를 파괴하는 것이 중요합니다.
在做这个之前,要确定所有对这个有名信号量的引用都已经通过sem_close()函数
关闭了,然后只需在退出或是退出处理函数中调用sem_unlink()去删除系统中的信号量,
注意如果有任何的处理器或是线程引用这个信号量,sem_unlink()函数不会起到任何的作
用。
也就是说,必须是最后一个使用该信号量的进程来执行sem_unlick才有效。因为每个
信号灯有一个引用计数器记录当前的打开次数,sem_unlink必须等待这个数为0时才能把
name所指的信号灯从文件系统中删除。也就是要等待最后一个sem_close发生。
(c)有名信号量在无相关进程间的同步
前面已经说过,有名信号量是位于共享内存区的,那么它要保护的资源也必须是位于
共享内存区,只有这样才能被无相关的进程所共享。
在下面这个例子中,服务进程和客户进程都使用shmget和shmat来获取得一块共享内
存资源。然后利用有名信号量来对这块共享内存资源进行互斥保护。
File1: server.c #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <semaphore.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #define SHMSZ 27 char SEM_NAME[]= "vik"; int main() { char ch; int shmid; key_t key; char *shm,*s; sem_t *mutex; //name the shared memory segment key = 1000; //create & initialize semaphore mutex = sem_open(SEM_NAME,O_CREAT,0644,1); if(mutex == SEM_FAILED) { perror("unable to create semaphore"); sem_unlink(SEM_NAME); exit(-1); } //create the shared memory segment with this key shmid = shmget(key,SHMSZ,IPC_CREAT|0666); if(shmid<0) { perror("failure in shmget"); exit(-1); } //attach this segment to virtual memory shm = shmat(shmid,NULL,0); //start writing into memory s = shm; for(ch='A';ch<='Z';ch++) { sem_wait(mutex); *s++ = ch; sem_post(mutex); } //the below loop could be replaced by binary semaphore while(*shm != '*') { sleep(1); } sem_close(mutex); sem_unlink(SEM_NAME); shmctl(shmid, IPC_RMID, 0); exit(0); } <u>File 2: client.c</u> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <semaphore.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #define SHMSZ 27 char SEM_NAME[]= "vik"; int main() { char ch; int shmid; key_t key; char *shm,*s; sem_t *mutex; //name the shared memory segment key = 1000; //create & initialize existing semaphore mutex = sem_open(SEM_NAME,0,0644,0); if(mutex == SEM_FAILED) { perror("reader:unable to execute semaphore"); sem_close(mutex); exit(-1); } //create the shared memory segment with this key shmid = shmget(key,SHMSZ,0666); if(shmid<0) { perror("reader:failure in shmget"); exit(-1); } //attach this segment to virtual memory shm = shmat(shmid,NULL,0); //start reading s = shm; for(s=shm;*s!=NULL;s++) { sem_wait(mutex); putchar(*s); sem_post(mutex); } //once done signal exiting of reader:This can be replaced by another semaphore *shm = '*'; sem_close(mutex); shmctl(shmid, IPC_RMID, 0); exit(0); }
SYSTEM V信号量
这是信号量值的集合,而不是单个信号量。相关的信号量操作函数由
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <stdio.h> static int nsems; static int semflg; static int semid; int errno=0; union semun { int val; struct semid_ds *buf; unsigned short *array; }arg; int main() { struct sembuf sops[2]; //要用到两个信号量,所以要定义两个操作数组 int rslt; unsigned short argarray[80]; arg.array = argarray; semid = semget(IPC_PRIVATE, 2, 0666); if(semid < 0 ) { printf("semget failed. errno: %d\n", errno); exit(0); } //获取0th信号量的原始值 rslt = semctl(semid, 0, GETVAL); printf("val = %d\n",rslt); //初始化0th信号量,然后再读取,检查初始化有没有成功 arg.val = 1; // 同一时间只允许一个占有者 semctl(semid, 0, SETVAL, arg); rslt = semctl(semid, 0, GETVAL); printf("val = %d\n",rslt); sops[0].sem_num = 0; sops[0].sem_op = -1; sops[0].sem_flg = 0; sops[1].sem_num = 1; sops[1].sem_op = 1; sops[1].sem_flg = 0; rslt=semop(semid, sops, 1); //申请0th信号量,尝试锁定 if (rslt < 0 ) { printf("semop failed. errno: %d\n", errno); exit(0); } //可以在这里对资源进行锁定 sops[0].sem_op = 1; semop(semid, sops, 1); //释放0th信号量 rslt = semctl(semid, 0, GETVAL); printf("val = %d\n",rslt); rslt=semctl(semid, 0, GETALL, arg); if (rslt < 0) { printf("semctl failed. errno: %d\n", errno); exit(0); } printf("val1:%d val2: %d\n",(unsigned int)argarray[0],(unsigned int)argarray[1]); if(semctl(semid, 1, IPC_RMID) == -1) { Perror(“semctl failure while clearing reason”); } return(0); }
推荐学习: swoole教程
위 내용은 Swoole에서 세마포어의 사용법은 무엇입니까의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!