C 进程共享条件变量:一个进程死亡后如何恢复?

C 进程共享条件变量:一个进程死亡后如何恢复?,c,linux,pthreads,condition-variable,C,Linux,Pthreads,Condition Variable,我正在使用一个简单的FIFO队列来同步服务器进程的多个实例 这与 ,除了处理多个进程而不是线程。我习惯于使用进程共享互斥体和共享内存段中的条件变量。如果一个进程在处理请求时死亡,它还会处理超时: #include <assert.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.

我正在使用一个简单的FIFO队列来同步服务器进程的多个实例

这与 ,除了处理多个进程而不是线程。我习惯于使用进程共享互斥体和共享内存段中的条件变量。如果一个进程在处理请求时死亡,它还会处理超时:

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <pthread.h>
#include <signal.h>
#include <errno.h>

static inline void fail(char *str)
{
    perror(str);
    exit(1);
}

/***************************************************************************************************/

/* Simple ticket lock queue with pthreads
 * https://stackoverflow.com/questions/3050083/linux-synchronization-with-fifo-waiting-queue
 */

typedef struct ticket_lock {
    pthread_mutex_t mutex;
    pthread_cond_t  cond;
    int queue_head, queue_tail;
} ticket_lock_t;

static void
ticket_init(ticket_lock_t *t)
{
    pthread_mutexattr_t mattr;
    pthread_mutexattr_init(&mattr);
    pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
    pthread_mutexattr_setrobust(&mattr, PTHREAD_MUTEX_ROBUST);
    pthread_mutex_init(&t->mutex, &mattr);
    pthread_mutexattr_destroy(&mattr);

    pthread_condattr_t cattr;
    pthread_condattr_init(&cattr);
    pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
    pthread_cond_init(&t->cond, &cattr);
    pthread_condattr_destroy(&cattr);

    t->queue_head = t->queue_tail = 0;
}

static void
ticket_broadcast(ticket_lock_t *ticket)
{
    pthread_cond_broadcast(&ticket->cond);
}

static int
ticket_lock(ticket_lock_t *ticket)
{
    pthread_mutex_lock(&ticket->mutex);
    int queue_me = ticket->queue_tail++;

    while (queue_me > ticket->queue_head) {
        time_t sec = time(NULL) + 5;  /* 5s timeout */
        struct timespec ts = { .tv_sec = sec, .tv_nsec = 0 };

        fprintf(stderr, "%i: waiting, current: %i  me: %i\n", getpid(), ticket->queue_head, queue_me);

        if (pthread_cond_timedwait(&ticket->cond, &ticket->mutex, &ts) == 0)
            continue;

        if (errno != ETIMEDOUT) fail("pthread_cond_timedwait");

        /* Timeout, kick current user... */
        fprintf(stderr, "kicking stale ticket %i\n", ticket->queue_head);
        ticket->queue_head++;
        ticket_broadcast(ticket);
    }

    pthread_mutex_unlock(&ticket->mutex);
    return queue_me;
}

static void
ticket_unlock(ticket_lock_t *ticket, int me)
{
    pthread_mutex_lock(&ticket->mutex);
    if (ticket->queue_head == me) {  /* Normal case: we haven't timed out. */
        ticket->queue_head++;
        ticket_broadcast(ticket);
    }
    pthread_mutex_unlock(&ticket->mutex);
}


/***************************************************************************************************/
/* Shared memory */

#define SHM_NAME    "fifo_sched"
#define SHM_MAGIC   0xdeadbeef

struct sched_shm {
    int  size;
    int  magic;
    int  ready;

    /* sched stuff */
    ticket_lock_t queue;
};

static unsigned int shm_size = 256;
static struct sched_shm *shm = 0;

