Linux进程间共享内存通讯

今天在跑海思3520D应用程序的时候,发现视频解码的时候老是出现打开共享内存失败,于是自己好好了解了下共享内存的机制,于是自己在这里做了下笔记

使用共享内存基本分四个步骤:

获得共享内存:shmget()->映射共享内存shmat() -->解除映射shmdt()-->删除共享内存shmctl()


于是自己在网上找来了一个例子看了下,并且用虚拟机单独跑了下共享内存的经典例程看了下,才知道了自己的问题出现哪里了 ,

发现有时候只要自己亲自将程序一步一步的去测,才知道问题的根源在哪里,结果出来的时候才恍然大悟

这是我在我开发用的一个共享内存函数模块找了出来分析了下:

*函数功能:打开共享内存
*输入参数:pathname--用于产生共享内存key的文件路径
proj_id--用于产生共享内存key的工程名
size--共享内存大小
*输出参数:无
*返回参数:返回共享内存id
*创建作者:hurong on 2010/12/27
*/

//#define HELPER_FILENAME_FTOK "/dev/ttyAMA0"

#define HELPER_FILENAME_FTOK "/dev/ttyS000"
int openshm(const char *pathname, int proj_id, size_t size)
{
    key_t    shmkey;
    int    sid;
    
    if ((shmkey = ftok (pathname, proj_id)) == -1) {
        perror ("ftok error");
        return (-1);
    }


    if ((sid = shmget (shmkey, size, 0)) == -1) {
        perror ("shmget call failed");
        return (-1);
    }
    return (sid);


}


int OpenShmEnv()
{
if(-1 == shmid)
{
/*创建共享内存*/
if ((shmid = openshm (HELPER_FILENAME_FTOK, HELPER_SHM_PROJECT_FTOK, sizeof(baseshm_u))) == -1)
{
printf("open shm FAILED\n");
return -1;   
}
}


//映射共享内存
if(NULL == shmaddr)
{
/*attach 共享内存区到自己的进程空间*/
if((shmaddr =(char *)shmat (shmid, (char *)0, 0)) == (char *)-1)
{
printf("attach shared memory error!\n");
return -1;   
}
}


//创建信号量
if(-1 == semid)
{
/*得到信号量集标识符*/
if((semid = opensem (HELPER_FILENAME_FTOK, HELPER_SHM_PROJECT_FTOK)) == -1) {
 printf("open sem error!\n");
return -1;
}
}
return 0;
}

这个是我当时在我海思3520D中运行的共享内存块的一部分代码 ,我在运行的时候当我进行解码的线程的时候就会出现(open shm FAILED!)这个问题。

当时很纳闷我看了下代码也没有写错,我发现在获取共享内存之前我们有一个ftok函数。


因为系统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值。通常情况下,该id值通过ftok函数得到。
ftok原型如下:
key_t ftok( char * fname, int id )

fname就时你指定的文件名(该文件必须是存在而且可以访问的),id是子序号,虽然为int,但是只有8个比特被使用(0-255)。

当成功执行的时候,一个key_t值将会被返回,否则 -1 被返回。

