Yesterday, we mentioned that we need to give you a demo of shared memory, but we need to know that shared memory requires read and write control, so let's first introduce the semaphores mentioned earlier. Read carefully and correct mistakes.
So what is a semaphore?
The use of semaphores is mainly used to protect shared resources so that resources have only one process (thread) at a time.
Owned. When the value of the semaphore is positive, it is idle. The thread under test can be locked and used. If it is 0, it means that it is occupied, and the test thread will enter the sleep queue, waiting to be awakened.
Linux provides two semaphores:
(1) Kernel semaphores, used by the kernel control path
(2) The semaphores used by user-mode processes, which are further divided into POSIX semaphores and SYSTEM
V semaphore.
POSIX semaphores are divided into named semaphores and anonymous semaphores.
A named semaphore whose value is stored in a file, so it can be used for threads or for inter-process synchronization. Unknown
A semaphore whose value is stored in memory.
Kernel semaphores
. Composition of Kernel Signals
Kernel semaphores are similar to spin locks because they do not allow the kernel control path to continue when the lock is closed. However,
When the kernel control path attempts to obtain the busy resources protected by the kernel semaphore lock, the corresponding process is suspended. Only when resources are released can processes become operational again.
Kernel semaphores can only be obtained by functions that can sleep; interrupt handlers and deferred functions cannot be used inside
Nuclear semaphores.
Kernel semaphores are struct semaphore-type objects that are
#include <pthread.h>
#include <semaphore.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int number; // Protected global variables
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;
}
The above routine, which thread first applies to semaphore resources, is random. If you want a particular order
Ordering, it can be achieved by two semaphores. For example, the following routine is that thread 1 executes first, then thread 2 succeeds.
Continue execution until the end.
int number; // protected global variables
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); // idle
Sem_init(& sem_id2, 0, 0); //busy
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) Synchronization of nameless semaphores between related processes
The reason for this is that there are two processes in this program, one of which is another sub-process.
fork
Produced).
Originally, for fork, the child process inherited only a copy of the code of the parent process, and mutex was supposed to be the parent-child process.
The two variables are independent of each other, but when mutex is initialized, pshared = 1 means
Mutex is located in the shared memory area, so mutex becomes a change of parent-child process sharing.
Quantity. At this point, mutex can be used to synchronize related processes.
#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);
}
2. Named semaphores
The characteristic of a named semaphore is to store the value of the semaphore in a file.
This determines that it can be used for a wide range of purposes: threads, related processes, or even irrelevant.
Process.
(a) Reasons why well-known semaphores can be shared between processes
Because the value of a named semaphore is stored in a file, the child process inherits the parent for the related process.
The file descriptor of the process, then the file descriptor inherited by the child process refers to the same file as the parent process.
However, the named semaphore values stored in the file are shared.
(b) Description of the correlation function of a named semaphore
When a named semaphore is in use, it shares sem_wait and sem_post functions with an anonymous semaphore.
The difference is that a named semaphore uses sem_open instead of sem_init, and it ends like closing a file.
Turn off this famous semaphore as well.
(1) Open an existing named semaphore, or create and initialize a named semaphore. A single call is over.
It becomes the setting of semaphore creation, initialization and privileges.
sem_t *sem_open(const char *name, int oflag, mode_t mode , int value);
Name is the path name of the file;
Oflag has two values: O_CREAT or O_CREAT|EXCL.
mode_t controls access to new semaphores;
Value specifies the initialization value of the semaphore.
Be careful:
The name here cannot be written in a format like / tmp/aaa.sem, because under linux, SEMS are created
In the / dev/shm directory. You can write the name as "/ mysem" or "mysem" and create all the files.
It's "/ dev/shm/sem.mysem." Never write paths. Never write "/ tmp/mysem" or anything like that.
When oflag = O_CREAT, if the semaphore specified by name does not exist, a semaphore is created, and
The mode l and value parameters of the surface must be valid. If the semaphore specified by name already exists, the semaphore is opened directly.
The mode l and value parameters are ignored at the same time.
When oflag = O_CREAT|O_EXCL, the function returns directly if the semaphore specified by name already exists
Back to error.
(2) Once you use semaphores, it becomes important to destroy them.
Before doing this, make sure that all references to this famous semaphore have passed through the sem_close() function
Closed, and then just call sem_unlink() in the exit or exit handler to delete the semaphores in the system.
Note that if any processor or thread references this semaphore, the sem_unlink() function will not do anything.
Use.
That is to say, the sem_unlick must be executed by the last process using the semaphore. Because each
The signal lamp has a reference counter to record the current number of times it is turned on, and sem_unlink has to wait for the number to be zero before it can turn it on.
The semaphore referred to by name is deleted from the file system. That is to wait for the last sem_close to occur.
(c) Synchronization of named semaphores between uncorrelated processes
As mentioned earlier, a named semaphore is located in the shared memory area, so the resources it protects must also be located in the shared memory area.
Shared memory area, only in this way can it be shared by unrelated processes.
In the following example, both the service process and the client process use shmget and shmat to obtain a shared share
Save resources. Then the shared memory resource is mutually exclusive protected by using a named semaphore.
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 semaphore
This is a set of semaphore values, not a single semaphore. The associated semaphore operation functions are
#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]; //To use two semaphores, define two operational arrays
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);
}
//Getting the original value of 0th semaphore
rslt = semctl(semid, 0, GETVAL);
printf("val = %d\n",rslt);
//Initialize the 0th semaphore, then read it, and check if the initialization is successful
arg.val = 1; // Only one occupier is allowed at the same time
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); //Apply for 0th semaphore, try locking
if (rslt < 0 )
{
printf("semop failed. errno: %d\n", errno);
exit(0);
}
//You can lock resources here
sops[0].sem_op = 1;
semop(semid, sops, 1); //Release 0th semaphore
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);
}
Seventh. Small Scalpel Trial of Signal Quantity: Problems of Producers and Consumers
1. Problem description:
A buffer pool of length N is shared by both producers and consumers, so long as the buffer pool is not full, the producer can
Messages are sent to the buffer pool; as long as the buffer pool is not empty, consumers can take a message from the buffer pool. Producer to Buffer Pool
When placing information, consumers cannot operate buffer pools, and vice versa.
2. Using multithreading and semaphore to solve the mutual exclusion of the classical problem
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
#define BUFF_SIZE 10
char buffer[BUFF_SIZE];
char count; // Number of messages in the buffer pool
sem_t sem_mutex; // Mutual exclusion between producers and consumers
sem_t p_sem_mutex; // When empty, no access to consumers
sem_t c_sem_mutex; // When it's full, it's impossible for producers to enter.
void * Producer()
{
while(1)
{
sem_wait(&p_sem_mutex); //When the buffer pool is not full
sem_wait(&sem_mutex); //Waiting for the buffer pool to be idle
count++;
sem_post(&sem_mutex);
if(count < BUFF_SIZE)//Buffer pool not full
sem_post(&p_sem_mutex);
if(count > 0) //Buffer pool is not empty
sem_post(&c_sem_mutex);
}
}
void * Consumer()
{
while(1)
{
sem_wait(&c_sem_mutex);//Buffer pool not empty
sem_wait(&sem_mutex); //Waiting for the buffer pool to be idle
count--;
sem_post(&sem_mutex);
if(count > 0)
sem_post(c_sem_nutex);
}
}
int main()
{
pthread_t ptid,ctid;
//initialize the semaphores
sem_init(&empty_sem_mutex,0,1);
sem_init(&full_sem_mutex,0,0);
//creating producer and consumer threads
if(pthread_create(&ptid, NULL,Producer, NULL))
{
printf("\n ERROR creating thread 1");
exit(1);
}
if(pthread_create(&ctid, NULL,Consumer, NULL))
{
printf("\n ERROR creating thread 2");
exit(1);
}
if(pthread_join(ptid, NULL)) /* wait for the producer to finish */
{
printf("\n ERROR joining thread");
exit(1);
}
if(pthread_join(ctid, NULL)) /* wait for consumer to finish */
{
printf("\n ERROR joining thread");
exit(1);
}
sem_destroy(&empty_sem_mutex);
sem_destroy(&full_sem_mutex);
//exit the main thread
pthread_exit(NULL);
return 1;
}