/* Create new shared memory segment */
static void
create_shm()
{
       int fd = shm_open(SHM_NAME, O_RDWR | O_CREAT | O_TRUNC, 0644);
       assert(fd != -1);
       int r = ftruncate(fd, shm_size);  assert(r == 0);
       void *pt = mmap(0, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
       assert(pt != MAP_FAILED);
       fprintf(stderr, "Created shared memory.\n");

       shm = pt;
       memset(shm, 0, sizeof(*shm));
       shm->size = shm_size;
       shm->magic = SHM_MAGIC;
       shm->ready = 0;

       ticket_init(&shm->queue);

       shm->ready = 1;
}

/* Attach existing shared memory segment */
static int
attach_shm()
{
    int fd = shm_open(SHM_NAME, O_RDWR, 0);
    if (fd == -1)  return 0;  /* Doesn't exist yet... */

    shm = mmap(0, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (shm == MAP_FAILED)  fail("mmap");
    fprintf(stderr, "Mapped shared memory.\n");

    assert(shm->magic == SHM_MAGIC);
    assert(shm->ready);
    return 1;
}

static void
shm_init()
{  
    fprintf(stderr, "shm_init()\n");
    assert(shm_size >= sizeof(struct sched_shm));
    if (!attach_shm())
        create_shm();
}


/***************************************************************************************************/

int main()
{
    shm_init();

    while (1) {
        int ticket = ticket_lock(&shm->queue);
        printf("%i: start %i\n", getpid(), ticket);
        printf("%i: done  %i\n", getpid(), ticket);
        ticket_unlock(&shm->queue, ticket);
    }
    return 0;
}
但是,杀死第二个实例会在第一个实例中中断pthread_cond_timedwait():

pthread_cond_timedwait: No such file or directory
这在某种程度上是有意义的,条件变量正在跟踪这个过程,它不再存在了


肯定有办法从中恢复过来?

[评论太长了]

胡!:-)

pthread_*()
函数族不会将
errno
设置为任何错误代码,而是返回它

所以为了得到任何有用的结果,改变这个

    if (pthread_cond_timedwait(&ticket->cond, &ticket->mutex, &ts) == 0)
        continue;

    if (errno != ETIMEDOUT) fail("pthread_cond_timedwait");
将来

好的,引用posix参考:

如果互斥锁是一个健壮的互斥锁,并且包含所属线程的进程在持有互斥锁时终止,那么对pthread_mutex_lock()的调用将返回错误值[eownerded]。[…]在这些情况下,互斥锁被线程锁定,但它所保护的状态被标记为不一致。应用程序应确保状态一致以便重用,完成后调用pthread_mutex_consistent()。如果应用程序无法恢复状态,则应在不调用pthread_mutex_consistent()的情况下解锁互斥体,然后将互斥体标记为永久不可用

因此,除了alk的注释,我们需要在调用pthread_mutex_lock()和pthread_cond_timedwait()时注意EOwnerded,并在其上调用pthread_mutex_consistent()

比如:

if ((errno = pthread_cond_timedwait(&ticket->cond, &ticket->mutex, &ts)) == 0)
    continue;
if (errno == EOWNERDEAD)  /* Recover mutex owned by dead process */
    pthread_mutex_consistent(&ticket->mutex);
else if (errno != ETIMEDOUT)
    fail("pthread_cond_timedwait");

的确:
pthread\u cond\u timedwait:Owner死了
现在,至少错误msg更有意义=)@lemonsqueze:建议阅读:…-以防万一你还没有被它绊倒。谢谢,比我的
man3posix pthread\u cond\u wait
更好,它甚至没有提到EOwnerded。
    if (pthread_cond_timedwait(&ticket->cond, &ticket->mutex, &ts) == 0)
        continue;

    if (errno != ETIMEDOUT) fail("pthread_cond_timedwait");
    if ((errno = pthread_cond_timedwait(&ticket->cond, &ticket->mutex, &ts)) == 0)
        continue;

    if (errno != ETIMEDOUT) fail("pthread_cond_timedwait");
if ((errno = pthread_cond_timedwait(&ticket->cond, &ticket->mutex, &ts)) == 0)
    continue;
if (errno == EOWNERDEAD)  /* Recover mutex owned by dead process */
    pthread_mutex_consistent(&ticket->mutex);
else if (errno != ETIMEDOUT)
    fail("pthread_cond_timedwait");