Linux共享内存实践(2)

shmdt函数

功能:将共享内存段与当前进程脱离

原型:

int shmdt(const void *shmaddr);

参数:

    shmaddr: 由shmat所返回的指针

 

返回值:

    成功返回0;失败返回-1


//实践:运行程序,观察ipcs输出
int main()
{
    //获取或者打开共享内存
    int shmid = shmget(0x15764221, 1024 * sizeof(int), 0666 | IPC_CREAT);
    if (shmid == -1)
    {
        err_exit("shmget error");
    }

    //将ID为shmid的共享内存连接到该进程
    int *pArray = static_cast<int *>(shmat(shmid, NULL, 0));
    if (pArray == (void *)-1)
    {
        err_exit("shmat error");
    }

    sleep(10);

    //将共享内存从当前进程分离
    if (shmdt(pArray) == -1)
    {
        err_exit("shmdt error");
    }
    sleep(10);

    return 0;
}

注意:将共享内存段与当前进程脱离不等于删除共享内存段

 

shmctl函数

功能:用于调控/获取共享内存属性

原型:

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

参数:

    shmid:由shmget返回的共享内存标识码

    cmd:将要采取的动作(三个取值见下)

    buf:指向一个保存着共享内存的模式状态和访问权限的数据结构



返回值:

    成功返回0;失败返回-1

//示例:detach & delete, 请在多个终端中启动该程序,仔细用ipcs观察,该程序的每一步
int main()
{
    //获取或者打开共享内存
    int shmid = shmget(0x15764221, 1024 * sizeof(int), 0666 | IPC_CREAT);
    if (shmid == -1)
    {
        err_exit("shmget error");
    }

    //将ID为shmid的共享内存连接到该进程
    int *pArray = static_cast<int *>(shmat(shmid, NULL, 0));
    if (pArray == (void *)-1)
    {
        err_exit("shmat error");
    }

    int choice = 0;
    cout << "Please input your choice: 0-detach, other-continue: ";
    cin >> choice;
    if (choice == 0)
    {
        //将共享内存从当前进程分离
        if (shmdt(pArray) == -1)
        {
            err_exit("shmdt error");
        }
    }

    //     int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    cout << "\nPlease input your choice: 0-delete, other-quit: ";
    cin >> choice;
    if (choice == 0)
    {
        //删除共享内存(shmid的引用计数-1)
        if (shmctl(shmid,IPC_RMID,NULL) == -1)
        {
            err_exit("shmctl error");
        }
        else
        {
            cout << "Delete Success!" << endl;
        }
    }

    return 0;
}
/**
  共享内存在内核中使用了引用计数技术:必须等到连接到该共享内存的数目为0时(等到该共享内存的引用计数变为0),才执行真正的删除该共享内存动作!当引用技术不为0时,调用IPC_RMID时,该共享内存的key会变成0(变为私有的);
*/

/**示例:查看在其中一个进程执行删除之后(但此时引用计数还不为0),该共享内存是否可读;
执行顺序:
  writeMemory
  readMemory
  writeMemory中,键入0
  readMemory中,键入0
*/

//writeMemory.cpp
int main()
{
    const int NUMBER = 36;
    int shmid = shmget(0x15764221, NUMBER * sizeof(int), 0666 | IPC_CREAT);
    if (shmid == -1)
    {
        err_exit("shmget error");
    }

    int *pArray = static_cast<int *>(shmat(shmid, NULL, 0));
    if (pArray == (void *)-1)
    {
        err_exit("shmat error");
    }

    for (int i = 0; i < NUMBER; ++i)
    {
        *(pArray+i) = i+1;
    }

    int choice = 0;
    cout << "Please input your choice: 0-delete, other-quit: ";
    cin >> choice;
    if (choice == 0)
    {
        //删除共享内存(shmid的引用计数-1)
        if (shmctl(shmid,IPC_RMID,NULL) == -1)
        {
            err_exit("shmctl error");
        }
        else
        {
            cout << "Delete Success!" << endl;
        }
    }

    return 0;
}

//2:readMemory.cpp
int main()
{
    const int NUMBER = 36;
    int shmid = shmget(0x15764221, NUMBER * sizeof(int), 0666);
    if (shmid == -1)
    {
        err_exit("shmget error");
    }

    int *pArray = static_cast<int *>(shmat(shmid, NULL, 0));
    if (pArray == (void *)-1)
    {
        err_exit("shmat error");
    }

    int choice = 0;
    cout << "Please input your Choice: 0-read, other-quit: ";
    cin >> choice;
    if (!choice)
    {
        for (int i = 0; i < NUMBER; ++i)
        {
            cout << *(pArray+i) << "\t";
        }
        cout << endl;
    }

    return 0;
}



    由上图可知:即使其中一个进程执行了IPC_RMID(只要该共享内存引用计数不为0),该共享内核还是可读的(但此时必须该读进程是已经连接到该共享内存的,不然的话,写进程虽然没有将共享内存真正的删掉,但是其键值已经变为0x00000000(Private),此时其他进程是连接不上的)!

 

总结:

    1.共享内存被别的程序占用,则删除该共享内存时,不会马上删除

    2.此时会出现一个现象:该共享内存的key变为0x00000000,变为私有

    3.此时还可以读,但必须还有办法获取该共享内存的ID(shmid),因为此时试图通过该共享内存的key获取该共享内存,都是白费的!


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。