于是我发现我们ftok函数中用的路径为(#define HELPER_FILENAME_FTOK "/dev/ttyS000"),于是我进入到我的根文件系统中,

用命令cat  /dev/

我发现其实我们共享内存的指定文件路径是 /dev/ttyAMA0 ,于是恍然大悟,难怪程序跑的时候打开共享内存失败!

于是我把#define HELPER_FILENAME_FTOK "/dev/ttyS000"

换成  #define HELPER_FILENAME_FTOK "/dev/ttyAMA0"

再重新编译下烧进去跑的时候,发现问题解决了 。


我们用一个简短的例子来体验一下这个过程:

int *addr;      //用来保存映射的地址

int shmid;      //用来保存共享内存的ID标识符


 shmid = shmget(IPC_PRIVATE, 4, 0);         //获得四个字节大小的共享内存,返回的共享内存ID标识符被保存到shmid中

addr = shmat(shmid, 0, 0);              //映射共享内存,获得共享内存地址

*addr = 3;             //对共享内存进行操作,将这个地址赋值为3

shmdt(addr);           //解除映射,但共享内存依然存在于系统中,没有被删除

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

 

上面只是一个简短示例,用来体会一下应用过程,实际编写程序时,需要对返回值进行检检查,以免出错。

 

查看系统中已有的共享内存可用命令:ipcs

 

现在解释一下这四个函数的用法:

1.int shmget(key_t key, int size, int shmflg);

       当key取值为IPC_PRIVATE时,建立新的共享内存,大小由size决定。

       当key取值不为IPC_PRIVATE时,而且也不是已建立的共享内存IPC key,则视参数shmflg中是否有IPC_CREAT标志,来决定是否建立新的共享内存,并且以key值为新共享内存的IPC key。如果没有IPC_CREAT标志,则会返回错误。

       当key取值为已建立的共享内存IPC key时,如果参数shmflg中包含IPC_CREAT和IPC_EXCL,则返回错误。如果只包含这两个标志中的其中之一,或者两个都不包含,则视size的大小是否小于等于这个已存在的共享内存的大小,如果小于等于,则返回这个共享内存的ID标识符;否则,返回错误。

     参数shmflg也可以用来设置共享内存的存取权限,其值相当于open()函数的mode用法,执行位不用。

返回值:若成功返回共享内存的ID标识符,错误返回-1,错误原因存于errno

 

2.void *shmat(int shmid, const void *shmaddr, int shmflg);

shmid为共享内存ID标识符。

shmaddr指定映射的地址:

如果shmaddr为0,则由内核自动分配。

如果不为0,shmflg也没有指定IPC_RND旗标,则以参数shmaddr为连接地址。

如果不为0,shmflg指定了IPC_RND旗标,则将自动将参数shmflg调整为SHMLBA的整数倍。

 

SHMLBA的定义有两种情况,如下:

#define   SHMLBA   PAGE_SIZE

#define   SHMLBA   (4 * PAGE_SIZE)

 

shmflg还可以有SHM_RDONLY,表示映射的内存只可以读。

 

附加说明:1.在经过fork()之后,映射的地址会被继承到子进程。

               2.在经过exec()之后,映射的地址连接会自动脱离。

               3.程序运行结束之后,映射的地址连接也会自动脱离。

返回值:若成功返回映射得到的地址,错误返回-1,错误原因存于errno

 

3. int shmdt(const void *shmaddr);

     shmaddr为shmat()映射后获得的地址,此函数解除shmaddr与它连接的共享内存之间的映射。

返回值:成功返回0,否则返回-1,错误原因存于errno

 

4. int shmctl(int shmid, int cmd, struct shmid_ds *buf);

     shmctl()提供了几种方式来控制共享内存的操作,shmid为共享内存的ID标识符,cmd为操作的命令,有如下操作:

     IPC_STAT          把共享内存的shmid_ds结构数据复制到buf(每个共享内存中,都包含有一个shmid_ds结构,里面保存了一些关于本共享内存的信息)

     IPC_SET           将参数所指的shmid_ds结构中的shm_perm.uid、shm_perm.gid和shm_perm.mode复制到共享内存的shmid_ds结构内。

     IPC_RMID          删除共享内存和其包含的数据结构

     SHM_LOCK         不让此共享内存置换到 swap    (什么是swap,这个本人也不理解,如果有高手知道,希望不吝赐教)

     SHM_UNLOCK      允许此共享内存置换到swap

     SHM_LOCK和SHM_UNLOCK为Linux特有,且唯有超级用户允许使用。

 

     shmid_ds结构定义(linux-2.6.32.2):

struct shmid_ds {

            struct ipc_perm         shm_perm;  /* operation perms */

            int          shm_segsz;     /* size of segment (bytes) */

     __kernel_time_t         shm_atime;  /* last attach time */

            __kernel_time_t        shm_dtime;  /* last detach time */

            __kernel_time_t        shm_ctime;  /* last change time */

     __kernel_ipc_pid_t     shm_cpid;    /* pid of creator */

       __kernel_ipc_pid_t     shm_lpid;     /* pid of last operator */

            unsigned short           shm_nattch; /* no. of current attaches */

            unsigned short          shm_unused;      /* compatibility */

            void                     *shm_unused2;  /* ditto - used by DIPC */

            void                     *shm_unused3;  /* unused */

};

 

shm_segsz        共享内存的大小(bytes)。

shm_atime        最后一次attach(映射)此共享内存的时间

shm_dtime        最后一次detach(解除映射)此共享内存的时间

shm_ctime        最后一次更动此共享内存的时间。

shm_cpid         建立此共享内存的进程识别码。

shm_lpid          最后一个操作此共享内存的进程识别码。

 

 

下面是一个经典范例:

#include <unistd.h>

#include <sys/ipc.h>

#include <sys/shm.h>

 

#define KEY          1234

#define SIZE         1024

 

int main()

{

       int shmid;

       char *shmaddr;

       struct shmid_ds buf;

       shmid = shmget(KEY, SIZE, IPC_CREAT | 0600);    /*建立共享内存*/

 

       if (fork() == 0)

       {

              shmaddr = (char *) shmat(shmid, 0, 0);

              strcpy(shmaddr, "Hi! I am Chiled process!/n");

              printf("Child:   write to shared memery: /"Hi! I am Chiled process!/"/n");

              shmdt(shmaddr);

              return;

       }

       else

       {

              sleep(3);      /*等待子进程执行完毕*/

              shmctl(shmid, IPC_STAT, &buf);          /*取得共享内存的状态*/

              printf("shm_segsz = %d bytes/n", buf.shm_segsz);

              printf("shm_cpid = %d/n", buf.shm_cpid);

              printf("shm_lpid = %d/n", buf.shm_lpid);

              shmaddr = (char*) shmat(shmid, 0, SHM_RDONLY);

              printf("Father:   %s/n", shmaddr);        /*显示共享内存内容*/

              shmdt(shmaddr);

              shmctl(shmid, IPC_RMID, NULL);         /*删除共享内存*/

       }

}

后面是网上找的每个函数的详解例子,前面是自己的实践心得以及自己的解决方法,希望当你遇到共享内存的问题,这篇博客记载对你有帮助
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值