码迷,mamicode.com
首页 > 系统相关 > 详细

【linux高级程序设计】(第十一章)System V进程间通信 4

时间:2015-07-30 00:21:13      阅读:284      评论:0      收藏:0      [点我收藏+]

标签:

共享内存

共享内存主要用于实现进程间大量数据传输。

技术分享

 

共享内存的数据结构定义:

技术分享

系统对共享内存的限制:

技术分享

 

共享内存与管道的对比:

技术分享

可以看到,共享内存的优势:

1.共享内存只需复制2次,而管道需要4次

2.共享内存不需要切换内核态与用户态,而管道需要。

共享内存效率高!

 

int shmget (key_t __key, size_t __size, int __shmflg) :创建共享内存

第一个参数:key值

第二个参数:欲创建的共享内存段的大小(字节)

第三个参数:shmflg创建标识,包括IPC_CREAT, IPC_EXCL, IPC_NOWAIT, SHM_R(可读), SHM_W(可写)

 

int shmctl (int __shmid, int __cmd, struct shmid_ds *__buf) :共享内存控制

第一个参数:要操作的共享内存标识符

第二个参数:要执行的操作,IPC_RMID, iPC_SET, IPC_STAT, IPC_INFO, 超级用户还有 SHM_LOCK(锁定共享内存段), SHM_UNLOCK(解锁共享内存段)

第三个参数:临时共享内存变量信息

 

void *shmat (int __shmid, __const void * __shmaddr, int __shmflg) : 挂载共享内存到当前进程,返回共享内存首地址

第一个参数:要操作的共享内存标识符

第二个参数:指定共享内存映射地址,如果是0,系统选择。

第三个参数:指定共享内存段的访问权限和映射条件,0表示可读可写

技术分享

 

int shmdt (__const void *__shmaddr) :把共享内存与当前进程分离,参数为共享内存映射地址

 

测试,在只读共享内存中写信息:

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<errno.h>

int main(int argc, char * argv[])
{
    key_t key;
    int shm_id;
    char *ptr;
    key = ftok("/", 10);
    shm_id = shmget(key, 100, IPC_CREAT|SHM_R); //创建shm
    printf("get the shm id is %d\n", shm_id);  //打印id
    if((ptr = (char *)shmat(shm_id, NULL, SHM_RDONLY)) == NULL)  //只读方式挂载
    {
        if(shmctl(shm_id, IPC_RMID, NULL) == -1)  //如果失败则删除
            perror("Failed to remove memory segment");
        exit(EXIT_FAILURE);
    }
    //打印挂载地址
    printf("the attach add is %p\n", ptr);
    printf("now try to write the memory\n");
    *ptr = d;
    printf("*ptr =%c\n", *ptr);
    shmdt(ptr);
    shmctl(shm_id, IPC_RMID, 0);
}

发生段错误:

技术分享

 

 

父子进程间对共享内存的约定:

  • fork()的子进程继承父进程挂载的共享内存。
  • 调用exec执行新程序,则共享内存被自动卸载。
  • 如果调用了exit(),挂载的共享内存与当前进程脱离关系。

 

 

下面是一个应用的例子

实现两个没有亲缘关系进程的通信,一个负责写,另一个负责接收。用信号量实现同步,即写的时候不可读,读的时候不可写。用一元信号量实现,0表示可写,1表示可读

注意:在代码实现中,实际上是读写轮流操作的,即写一次,读一次。并没有达到真正多进程的效果。

代码经验证,可以使用

发送端代码:

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<errno.h>

int main(int argc, char *argv[])
{
    int running = 1;
    int shid;
    int semid;
    int value;
    void *sharem = NULL;
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_flg = SEM_UNDO;
    //创建信号量
    if((semid = semget((key_t)123456, 1, 0666|IPC_CREAT)) == -1) 
    {
        perror("semget");
        exit(EXIT_FAILURE);
    }
    //初始化信号量为0
    if(semctl(semid,0,SETVAL,0) == -1)
    {
        printf("sem init error");
        if(semctl(semid, 0, IPC_RMID, 0) != 0)
        {
            perror("semctl");
            exit(EXIT_FAILURE);
        }
        exit(EXIT_FAILURE);
    }
    //创建共享内存
    shid = shmget((key_t)654321, (size_t)2048, 0600|IPC_CREAT);  //创建共享内存
    if(shid == -1)
    {
        perror("shmget");
        exit(EXIT_FAILURE);
    }
    //挂载共享内存到当前进程
    sharem = shmat(shid, NULL, 0);
    if(sharem == NULL)
    {
        perror("shmat");
        exit(EXIT_FAILURE);
    }
    while(running)
    {
        //测试信号量值,如果为0则可写
        if((value = semctl(semid, 0, GETVAL)) == 0)
        {
            printf("write data operate\n");
            printf("please input something:");
            scanf("%s", sharem);
            sem_b.sem_op = 1;
            //执行信号量加1操作,允许读
            if(semop(semid, &sem_b, 1) == -1)
            {
                fprintf(stderr, "semaphore_p failed\n");
                exit(EXIT_FAILURE);
            }
        }
        //比较是否是结束符号
        if(strcmp(sharem, "end") == 0)
                running--;
    }
    shmdt(sharem);
        return 0;
}

接收端代码:

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<errno.h>

int main(int argc, char *argv[])
{
    int running = 1;
    int shid;
    int semid;
    int value;
    void *sharem = NULL;
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_flg = SEM_UNDO;
    //创建信号量
    if((semid = semget((key_t)123456, 1, 0666|IPC_CREAT)) == -1) 
    {
        perror("semget");
        exit(EXIT_FAILURE);
    }
    //创建共享内存
    shid = shmget((key_t)654321, (size_t)2048, 0600|IPC_CREAT);  //创建共享内存
    if(shid == -1)
    {
        perror("shmget");
        exit(EXIT_FAILURE);
    }
    //挂载共享内存到当前进程
    sharem = shmat(shid, NULL, 0);
    if(sharem == NULL)
    {
        perror("shmat");
        exit(EXIT_FAILURE);
    }
    while(running)
    {
        //测试信号量值,如果为1则可读
        if((value = semctl(semid, 0, GETVAL)) == 1)
        {
            printf("read data operate\n");
            sem_b.sem_op = -1;
            //执行信号量减1操作,允许写
            if(semop(semid, &sem_b, 1) == -1)
            {
                fprintf(stderr, "semaphore_p failed\n");
                exit(EXIT_FAILURE);
            }
            printf("%s\n", sharem);
        }
        //比较是否是结束符号
        if(strcmp(sharem, "end") == 0)
                running--;
    }
    shmdt(sharem);
    //删除共享内存
    if(shmctl(shid, IPC_RMID, 0) != 0)
    {
        perror("shmctl");
        exit(EXIT_FAILURE);
    }
    //删除信号量
    if(semctl(semid, 0, IPC_RMID, 0) != 0)
    {
        perror("semctl");
        exit(EXIT_FAILURE);
    }
    return 0;
}

 

【linux高级程序设计】(第十一章)System V进程间通信 4

标签:

原文地址:http://www.cnblogs.com/dplearning/p/4687747.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!