1. Introducción a los semáforos y conjuntos de semáforos
1. Se utiliza para la exclusión mutua y la sincronización entre procesos para controlar el acceso a los recursos compartidos.
2. Para facilitar el funcionamiento de una gran cantidad de recursos compartidos, se introduce un conjunto de semáforos y cada recurso compartido corresponde a un semáforo.
3. Operaciones de semáforo: operación P (restar el semáforo) y operación V (agregar al semáforo).
Dos API relacionadas con el semáforo
1. Crea un conjunto de semáforos
int semget (clave key_t, int nsems, int flag);
parámetro:
clave: el valor clave del conjunto de semáforos especificado por el usuario
nsems: el número de semáforos en el conjunto de semáforos
bandera: una combinación de IPC_CREAT, IPC_EXCL y otros permisos.
Retorno: Devuelve el ID del conjunto de semáforos en caso de éxito, devuelve -1 en caso de error
2. Control de conjunto de semáforos
int semctl (int semid, int semnum, int cmd, ... / * union semnu arg * /);
union semnu {
int val;
struct semid_ds * buf;
corto sin firmar * arry;
};
parámetro:
semid: ID de conjunto de semáforos
semnu: 0 significa operar en todos los semáforos, el número de semáforo comienza desde 0
val: Obtiene o el valor de un semáforo en el conjunto de semáforos
buf: puntero de atributo de conjunto de semáforo
arry: Obtiene o establece el valor de todos los semáforos en el conjunto de semáforos.
cmd:
3. Operación de conjunto de semáforos (operaciones de suma y resta en el semáforo en el conjunto de semáforos (operación PV))
int semop (int semid, struct sembuf * sops, size_t nsops);
struct sembuf {
sem_num corto sin firmar;
sem_op corto;
sem_flag corto;
};
parámetro:
semid: ID de conjunto de semáforos
sops: puntero de matriz de estructura sembuf
nsops: el número de semáforos en sops, sizeof (sops) / sizeof (sops [0])
sem_num: el número del semáforo en el conjunto de semáforos
sep_op: el número positivo es la operación V (sumar la operación al semáforo), el número negativo es la operación P (restar el semáforo)
sem_flag: generalmente SEM_UNDO
regreso:
Devuelve 0 en caso de éxito; devuelve -1 en caso de error.
Tres, prueba de código
Tarea: Cree una memoria compartida y dos procesos P1 y P2. P1 escribe datos en la memoria compartida. Después de escribir, P2 lee datos de la memoria compartida. Después de leer P2, P1 continúa escribiendo y luego se repite.
el código se muestra a continuación:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
typedef union {
int val;
struct semid_ds *buf;
unsigned short* arry;
}semnn_u;
typedef struct
{
int val;
int semid;
}Storage_t;//共享内存的数据结构
int create_sem(Storage_t * s)
{
//创建两个信号量
s->semid = semget(IPC_PRIVATE,2,IPC_CREAT|IPC_EXCL|0777);
if(s->semid < 0)
{
printf("semget error\n");
return -1;
}
//对信号量进行初始化,都初始化为0
semnn_u nu;
unsigned short val[2] = {0,0};
nu.arry = val;
if(semctl(s->semid,2,SETALL,nu) < 0)
{
printf("semctl error\n");
return -1;
}
return 0;
}
void destory_sem(Storage_t *s)
{
//销毁2个信号量
if(semctl(s->semid,2,IPC_RMID,NULL) < 0)
{
printf("destory error\n");
}
}
void writer(Storage_t *s ,int val)
{
//将数据写入共享内存
s->val = val;
printf("writer:%d,pid:%d\n",val,getpid());
//对S1做V操作
//s1的sem_num是0
struct sembuf sem_op_v = {0,1,SEM_UNDO};
if(semop(s->semid,&sem_op_v,1) < 0)
{
printf("sem op error 1\n");
}
//对S2做P操作
//s2的sem_nu是1
struct sembuf sem_op_p = {1,-1,SEM_UNDO};
if(semop(s->semid,&sem_op_p,1) < 0)
{
printf("sem op error 2\n");
}
}
void reader(Storage_t * s)
{
//先对S1做P操作
struct sembuf sem_op_p = {0,-1,SEM_UNDO};
if(semop(s->semid,&sem_op_p,1) < 0)
{
printf("reader semop error 1\n");
}
//对共享内存数据进行读操作
printf("reader: %d,pid:%d\n",s->val,getpid());
//对S2做V操作
struct sembuf sem_op_v = {1,1,SEM_UNDO};
if(semop(s->semid,&sem_op_v,1) < 0)
{
printf("reader semop error 2\n");
}
}
int main(void)
{
//创建共享内存
pid_t pid ;
int shmid = shmget(IPC_PRIVATE,sizeof(Storage_t),IPC_CREAT|IPC_EXCL|0777);
Storage_t *p;
//将共享内存映射到本进程中
p = (Storage_t *)shmat(shmid,0,0);
if(p == (Storage_t *) -1)
{
printf("shmat error\n");
return -1;
}
if(shmid < 0)
{
printf("shmget error\n");
return -1;
}
//创建信号量
create_sem(p);
pid = fork();
if(pid > 0)
{
//parent process
printf("parent pid :%d\n",getpid());
//向共享内存中写20次
for(int i = 0; i < 20;i ++)
{
writer(p,i);
}
wait(0);//等待回收子进程
destory_sem(p); //销毁信号量
shmdt(p); //解除共享内存映射
shmctl(shmid,IPC_RMID,NULL);//销毁共享内存
}
else if(pid == 0)
{
printf("child pid:%d\n",getpid());
//从共享内存中读20次
for(int i = 0;i < 20;i ++)
{
reader(p);
}
shmdt(p);
}
return 0;
}
Resultados de la prueba: