Chapter 11 Inter-Process Communication (3) _ shared memory

3.3  Shared Memory

(1) Shared Memory Profile

  ① shared memory area is being shared by multiple processes are part of the physical memory

  ② multiple processes can be mapped to the shared memory to its own virtual memory space . All user processes shared memory space to operate, it must be mapped to its own virtual memory space, by the virtual memory address space mapping of shared memory to operate, so as to achieve data inter-process communication.

  ③ Shared memory is the fastest way to share data between processes, a process writes data to the shared memory region, all processes shared this memory area, you can see its contents immediately .

  ④ synchronization mechanism itself does not provide, can be synchronized by semaphores.

  ⑤ improve data processing efficiency, an inefficient highest IPC mechanism .

(2) Shared memory attribute structure

(3) the step of using the shared memory

  ① use shmget function to create a shared memory

  ② use shmat function mapping shared memory , shared memory mapping to create this specific process virtual memory space.

  ③ unmapped

  ④ remove shared memory

(4) create a shared memory, control, mapping and de-mapping

  ① create a shared memory

head File

#include <sys/shm.h>

function

int shmget(key_t key, size_t size, int shmflag);

parameter

Key : a user-specified shared memory key

size : shared memory size

shmflag : IPC_CREAT, and other combinations of privileges IPC_EXCL

Features

Create a shared memory

return value

Successful return kernel shared memory identifier ID, Error -1

errno:

(1) EINVAL (invalid memory segment size) (2) EEXIST (memory segment already exists, can not create)

(3) EIDRM (memory segment has been deleted) (4) ENOENT (memory segment does not exist)

(5) EACCESS (insufficient privileges) (6) ENOMEM (not enough memory to create memory segment)

  ② shared memory control

head File

#include <sys/shm.h>

function

int shmctl(int shmid, int cmd, struct shmid_ds buf);

parameter

(1) shmid: shared memory ID

(2) buf: a pointer property shared memory

(3)cmd:

  ①IPC_STAT: obtaining a shared memory segment attributes.

  ②IPC_SET: Set shared memory segment attributes

  ③IPC_RMID: Delete the shared memory segment

  ④SHM_LOCK: locking the shared memory segment page (page mapped to physical memory and the external memory is not performed swapped in and out of operation)

  ⑤SHM_UNLOCK: shared memory segment and face lift lock.

Features

Shared memory control

return value

Successful return kernel shared memory identifier ID, Error -1

  ③ shared memory mapping and address mapping

head File

#include <sys/shm.h>

function

void * shmat (int shmid, char * shmaddr, int shmflag); // maps, the successful return of the shared memory mapped into the process virtual memory address space, failure to return -1

int shmdt (char * shmaddr); // unmapped. Returns 0 on success, failure to return -1.

parameter

(1) shmid: Shared Memory ID

(2) shmaddr: mapped into the process virtual memory address . The recommended setting is 0, assigned by the operating system.

(3) shmflag: shmaddr If set to 0, then also set to 0 shmflag.

  ①SHM_RND: Random

  ②SHM_BA: Address for the square of 2

  ③SHM_RDONLY: read-only links

Remark

(1)errno:

  ①EINVAL (Invalid IPC ID or invalid address);

  ②ENOMEM (not enough memory)

  ③EACCESS(存取权限不够)

(2)子进程不继承父进程创建的共享内存,因为大家是共享的。子进程继承父进程映射的地址。

【编程实验】不同进程操作共享内存(使用管道来同步)

//tell.h

复制代码

#ifndef __TELL_H__
#define __TELL_H__

//管道初始化
extern void init();

//利用管道进行等待
extern void wait_pipe();

//利用管道进行通知
extern void notify_pipe();

//销毁管道
extern void destroy_pipe();

#endif

复制代码

//tell.c

复制代码

#include "tell.h"
#include <stdio.h>
#include <stdlib.h>

static int fd[2];  //保存管道的文件描述符

//管道初始化
void init()
{
    if(pipe(fd) < 0){
        perror("pipe error");
    }
}

//利用管道进行等待
void wait_pipe()
{
    char c;
    //管道读写默认是阻塞性的
    if(read(fd[0], &c, 1) < 0){
        perror("wait pipe error");
    }
}

//利用管道进行通知
void notify_pipe()
{
    char c = 'c';
    if(write(fd[1], &c, 1) != 1){
        perror("notify pipe error");
    }   
}

//销毁管道
void destroy_pipe()
{
    close(fd[0]);
    close(fd[1]);
}

复制代码

//cal_shm.c

复制代码

#include <unistd.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include "tell.h"

