Dans swoole, les sémaphores sont principalement utilisés pour protéger les ressources partagées afin qu'il n'y ait qu'un seul processus à la fois ; lorsque la valeur du sémaphore est positive, cela signifie que le thread testé peut être verrouillé pour utilisation si la valeur de. le sémaphore est 0 , cela signifie que le thread testé entrera dans la file d'attente de veille et attendra d'être réveillé.
L'environnement d'exploitation de ce tutoriel : système Windows 10, version Swoole 4, ordinateur DELL G3
L'utilisation des sémaphores est principalement utilisée pour protéger les ressources partagées, donc ? ces ressources ne peuvent être utilisées qu'à la fois Appartenant à un processus (thread)
. Lorsque la valeur du sémaphore est positive, cela signifie qu'il est inactif. Le thread testé peut se verrouiller lors de son utilisation. S'il vaut 0, cela signifie qu'il est occupé, et le thread de test entrera dans la file d'attente de veille et attendra d'être réveillé.
Linux propose deux types de sémaphores :
(1) Les sémaphores du noyau, utilisés par le chemin de contrôle du noyau
(2) Les sémaphores utilisés par les processus en mode utilisateur, qui sont divisés en sémaphores POSIX et sémaphore SYSTEM
V .
Les sémaphores POSIX sont divisés en sémaphores nommés et sémaphores sans nom.
Nommé sémaphore, sa valeur est enregistrée dans un fichier, il peut donc être utilisé pour les threads et pour la synchronisation entre processus. Sémaphore
sans nom dont la valeur est stockée en mémoire.
Sémaphore du noyau
Construction du sémaphore du noyau
Un sémaphore du noyau est similaire à un verrou tournant dans la mesure où lorsque le verrou est fermé, il ne permet pas au chemin de contrôle du noyau de continuer. Cependant,
Lorsque le chemin de contrôle du noyau tente d'acquérir la ressource occupée protégée par le verrou sémaphore du noyau, le processus correspondant est suspendu. Ce n'est que lorsque la ressource est libérée que le processus redevient exécutable.
Seules les fonctions qui peuvent dormir peuvent obtenir des sémaphores du noyau ; ni les gestionnaires d'interruption ni les fonctions différées ne peuvent utiliser les sémaphores du noyau.
Le sémaphore du noyau est un objet de type sémaphore struct Dans la routine ci-dessus
#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; }
, le thread qui s'applique en premier à la ressource sémaphore est aléatoire. Si vous souhaitez une commande spécifique, vous pouvez utiliser 2 sémaphores pour la réaliser. Par exemple, dans la routine suivante, le thread 1 termine son exécution en premier, puis le thread 2 continue de s'exécuter jusqu'à la fin.
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) Synchronisation de sémaphores sans nom entre des processus liés
On dit qu'il s'agit de processus liés car il y a deux processus dans ce programme, dont l'un est un processus enfant de l'autre (généré par
fork
) .
À l'origine pour fork, le processus enfant hérite uniquement de la copie de code du processus parent. Mutex doit être deux variables indépendantes dans les processus parent et enfant
Cependant, lors de l'initialisation de mutex, pshared = 1 fait référence à
Il est déterminé. ce mutex se trouve dans la zone de mémoire partagée, donc à ce moment-là, le mutex devient une variable partagée par les processus parent et enfant. À ce stade, le mutex peut être utilisé pour synchroniser les processus associés.
#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); }
La caractéristique d'un sémaphore nommé est de sauvegarder la valeur du sémaphore dans un fichier.
Cela détermine qu'il a un très large éventail d'utilisations : il peut être utilisé pour des threads, des processus associés et même des processus
non liés. (a) La raison pour laquelle un sémaphore nommé peut être partagé entre des processus
Puisque la valeur d'un sémaphore nommé est stockée dans un fichier, pour le processus associé, le processus enfant hérite du descripteur de fichier du processus parent
, alors le fichier pointé par le descripteur de fichier hérité par le processus enfant est le même que le processus parent. Bien entendu, la valeur du sémaphore nommée enregistrée dans le fichier est partagée.
(b) Description des fonctions liées aux sémaphores nommés
Lorsque le sémaphore nommé est utilisé, il partage les fonctions sem_wait et sem_post avec le sémaphore sans nom.
La différence est que le sémaphore nommé utilise sem_open au lieu de sem_init. De plus, à la fin, le sémaphore nommé doit être fermé comme un fichier
.
(1) Ouvrez un sémaphore nommé existant, ou créez et initialisez un sémaphore nommé. Un seul appel complète la création, l'initialisation et le paramétrage des autorisations du sémaphore.
sem_t *sem_open(const char *name, int oflag, mode_t mode, int value);
name est le nom du chemin du fichier
Oflag a deux valeurs : O_CREAT ou O_CREAT|EXCL ; new Autorisations d'accès du sémaphore ;
Value spécifie la valeur d'initialisation du sémaphore.
Remarque :
Le nom ici ne peut pas être écrit au format /tmp/aaa.sem, car sous Linux, sem est créé
dans le répertoire /dev/shm. Vous pouvez écrire le nom sous la forme "/mysem" ou "mysem", et les fichiers créés seront "/dev/shm/sem.mysem". N'écrivez pas le chemin. N'écrivez jamais non plus "/tmp/mysem" ou quelque chose comme ça.
Lorsque oflag = O_CREAT, si le sémaphore spécifié par nom n'existe pas, un sera créé et les paramètres
mode et valeur suivants doivent être valides. Si le sémaphore spécifié par name existe déjà, ouvrez le sémaphore directement et ignorez les paramètres mode et value.
Lorsque oflag = O_CREAT|O_EXCL, si le sémaphore spécifié par nom existe déjà, la fonction retournera directement
erreur.
(2) Une fois que vous utilisez des sémaphores, il est important de les détruire.
在做这个之前,要确定所有对这个有名信号量的引用都已经通过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教程
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!