进程间通信-信号量

概括

信号量本质上是一个计数器,用于多进程对共享数据对象的读取,它和管道有所不同,它不以传送数据为主要目的,它主要是用来保护共享资源(信号量也属于临界资源),使得资源在一个时刻只有一个进程独享。
信号量是一个特殊的变量,值可以改变,但只能取正整数值,并且对它的加1和减1操作是原子操作。如果信号量值为0,那么再进行减1操作时会阻塞。信号量的初始值,代表资源的数量。

二元信号量

二元信号量(Binary Semaphore)是最简单的一种锁(互斥锁),它只用两种状态:占用与非占用。所以它的引用计数为1。

上PHP代码

信号量的进程通信方式PHP也只封装了System V的方式。demo代码是多进程同时对一个临界文件读写,使用信号量当锁防止重复读写。

<?php
$file = "signal_amount.log";
$count = 0;
file_put_contents($file, $count);
$key = ftok("signal_amount.php", 'x');
// 获取&创建信号量ID 最大进程数为1
$sem_id = sem_get($key, 1);
$pid = pcntl_fork();
if($pid == 0){
    for($i = 0;$i<1000;$i++){
        // 获取信号量
        sem_acquire($sem_id);
        $d = (int) file_get_contents($file);
        $d+=1;
        file_put_contents($file, $d);
        // 释放信号量
        sem_release($sem_id);
    }
    exit();
}
for($i = 0;$i<1000;$i++){
    // 获取信号量
    sem_acquire($sem_id);
    $d = (int) file_get_contents($file);
    $d+=1;
    file_put_contents($file, $d);
    // 释放信号量
    sem_release($sem_id);
}
// 移除信号量
sem_remove($sem_id);

底层函数解析

sem_get 函数底层调用 semget semop semctl 实现。

PHP_FUNCTION(sem_get)
{
    zend_long key, max_acquire = 1, perm = 0666;
    bool auto_release = 1;
    int semid;
    struct sembuf sop[3];
    int count;
    sysvsem_sem *sem_ptr;

    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "l|llb", &key, &max_acquire, &perm, &auto_release)) {
        RETURN_THROWS();
    }

    /* Get/create the semaphore.  Note that we rely on the semaphores
     * being zeroed when they are created.  Despite the fact that
     * the(?)  Linux semget() man page says they are not initialized,
     * the kernel versions 2.0.x and 2.1.z do in fact zero them.
     */

    semid = semget(key, 3, perm|IPC_CREAT);
    if (semid == -1) {
        php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno));
        RETURN_FALSE;
    }

    /* Find out how many processes are using this semaphore.  Note
     * that on Linux (at least) there is a race condition here because
     * semaphore undo on process exit is not atomic, so we could
     * acquire SYSVSEM_SETVAL before a crashed process has decremented
     * SYSVSEM_USAGE in which case count will be greater than it
     * should be and we won't set max_acquire.  Fortunately this
     * doesn't actually matter in practice.
     */

    /* Wait for sem 1 to be zero . . . */

    sop[0].sem_num = SYSVSEM_SETVAL;
    sop[0].sem_op  = 0;
    sop[0].sem_flg = 0;

    /* . . . and increment it so it becomes non-zero . . . */

    sop[1].sem_num = SYSVSEM_SETVAL;
    sop[1].sem_op  = 1;
    sop[1].sem_flg = SEM_UNDO;

    /* . . . and increment the usage count. */

    sop[2].sem_num = SYSVSEM_USAGE;
    sop[2].sem_op  = 1;
    sop[2].sem_flg = SEM_UNDO;
    while (semop(semid, sop, 3) == -1) {
        if (errno != EINTR) {
            php_error_docref(NULL, E_WARNING, "Failed acquiring SYSVSEM_SETVAL for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno));
            break;
        }
    }

    /* Get the usage count. */
    count = semctl(semid, SYSVSEM_USAGE, GETVAL, NULL);
    if (count == -1) {
        php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno));
    }

    /* If we are the only user, then take this opportunity to set the max. */

    if (count == 1) {
#if HAVE_SEMUN
        /* This is correct for Linux which has union semun. */
        union semun semarg;
        semarg.val = max_acquire;
        if (semctl(semid, SYSVSEM_SEM, SETVAL, semarg) == -1) {
            php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno));
        }
#elif defined(SETVAL_WANTS_PTR)
        /* This is correct for Solaris 2.6 which does not have union semun. */
        if (semctl(semid, SYSVSEM_SEM, SETVAL, &max_acquire) == -1) {
            php_error_docref(NULL, E_WARNING, "Failed for key 0x%lx: %s", key, strerror(errno));
        }
#else
        /* This works for i.e. AIX */
        if (semctl(semid, SYSVSEM_SEM, SETVAL, max_acquire) == -1) {
            php_error_docref(NULL, E_WARNING, "Failed for key 0x%lx: %s", key, strerror(errno));
        }
#endif
    }

    /* Set semaphore 1 back to zero. */

    sop[0].sem_num = SYSVSEM_SETVAL;
    sop[0].sem_op  = -1;
    sop[0].sem_flg = SEM_UNDO;
    while (semop(semid, sop, 1) == -1) {
        if (errno != EINTR) {
            php_error_docref(NULL, E_WARNING, "Failed releasing SYSVSEM_SETVAL for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno));
            break;
        }
    }

    object_init_ex(return_value, sysvsem_ce);

    sem_ptr = Z_SYSVSEM_P(return_value);
    sem_ptr->key   = key;
    sem_ptr->semid = semid;
    sem_ptr->count = 0;
    sem_ptr->auto_release = (int) auto_release;
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