Table of contents
1. System V shared memory - first let different processes see the same resource
(1) Where does the shared memory exist?
(2) Explanation of the first parameter key
(3) The key is generally explained by the user
(4) The function ftok gets the key
4. Detailed explanation of ipcs and ipcrm commands
(1) ipcs checks the usage of message queue, shared memory and semaphore
(2) ipcrm (administrator) removes message queues, shared memory, and semaphores
4. Delete the system interface shmctl of shared memory
6. How do I know whether this shared memory exists or not?
7. Let the process see the code of the same resource
1. The phenomenon of shared memory usage
(1) No access control when using shared memory - no blocking and waiting
(2) Shared memory is the fastest communication between all processes! explain
2. Pipeline access control code for shared memory
3. Critical resources, critical sections
3. Signal volume, Signal light
1. System V shared memory - first let different processes see the same resource
The premise of inter-process communication is: let different processes see the same resource first!
1. Shared memory principle
Monitor Shared Memory Script
while :; do ipcs -m; sleep 1; echo "############################";done
2. Create/get shared memory interface — shmget function (shared memory get)
①Create shared memory——②Delete shared memory
③Associate shared memory——④Deassociate shared memory
These operations are done for us inside the OS and associated with the process
shmget function:
Create a System V level shared memory segment
int shmget(key_t key, size_t size, int shmflg);
Return value: successfully returns the shared memory identifier shmid; error returns -1
① key: the unique value of the shared memory kernel (use the function ftok to get the key) for detailed explanation see 3.—>(2)(3)
② size: The size of the shared memory created. (It is recommended to set it as an integer multiple of the page (4KB), because the basic unit of operating system and disk IO is 4KB)
③ shmflg options:
IPC_CREAT: Create shared memory, get it if it already exists, create it if it does not exist
IPC_ EXCL : Not used alone, must cooperate with IPC_CREAT. If the specified shared memory does not exist, create it; if it exists, an error returns IPC_CREAT and IPC_CREAT cooperation: it can be guaranteed that if the shmget function is called successfully - it must
be a brand new share memory! Distinguish whether it is newly created or existing. If it is used together and returns successfully, it is a new shared memory; if it is unsuccessful, it already exists )
shmflg | 0666 : support XOR upper permissions
3. Parameter key explanation
(1) Where does the shared memory exist?
——In the kernel, the kernel will maintain the structure of shared memory for us! Shared memory should also be managed!! First describe, then organize!
(2) Explanation of the first parameter key
If you want to indicate the existence or non-existence, you must first have a method to identify the uniqueness of the shared memory! !——use the key to identify the shared memory to be
managed.
struct shmid_ ds{} contains a structure struct ipc_ perm, and this structure struct ipc_ perm contains Contains a member value called key (shmget) (the role of key: the unique value of shared memory!! This key is generally provided by the user)
(3) The key is generally explained by the user
The premise of inter-process communication is: let different processes see the same resource first. How to ensure that different processes see the same shared memory? ——The method is: let them have the same key! (Use the function ftok to get the key)
Named pipe --> agree to use the same file
to share memory --> agree to use the same unique key for communication!!
When we finish running the code to create a new shared memory (the process exits), but the second ( n) times, the code cannot run, telling us that the file exists, that is, the shared memory exists!
The life cycle of the shared memory under systen-V follows the kernel!!
If it is not displayed, it can only be deleted through the kernel (os) reboot to fix!
(4) The function ftok gets the key
Function: Convert a file path and project identifier into a unique number.
key_t ftok(const char *pathname, int proj_id);
Return value: successfully returns the generated key; fails to return -1
For example:
#define PATH_NAME "/home/whb/104"
#define PROJ_ID 0x14
key_t key = ftok(PATH_NAME, PROJ_ID) PROJ_ID just set one
4. Detailed explanation of ipcs and ipcrm commands
(1) View the command ipcs -m of the shared memory, delete the shared memory ipcrm -m (shmid of the shared memory to be deleted)
perm: permission; nattch: number of hooked processes
(1) ipcs
Check the usage of message queue , shared memory and semaphore
Order | Function |
---|---|
ipcs 或 ipcs -a | Check the usage of message queue, shared memory and semaphore |
ipcs -s | View semaphore (signal) |
ipcs -m | View shared memory (memory) |
ipcs -q | View message queue (queue) |
(2) ipcrm
(Administrator) Remove message queue, shared memory, semaphore
(3) ipcs
Output options
Order | Function |
---|---|
ipcs -t | Output detailed time changes |
ipcs -p | Output the process number of the ipc |
ipcs -c | Output the creator of ipc corresponding information |
ipcs -l | Output the limit information of the system ipc |
ipcs -u | Output various status information of the system ipc |
4. Delete the system interface shmctl of shared memory
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid: Shared memory identifier, indicating which shared memory to delete.
cmd: The option has the commonly used IPC_RMID (delete immediately, at this time buf can be deleted by passing nullptr) (there are also IPC_SET settings, IPC_STAT copy options)
例如 shmctl(shmid, IPC_RMID, nullptr);
5. Associated function shmat
shmat (attach-attach), shmdt (detach-disassemble)
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmid: shared memory identifier. shmaddr: Set where in the address space, now set to nullptr. shmflg: read and write, now set to 0
Return value: successfully returns the shared memory address of the mount, and returns (void*)-1 if an error occurs.
The return value has the same meaning as malloc, how you use the malloc space, you can use the shared memory space! (Definitely not free)
例如:char *str = (char *)shmat(shmid, nullptr, 0);
————————————————————————
shmdt (detach - disassemble)
int shmdt(const void *shmaddr);
shmaddr: pass the return value of shmat . can be associated
6. How do I know whether this shared memory exists or not?
Just compare the key you have set with the key you want to set to confirm whether the shared memory exists.
7. Let the process see the code of the same resource
Log.hpp
#pragma once
#include <iostream>
#include <ctime>
std::ostream &Log()
{
std::cout << "Fot Debug |" << " timestamp: " << (uint64_t)time(nullptr) << " | ";
return std::cout;
}
Comm.hpp
(.hpp is a file in which declarations and definitions are written together)
#pragma once
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cerrno>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define PATH_NAME "/home/whb/104"
#define PROJ_ID 0x14
#define MEM_SIZE 4096
key_t CreateKey()
{
key_t key = ftok(PATH_NAME, PROJ_ID);
if(key < 0)
{
std::cerr <<"ftok: "<< strerror(errno) << std::endl;
exit(1);
}
return key;
}
Makefile
.PHONY:all
all: IpcShmCli IpcShmSer
IpcShmCli:IpcShmCli.cc
g++ -o $@ $^ -std=c++11
IpcShmSer:IpcShmSer.cc
g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -f IpcShmCli IpcShmSer
lpcShmCli.cc
#include "Comm.hpp"
#include "Log.hpp"
#include <unistd.h>
using namespace std;
// 充当使用共享内存的角色
int main()
{
// 创建相同的key值
key_t key = CreateKey();
Log() << "key: " << key << "\n";
// 获取共享内存
int shmid = shmget(key, MEM_SIZE, IPC_CREAT);
if (shmid < 0)
{
Log() << "shmget: " << strerror(errno) << "\n";
return 2;
}
// 挂接
char *str = (char*)shmat(shmid, nullptr, 0);
// 用它
sleep(5);
// 去关联
shmdt(str);
return 0;
}
IpcShmSer.cc
#include "Comm.hpp"
#include "Log.hpp"
#include <unistd.h>
using namespace std;
// 我想创建全新的共享内存
const int flags = IPC_CREAT | IPC_EXCL;
// 充当使用共享内存的角色
int main()
{
key_t key = CreateKey();
Log() << "key: " << key << "\n";
Log() << "create share memory begin\n";
int shmid = shmget(key, MEM_SIZE, flags | 0666);
if (shmid < 0)
{
Log() << "shmget: " << strerror(errno) << "\n";
return 2;
}
Log() << "create shm success, shmid: " << shmid << "\n";
sleep(5);
// 1. 将共享内存和自己的进程产生关联attach
char *str = (char *)shmat(shmid, nullptr, 0);
Log() << "attach shm : " << shmid << " success\n";
sleep(5);
// 用它
// 2. 去关联
shmdt(str);
Log() << "detach shm : " << shmid << " success\n";
sleep(5);
// 删它
shmctl(shmid, IPC_RMID, nullptr);
Log() << "delete shm : " << shmid << " success\n";
sleep(5);
return 0;
}
2. Use of shared memory
1. The phenomenon of shared memory usage
(1) No access control when using shared memory - no blocking and waiting
We actually map the shared memory to the user space of our process address space (between the heap and the stack). For each process, the shared memory attached to its own context belongs to its own space, similar to heap space or stack space, and can be directly used by users without calling the system interface.
Shared memory, because of its own characteristics, it does not have any access control , shared memory is directly seen by both parties, belongs to the user space of both parties, and can communicate directly, but it is not safe!
(2) Shared memory is the fastest communication between all processes! explain
Pipeline: The parent process writes (copy) the data of the peripheral device into the context code of its own process, then writes (copy) the context code of its own process into the pipeline file, and reads the data in the pipeline file of the child process into ( Copy) into the sub-process context code, and then brush (copy) the sub-process context code to the peripheral device, and 4 copies are required ;
And shared memory: When process 1 writes (copy) the data of the peripheral device into the shared memory, process 2 can see it immediately and use the data of the shared memory. If it is not flashed to the peripheral device, it only needs to be copied once , so shared memory is the fastest
2. Pipeline access control code for shared memory
An access control effect based on shared memory + pipeline!
Log.hpp
#pragma once
#include <iostream>
#include <ctime>
std::ostream &Log()
{
std::cout << "Fot Debug |" << " timestamp: " << (uint64_t)time(nullptr) << " | ";
return std::cout;
}
Makefile
.PHONY:all
all: IpcShmCli IpcShmSer
IpcShmCli:IpcShmCli.cc
g++ -o $@ $^ -std=c++11
IpcShmSer:IpcShmSer.cc
g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -f IpcShmCli IpcShmSer
Comm.hpp
#pragma once
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cerrno>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "Log.hpp"
#define PATH_NAME "/home/whb/104"
#define PROJ_ID 0x14
#define MEM_SIZE 4096
#define FIFO_FILE ".fifo"
key_t CreateKey()
{
key_t key = ftok(PATH_NAME, PROJ_ID);
if(key < 0)
{
std::cerr <<"ftok: "<< strerror(errno) << std::endl;
exit(1);
}
return key;
}
void CreateFifo()
{
umask(0);
if(mkfifo(FIFO_FILE, 0666) < 0)
{
Log() << strerror(errno) << "\n";
exit(2);
}
}
#define READER O_RDONLY
#define WRITER O_WRONLY
int Open(const std::string &filename, int flags)
{
return open(filename.c_str(), flags);
}
int Wait(int fd)
{
uint32_t values = 0;
ssize_t s = read(fd, &values, sizeof(values));
return s;
}
int Signal(int fd)
{
uint32_t cmd = 1;
write(fd, &cmd, sizeof(cmd));
}
int Close(int fd, const std::string filename)
{
close(fd);
unlink(filename.c_str());
}
IpcShmSer.cc
#include "Comm.hpp"
#include "Log.hpp"
#include <unistd.h>
using namespace std;
// 我想创建全新的共享内存
const int flags = IPC_CREAT | IPC_EXCL;
// 充当使用共享内存的角色
int main()
{
CreateFifo();
int fd = Open(FIFO_FILE, READER);
assert(fd >= 0);
key_t key = CreateKey();
Log() << "key: " << key << "\n";
Log() << "create share memory begin\n";
int shmid = shmget(key, MEM_SIZE, flags | 0666);
if (shmid < 0)
{
Log() << "shmget: " << strerror(errno) << "\n";
return 2;
}
Log() << "create shm success, shmid: " << shmid << "\n";
// sleep(5);
// 1. 将共享内存和自己的进程产生关联attach
char *str = (char *)shmat(shmid, nullptr, 0);
Log() << "attach shm : " << shmid << " success\n";
// sleep(5);
// 用它
while(true)
{
// 让读端进行等待
if(Wait(fd) <= 0) break;
printf("%s\n", str);
sleep(1);
}
// 2. 去关联
shmdt(str);
Log() << "detach shm : " << shmid << " success\n";
// sleep(5);
// 删它
shmctl(shmid, IPC_RMID, nullptr);
Log() << "delete shm : " << shmid << " success\n";
Close(fd, FIFO_FILE);
// sleep(5);
return 0;
}
IpcShmCli.cc
#include "Comm.hpp"
#include "Log.hpp"
#include <cstdio>
#include <unistd.h>
using namespace std;
// 充当使用共享内存的角色
int main()
{
int fd = Open(FIFO_FILE, WRITER);
// 创建相同的key值
key_t key = CreateKey();
Log() << "key: " << key << "\n";
// 获取共享内存
int shmid = shmget(key, MEM_SIZE, IPC_CREAT);
if (shmid < 0)
{
Log() << "shmget: " << strerror(errno) << "\n";
return 2;
}
// 挂接
char *str = (char*)shmat(shmid, nullptr, 0);
// 用它
// sleep(5);
// 竟然没有使用任何的系统调用接口!
// str
while(true)
{
printf("Please Enter# ");
fflush(stdout);
ssize_t s = read(0, str, MEM_SIZE);
if(s > 0)
{
str[s] = '\0';
}
Signal(fd);
}
// int cnt = 0;
// while(cnt <= 26)
// {
// str[cnt] = 'A' + cnt;
// ++cnt;
// str[cnt] = '\0';
// sleep(5);
// }
// 去关联
shmdt(str);
return 0;
}
3. Critical resources, critical sections
(1) Critical resources
Common resources that can be seen by multiple processes are called critical resources . (Both pipelines and shared memory are called critical resources , but pipelines are safe, and shared memory is not).
If there is no protection for critical resources, access to critical resources will be chaotic when both processes are accessing them. In sequence, there may be various garbled characters, discarded data, and other access control problems caused by cross-reading and reading. (For example, any printf of the parent-child process, because the monitor is the critical resource of the parent-child process)
(2) Critical section
For multiple processes, there is a large amount of code in the process code, and only a part of the code will access critical resources. This part of the code that accesses critical resources is called a critical
area. We
put one thing, or do not do it, or do it It's over --- Atomicity
No intermediate state
At any time, only one process is allowed to access critical resources
Atomicity : We either don’t do one thing or finish it – there is no intermediate state Mutual
exclusion : At any time, only one process is allowed to access critical resources
3. Signal volume, Signal light
1. Signal amount
A semaphore is essentially a counter. Definition: A semaphore is a counter, and the PV operation (V ++ P --) corresponding to this counter is atomic. PV operation of semaphore: V++ returns resources, P -- applies for resources
How do you prove that a specific seat in the auditorium is yours? ——As long as I buy a ticket, this seat is mine.
Watching a movie is an analogy, assuming that a screening hall (critical resource) has 100 seats (the seat is a small piece of critical resource), 100 seats correspond to 100 tickets, counted by cnt: cnt=100, if(cnt <= 0 ) wait ; else return cnt--; sell a ticket, cnt--, wait when sold out. This cnt is similar to a semaphore.
Binary semaphore : A semaphore that can only be 0 or 1 is called a binary semaphore. When it is 1, it shows the mutual exclusion feature. ——Compared to watching a movie. At this time, there is only one seat in a projection hall (critical resource), and only one person (process) can enter. When a person enters and sits in this seat to watch a movie, this behavior is mutually exclusive access.
Conventional semaphore: multiple semaphore
When multiple people watch a movie, they don’t just rush into the auditorium to grab seats, but to grab tickets -> the essence is to preempt the semaphore (int cnt = 100) Anyone who wants to watch a movie - must first apply for the
counter cnt - if the application If you succeed, you will be able to watch the movie.
Any process that wants to access critical resources - must apply for a semaphore - if the application is successful, it must be able to access some of the critical resources [semaphore (1,0) mutually exclusive]
How do you prove that a specific seat in the auditorium is yours? ——As long as I buy a ticket, this seat is mine.
2. Semaphore operation
sem: -- : Apply for resources: P
sem: ++: Release resources:
The operation corresponding to the V semaphore is a PV operation. PV operations are atomic
Shared memory does not do access control, and resources can be protected through semaphores!
Create Release Corresponding operation View Delete
shmget: create shared memory shmctl shmat, shmdt ipcs -m ipcrm -m
msgget: create message queue msgctl msgsnd, msgrcv ipcs -q ipcrm -q
semget: create semaphore semctl semop -> +1 -1 PV ipcs -s ipcrm -s
The address of the first element of the structure is numerically the same size as the address of the structure as a whole!
3. Semaphore allows multiple threads to use shared resources at the same time
The semaphore is mainly used to implement synchronous operations, as long as the number of resources is greater than 0, it means that it can be acquired and accessed. If you want to use semaphore simulation to achieve mutual exclusion, you need to initialize the resource count to 1, which means that there is only one resource, and only one execution flow can access it
(2) Which of the following methods can be used to implement inter-thread notification and wake-up: ( ) [multiple choice]
contents of homework
A. Mutex lock
B. Condition variable
C. Semaphore
D. Read-write lock
Answer: Notification and wake-up between threads and thread waiting are the basis for synchronization between threads, and semaphores and condition variables are used to achieve synchronization between threads by providing thread waiting and wake-up functions, so choose B and C
The mutex lock in A and the read-write lock in D are all lock technologies for realizing safe access to shared resources, and do not include the functions of notifying and waking up threads.
(3) What is the difference between semaphore implementation and condition variable [multiple choice]
A. The semaphore can realize both synchronization and mutual exclusion
B. Condition variables can achieve both synchronization and mutual exclusion
C. Condition variables need to be used with mutexes, semaphores do not
D. The semaphore needs to be used with a mutex, and the condition variable does not need to
- The condition variable provides a pcb blocking queue and an interface for blocking and waking up threads to achieve synchronization, but when to wake up and when to block the thread is controlled by the programmer, and this control usually requires a conditional judgment of a shared resource to complete. Therefore, the condition variable also needs to be used with a mutex to protect the conditional judgment and operation of this shared resource.
- The semaphore provides a pcb waiting queue, and a counter that implements atomic operations to count resources. The condition judgment of synchronization is realized through its own counter, so it does not need to be used with a mutex, and the semaphore is initialized with a count of 1. The mutual exclusion operation can also be simulated below.