제 11 장 프로세스 간 통신 (3) _ 공유 메모리

3.3  공유 메모리

(1) 공유 메모리 프로필

  ① 공유 메모리 영역이되는 여러 프로세스에 의해 공유되는 물리적 메모리의 일부

  ② 여러 프로세스는 자신의 가상 메모리 공간을 공유 메모리에 매핑 할 수 있습니다 . 동작하는 메모리 공간을 공유하는 모든 사용자 프로세스는 그것의 데이터를 프로세스 간 통신을 달성 시키도록, 동작하는 공유 메모리의 가상 메모리 주소 공간에 매핑하여, 자신의 가상 메모리 공간에 매핑되어야한다.

  ③ 공유 메모리는 프로세스간에 데이터를 공유하는 가장 빠른 방법입니다 프로세스가 공유 메모리 영역에 데이터가 모든 프로세스는, 즉시 그 내용을 볼 수있는이 메모리 영역을 공유 씁니다 .

  자체는 제공하지 않습니다 ④ 동기화 메커니즘은 세마포어에 의해 동기화 할 수 있습니다.

  데이터 처리 효율을 향상 ⑤ 비효율적 높은 IPC기구 .

(2) 공유 메모리 속성 구조

(3) 상기 공유 메모리를 사용하는 단계

  ① 사용하는 shmget 기능은 공유 메모리를 만들 수 있습니다

  ② 사용 시 shmat 기능 매핑 공유 메모리 , 공유 메모리 매핑이 특정 프로세스의 가상 메모리 공간을 만듭니다.

  ③ 매핑되지 않은

  ④ 공유 메모리를 제거

(4)는, 공유 메모리, 제어부, 매핑 및 디 맵핑을 작성

  공유 메모리를 만들 ①

헤더

사용법 #include <SYS / shm.h>

기능

INT하는 shmget (된 key_t 키를 size_t 크기의 shmflag INT);

매개 변수

: 사용자가 지정한 공유 메모리 키

크기 : 공유 메모리 크기

shmflag : IPC_CREAT 및 권한의 다른 조합 IPC_EXCL

기능

공유 메모리를 생성

반환 값

성공적인 복귀 커널 공유 메모리 식별자 ID, 오류 -1

errno를 :

(1) EINVAL (잘못된 메모리 세그먼트 크기) (2) EEXIST (메모리 영역이 이미 작성 불가)

(3) EIDRM (4) ENOENT (메모리 영역이 존재하지 않음) (메모리 영역이 삭제 된)

(5) EACCESS (있는 권한) (6) ENOMEM (충분한 메모리가 메모리 세그먼트 생성)

  ② 공유 메모리 제어

헤더

사용법 #include <SYS / shm.h>

기능

INT 설명 shmctl (INT shmid가, INT cmd를 구조체의 shmid_ds BUF);

매개 변수

(1) shmid가 : 공유 메모리 ID

(2) 버피는 : 포인터 속성은 공유 메모리

(3) cmd를

  ①IPC_STAT는 : 공유 메모리 세그먼트의 특성을 얻을 수있다.

  ②IPC_SET : 설정 공유 메모리 세그먼트 속성

  ③IPC_RMID는 : 공유 메모리 세그먼트를 삭제

  ④SHM_LOCK : 공유 메모리 세그먼트 페이지 (페이지가 물리적 메모리에 맵핑하고, 외부 메모리와 동작에서 교환이 수행되지 않은) 잠금

  ⑤SHM_UNLOCK : 공유 메모리 세그먼트 및 얼굴 리프트 잠금.

기능

공유 메모리 제어

반환 값

성공적인 복귀 커널 공유 메모리 식별자 ID, 오류 -1

  ③ 공유 메모리 매핑 및 주소 매핑

헤더

사용법 #include <SYS / shm.h>

기능

보이드의 *는 shmat (INT의 shmid가 숯불 * 않은 shmaddr, INT의 shmflag) // 맵, 프로세스 가상 메모리 주소 공간에 매핑 공유 메모리의 성공적인 복귀 실패 반환 -1

INT시, shmdt (문자 * 않은 shmaddr); // 매핑되지 않은. 성공 반환 0, 실패 반환 -1.

매개 변수

(1) shmid가 : 공유 메모리 ID를

(2)는 않은 shmaddr : 프로세스 가상 메모리 주소로 매핑 . 권장 설정은 운영 체제에 의해 할당, 0입니다.

(3)는 shmflag : 않은 shmaddr 설정된 경우 0으로, 다음과 같은 shmflag 0으로 설정.

  ①SHM_RND : 무작위

  ②SHM_BA : 2의 제곱에 대한 주소

  ③SHM_RDONLY : 읽기 전용 링크

(1)의 errno :

  ①EINVAL (잘못된 IPC ID 또는 잘못된 주소);

  ②ENOMEM (메모리가 부족합니다)

  ③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万+

추천

출처blog.csdn.net/zhou8400/article/details/97623706