php 內(nèi)存共享shmop源碼閱讀

多進(jìn)程通信的時(shí)候,會(huì)涉及到共享內(nèi)存。
shmop_open()
創(chuàng)建或打開一個(gè)內(nèi)存塊

PHP_FUNCTION(shmop_open)
{
    long key, mode, size;
    struct php_shmop *shmop;    
    struct shmid_ds shm;
    int rsid;
    char *flags;
    int flags_len;
    //解析傳PHP進(jìn)來(lái)的參數(shù)
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lsll", &key, &flags, &flags_len, &mode, &size) == FAILURE) {
        return;
    }

    if (flags_len != 1) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s is not a valid flag", flags);
        RETURN_FALSE;
    }

    //創(chuàng)建一個(gè)共享內(nèi)存塊
    shmop = emalloc(sizeof(struct php_shmop));
    //初始化共享內(nèi)存塊  
    memset(shmop, 0, sizeof(struct php_shmop));

    shmop->key = key;
    shmop->shmflg |= mode;

    switch (flags[0]) 
    {
        case 'a':
            shmop->shmatflg |= SHM_RDONLY;
            break;
        case 'c':
            shmop->shmflg |= IPC_CREAT;
            shmop->size = size;
            break;
        case 'n':
            shmop->shmflg |= (IPC_CREAT | IPC_EXCL);
            shmop->size = size;
            break;  
        case 'w':
            /* noop 
                shm segment is being opened for read & write
                will fail if segment does not exist
            */
            break;
        default:
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid access mode");
            goto err;
    }

    if (shmop->shmflg & IPC_CREAT && shmop->size < 1) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Shared memory segment size must be greater than zero");
        goto err;
    }
    //C語(yǔ)言shmget(得到一個(gè)共享內(nèi)存標(biāo)識(shí)符或創(chuàng)建一個(gè)共享內(nèi)存對(duì)象)
    shmop->shmid = shmget(shmop->key, shmop->size, shmop->shmflg);
    if (shmop->shmid == -1) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to attach or create shared memory segment");
        goto err;
    }
    //shmctl(共享內(nèi)存管理)  IPC_STAT 獲取內(nèi)存狀態(tài)
    if (shmctl(shmop->shmid, IPC_STAT, &shm)) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to get shared memory segment information");
        goto err;
    }   
    //shmat(把共享內(nèi)存塊對(duì)象映射到調(diào)用進(jìn)程的地址空間) 通俗的來(lái)說(shuō),用來(lái)標(biāo)識(shí)是哪塊進(jìn)程在使用。
    shmop->addr = shmat(shmop->shmid, 0, shmop->shmatflg);
    if (shmop->addr == (char*) -1) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to attach to shared memory segment");
        goto err;
    }

    shmop->size = shm.shm_segsz;
    //將內(nèi)存塊插入到zend資源列表(后續(xù)繼續(xù)說(shuō))
    rsid = zend_list_insert(shmop, shm_type TSRMLS_CC);
    RETURN_LONG(rsid);
err:
    efree(shmop);
    RETURN_FALSE;
}

解釋一下shmget()、shmat()

int shmget(key_t key, size_t size, int shmflg)
得到一個(gè)共享內(nèi)存標(biāo)識(shí)符或創(chuàng)建一個(gè)共享內(nèi)存對(duì)象并返回共享內(nèi)存標(biāo)識(shí)符

<key>
  0(IPC_PRIVATE):會(huì)建立新共享內(nèi)存對(duì)象
  大于0的32位整數(shù):視參數(shù)shmflg來(lái)確定操作。通常要求此值來(lái)源于ftok返回的IPC鍵值
<size>
  大于0的整數(shù):新建的共享內(nèi)存大小,以字節(jié)為單位
  0:只獲取共享內(nèi)存時(shí)指定為0
<shmflg>
  0:取共享內(nèi)存標(biāo)識(shí)符,若不存在則函數(shù)會(huì)報(bào)錯(cuò)
  IPC_CREAT:當(dāng)shmflg&IPC_CREAT為真時(shí),如果內(nèi)核中不存在鍵值與key相等的共享內(nèi)存,則新建一個(gè)共享內(nèi)存;如果存在這樣的共享內(nèi)存,返回此共享內(nèi)存的標(biāo)識(shí)符
  IPC_CREAT|IPC_EXCL:如果內(nèi)核中不存在鍵值與key相等的共享內(nèi)存,則新建一個(gè)消息隊(duì)列;如果存在這樣的共享內(nèi)存則報(bào)錯(cuò)
void *shmat(int shmid, const void *shmaddr, int shmflg)
連接共享內(nèi)存標(biāo)識(shí)符為shmid的共享內(nèi)存,連接成功后把共享內(nèi)存區(qū)對(duì)象映射到調(diào)用進(jìn)程的地址空間,隨后可像本地空間一樣訪問

msqid
  共享內(nèi)存標(biāo)識(shí)符
shmaddr
  指定共享內(nèi)存出現(xiàn)在進(jìn)程內(nèi)存地址的什么位置,直接指定為NULL讓內(nèi)核自己決定一個(gè)合適的地址位置
shmflg
  SHM_RDONLY:為只讀模式,其他為讀寫模式

shmop_read()函數(shù)
讀取內(nèi)存的里面的數(shù)據(jù)

