Linux article [9]: Inter-process communication (shared memory) - <sequence>

Table of contents

1. System V shared memory - first let different processes see the same resource

1. Shared memory principle

Monitor Shared Memory Script

2. Create/get shared memory interface—shmget function (shared memory get)

3. Parameter key explanation

(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) View the command ipcs -m of the shared memory, delete the shared memory ipcrm -m (shmid of the shared memory to be deleted)

(1) ipcs checks the usage of message queue, shared memory and semaphore

(2) ipcrm (administrator) removes message queues, shared memory, and semaphores

 (3) ipcs output options

4. Delete the system interface shmctl of shared memory

5. Associated function shmat

6. How do I know whether this shared memory exists or not?

7. Let the process see the code of the same resource

Log.hpp

Comm.hpp

 Makefile

lpcShmCli.cc

IpcShmSer.cc

2. Use of shared memory

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

 Log.hpp

Makefile

Comm.hpp

IpcShmSer.cc

IpcShmCli.cc

3. Critical resources, critical sections

(1) Critical resources

(2) Critical section

3. Signal volume, Signal light

1. Signal amount

2. Semaphore operation


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

Principle: Create shared memory and attach the shared memory to their respective process contexts: create a space in physical memory through some system interfaces, and map it to the respective address spaces of process 1 and process 2 through the page table corresponding to the process. Then return the address of the process address space of the two processes to the user

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) ipcsCheck 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) ipcsOutput 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

Note: shared memory is not synchronized and mutually exclusive

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.

Guess you like

Origin blog.csdn.net/zhang_si_hang/article/details/127752019