int main(void)
{
    //创建共享内存
    int shmid;
    if((shmid = shmget(IPC_PRIVATE, 1024, //大小为1024字节 
                      IPC_CREAT | IPC_EXCL | 0777)) < 0){
        perror("shmget error");
        exit(1);
    }

    pid_t  pid;
    init(); //初始化管道
    //创建子进程
    if((pid = fork()) < 0){
        perror("fork error");
        exit(1);
    }else if(pid > 0){  //parent process
        //进行共享内存的映射
        int* pi = (int*)shmat(shmid, 0, 0);
        if(pi == (int*)-1){
            perror("shmat error");
            exit(1);
        }

        //往共享内存中写入数据(通过地址即可操作!)
        *pi = 100;
        *(pi + 1) = 200;
        
        //操作完毕,解除映射
        shmdt(pi);
        
        //通知子进程到读取共享内存中的数据
        notify_pipe();
        
        destroy_pipe();
        wait(0);
        
        //删除共享内存
        shmctl(shmid, IPC_RMID, NULL);
    }else{ //child process
        //子进程阻塞,等待父进程先往共享内存中写入数据
        wait_pipe();
        
        /*子进程从共享内存中读取数据*/

        //子进程进行共享内存映射
        int* pi = (int*)shmat(shmid, 0, 0);
        if(pi == (int*)-1){
            perror("shmat error");
            exit(1);
        }
        
        printf("start: %d end: %d\n", *pi, *(pi + 1));

        shmdt(pi);

        destroy_pipe();
    }
}

复制代码

【编程实验】共享内存实现ATM(没有互斥、是不安全!)

(1)银行帐户创建在共享内存中(而不是原来的堆)

(2)改多线程为多进程程序。

//account.h

复制代码

#ifndef __ACCOUNT_H__
#define __ACCOUNT_H__

typedef struct
{
    int      code;    //帐号
    double   balance; //余额
}Account;

//取款
extern double withdraw(Account* a, double amt); //amt == amount
//存款
extern double deposit(Account* a, double amt);
//查看帐户余额
extern double get_balance(Account* a);

#endif  //__ACCOUNT_H__

复制代码

//account.c

复制代码

#include "account.h"
#include <string.h>
#include <assert.h>

//取款
double withdraw(Account* a, double amt) //amt == amount
{
    assert(a != NULL);

    if(amt < 0 || amt > a->balance){
        return 0.0;
    }

    double balance = a->balance; //先取余额
    sleep(1); //为模拟进程下可能出现的问题

    balance -= amt;
    a->balance = balance; //更新余额。在读取余额和更新余额之间有
                          //故意留出“时间窗口”。
    
    return amt;    
}

//存款
double deposit(Account* a, double amt)
{
    assert(a != NULL);

    if(amt < 0){
        return 0.0;
    }

    double balance = a->balance; //先取余额

    sleep(1); //为模拟多进程下可能出现的问题

    balance += amt;
    a->balance = balance; //更新余额。
    
    return amt;    
}

//查看帐户余额
double get_balance(Account* a)
{
    assert(a != NULL);
    
    double balance = a->balance;
 
    return balance;
}

复制代码

//account_test.c

复制代码

#include "account.h"
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/shm.h>

int main(void)
{
    //在共享内存中创建银行帐户
    int shmid;
    if((shmid = shmget(IPC_PRIVATE, sizeof(Account), IPC_CREAT | IPC_EXCL | 0777)) < 0){
        perror("shmget error");
        exit(1);
    }

    //进程共享内存映射(a为返回的映射地址)
    Account* a= (Account*)shmat(shmid, 0, 0);
    if(a == (Account*)-1){
        perror("shmat error");
        exit(1);
    }

    //银行帐户初始化
    a->code = 100001;
    a->balance = 10000;
    printf("balance: %f\n", a->balance);
    
    //父子进程都进行取款
    pid_t pid;
    if((pid = fork()) < 0){
        perror("fork error");
        exit(1);
    }else if(pid > 0){ //parent process
        //父进程进行取款操作
        double amt = withdraw(a, 10000);
        printf("pid %d withdraw %f from code %d\n", getpid(), amt, a->code);
       
        //解除映射
        shmdt(a);

        wait(0);
        
        //删除共享内存区
        shmctl(shmid, IPC_RMID, NULL);

    }else{ //child process
        //子进程会继承父进程映射的共享内存地址
        //子进程进行取款操作
        double amt = withdraw(a, 10000);
        printf("pid %d withdraw %f from code %d\n", getpid(), amt, a->code);

        //解除映射
        shmdt(a);
    }

    return 0;
}
/*输出结果:
 balance: 10000.000000
 pid 1939 withdraw 10000.000000 from code 100001  
 pid 1940 withdraw 10000.000000 from code 100001 //不安全!(可用信号量解决)
 */

地址:https://www.cnblogs.com/5iedu/p/6588876.html

发布了25 篇原创文章 · 获赞 4 · 访问量 2万+

Guess you like

Origin blog.csdn.net/zhou8400/article/details/97623706