多進(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);
}