PHP_FUNCTION(shmop_read)
{
    long shmid, start, count;
    struct php_shmop *shmop;
    int type;
    char *startaddr;
    int bytes;
    char *return_string;
    //解析出PHP函數(shù)傳入的參數(shù)
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lll", &shmid, &start, &count) == FAILURE) {
        return;
    }

    PHP_SHMOP_GET_RES

    if (start < 0 || start > shmop->size) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "start is out of range");
        RETURN_FALSE;
    }

    if (count < 0 || start > (INT_MAX - count) || start + count > shmop->size) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "count is out of range");
        RETURN_FALSE;
    }
    //獲取共享內(nèi)存的地址
    startaddr = shmop->addr + start;
    bytes = count ? count : shmop->size - start;
    //分配一個(gè)內(nèi)存空間
    return_string = emalloc(bytes+1);
    //開始從指定的位置拷貝數(shù)據(jù),
    //return_string 是返回值
    //startaddr 開始地址
    //bytes 要讀的字節(jié)數(shù)
    memcpy(return_string, startaddr, bytes);
    return_string[bytes] = 0;

    RETURN_STRINGL(return_string, bytes, 0);
}

shmop_write()函數(shù)
往一個(gè)內(nèi)存塊里面寫數(shù)據(jù)

PHP_FUNCTION(shmop_write)
{
    struct php_shmop *shmop;
    int type;
    int writesize;
    long shmid, offset;
    char *data;
    int data_len;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lsl", &shmid, &data, &data_len, &offset) == FAILURE) {
        return;
    }

    PHP_SHMOP_GET_RES

    if ((shmop->shmatflg & SHM_RDONLY) == SHM_RDONLY) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "trying to write to a read only segment");
        RETURN_FALSE;
    }

    if (offset < 0 || offset > shmop->size) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "offset out of range");
        RETURN_FALSE;
    }
    //判斷共享空間是否滿足大小,防止寫入溢出
    writesize = (data_len < shmop->size - offset) ? data_len : shmop->size - offset;
    //開始往共享內(nèi)存里面進(jìn)行寫入
    //shmop->addr 是共享內(nèi)存的地址
    //offset 是寫入的偏移量
    //writesize 寫入數(shù)據(jù)的多少
    memcpy(shmop->addr + offset, data, writesize);

    RETURN_LONG(writesize);
}

由上面的兩個(gè)函數(shù)的源碼看來(lái),其實(shí)都是共用了一個(gè)函數(shù)memcpy(),這個(gè)函數(shù)我們可以在源碼里面的main/php.h追蹤到。

define memcpy(d, s, n)  bcopy((s), (d), (n))

可以看出,是對(duì)C函數(shù)bcopy()的封裝。
解釋bcopy()

原型:void bcopy(const  void  *src,  void  *dest,  int  n)
用法:#include <string.h>
功能:將字符串src的前n個(gè)字節(jié)復(fù)制到dest中。

shmop_size()函數(shù)
獲取內(nèi)存塊的大小

/* {{{ proto int shmop_size (int shmid)
   returns the shm size */
PHP_FUNCTION(shmop_size)
{
    long shmid;
    struct php_shmop *shmop;
    int type;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &shmid) == FAILURE) {
        return;
    }

    PHP_SHMOP_GET_RES

    //直接獲取結(jié)構(gòu)體的size值
    RETURN_LONG(shmop->size);
}

shmop_delete()函數(shù)
刪除一個(gè)內(nèi)存塊

PHP_FUNCTION(shmop_delete)
{
    long shmid;
    struct php_shmop *shmop;
    int type;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &shmid) == FAILURE) {
        return;
    }

    PHP_SHMOP_GET_RES
    //本質(zhì)是對(duì)C語(yǔ)言函數(shù)的shmctl()的封裝
    //IPC_RMID 這常量表示刪除共享空間
    if (shmctl(shmop->shmid, IPC_RMID, NULL)) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "can't mark segment for deletion (are you the owner?)");
        RETURN_FALSE;
    }
    RETURN_TRUE;
}

解釋一下shmctl()函數(shù)

int shmctl(int shmid, int cmd, struct shmid_ds *buf)
<msqid>  共享內(nèi)存標(biāo)識(shí)符
<cmd>  
  IPC_STAT:得到共享內(nèi)存的狀態(tài),把共享內(nèi)存的shmid_ds結(jié)構(gòu)復(fù)制到buf中
  IPC_SET:改變共享內(nèi)存的狀態(tài),把buf所指的shmid_ds結(jié)構(gòu)中的uid、gid、  mode復(fù)制到共享內(nèi)存的shmid_ds結(jié)構(gòu)內(nèi)
  IPC_RMID:刪除這片共享內(nèi)存
<buf>   共享內(nèi)存管理結(jié)構(gòu)體

shmop_close()
關(guān)閉一個(gè)內(nèi)存塊

PHP_FUNCTION(shmop_close)
{
    long shmid;
    struct php_shmop *shmop;
    int type;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &shmid) == FAILURE) {
        return;
    }

    PHP_SHMOP_GET_RES
    //將一個(gè)內(nèi)存塊從當(dāng)前進(jìn)程的符號(hào)表刪除
    zend_list_delete(shmid);
   
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,954評(píng)論 25 709
  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來(lái)就是把...
    Dove_iOS閱讀 27,606評(píng)論 30 472
  • ———————————————回答好下面的足夠了---------------------------------...
    恒愛DE問候閱讀 1,842評(píng)論 0 4
  • 心智模式就是看不見的“眼鏡”加上一套固定的思維程序所搭建成的內(nèi)在世界模型。即看到的世界就是我們的大腦想讓我們看到的...
    青衣語(yǔ)閱讀 529評(píng)論 5 4
  • 一份耕耘,一份收獲 在生活里,經(jīng)常性地聽到這樣一句話,沒有功勞還有苦勞 這是很多努力過(guò),奮斗過(guò)的人,對(duì)自己的待遇的...
    大麥茶的故事閱讀 174評(píng)論 1 0

友情鏈接更多精彩內(nèi)容