5

POSIX 和 C11 都要求流具有关联的锁以防止数据竞争和交错。POSIX 文档flockfile()和相关函数允许线程安全 I/O 跨越多个调用。是否有等效的 C 函数?我不记得有一个,也找不到提到的一个,这并不一定意味着一个不存在。

在相关的说明中,假设任何声称符合 POSIX 一致性的实现flockfile()在使用 C11 threads.h 时都会得到尊重,这是否安全?我认为大多数支持threads.h的主要POSIX兼容C实现都是通过在pthreads上构建的,这意味着这种假设通常是安全的。但是,多线程很难,知道异常会很有帮助。

4

2 回答 2

2

POSIX 和 C11 都要求流具有关联的锁以防止数据竞争和交错。

是的,但对该锁的特定形式没有要求,C 也没有要求程序可以直接访问该锁,也没有任何标准机制可以做到这一点。

POSIX 记录了flockfile() 和相关函数以允许线程安全I/O 跨越多个调用。是否有等效的 C 函数?

没有任何已发布的 C 语言规范版本记录了flockfile()ftrylockfile()funlockfile(). 一个严格符合 C11 或 C17 的程序想要防止被交错的不同线程调用 I/O 函数将(因为它是严格符合的)在未定义宏的实现上使用 C 线程库__STDC_NO_THREADS__。这样的程序可以为此目的使用该库的互斥锁实现。没有直接访问与单个FILE对象直接关联的任何锁的标准方法。

在相关说明中,假设任何声称符合 POSIX 的实现在使用 C11 threads.h 时都会尊重flockfile() 是否安全?

据我所知,POSIX C 语言接口仍然是专门根据 C99 定义的,因此 POSIX 对 C11 线程视而不见。从这个意义上说,不,假设关于线程行为的 POSIX 规定将适用于 C11 线程是不安全的。

我认为大多数支持threads.h的主要POSIX兼容C实现都是通过在pthreads上构建的,这意味着这种假设通常是安全的。但是,多线程很难,知道异常会很有帮助。

在实践中,我期望 POSIX 系统上的 C11 和更高版本的实现将确保 C11 线程实际上确实具有 POSIX 线程的语义,无论是通过在 POSIX 线程之上实现 C11 线程还是通过其他方式。因此,希望 C11 线程能够与flockfile().

另请注意,在这样的上下文中,“通常安全”是说“不安全”的一种狡猾的方式。

于 2021-04-06T16:07:04.050 回答
0

Stockfish 中的 Windows 实现:

void flockfile(FILE *F)
{
  if ((F >= (&__iob_func()[0])) && (F <= (&__iob_func()[_IOB_ENTRIES-1]))) {
    _lock(_STREAM_LOCKS + (int)(F - (&__iob_func()[0])));
    F->_flag |= _IOLOCKED;
  } else
    EnterCriticalSection(&(((_FILEX *)F)->lock));
}

void funlockfile(FILE *F)
{
  if ((F >= (&__iob_func()[0])) && (F <= (&__iob_func()[_IOB_ENTRIES-1]))) {
    F->_flag &= ~_IOLOCKED;
    _unlock(_STREAM_LOCKS + (int)(F - (&__iob_func()[0])));
  } else
    LeaveCriticalSection(&(((_FILEX *)F)->lock));
}

https://github.com/syzygy1/Cfish/blob/5dbf00f9ffae28b0c96a713e897d718204af73e9/src/misc.c#L299

或者它是如何做的:

void
flockfile(FILE * fp)
{
    LockTable*  t = lock_table_lock();
    if (t != NULL) {
        FileLock**  lookup = lock_table_lookup(t, fp);
        FileLock*   lock   = *lookup;
        if (lock == NULL) {
            pthread_mutexattr_t  attr;
            /* create a new node in the hash table */
            lock = malloc(sizeof(*lock));
            if (lock == NULL) {
                lock_table_unlock(t);
                return;
            }
            lock->next        = NULL;
            lock->file        = fp;
            pthread_mutexattr_init(&attr);
            pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
            pthread_mutex_init( &lock->mutex, &attr );
            *lookup           = lock;
        }
        lock_table_unlock(t);
        /* we assume that another thread didn't destroy 'lock'
        * by calling fclose() on the FILE*. This can happen if
        * the client is *really* buggy, but we don't care about
        * such code here.
        */
        pthread_mutex_lock(&lock->mutex);
    }
}

https://source.codeaurora.org/external/gigabyte/qrd-gb-ssss-7225/plain/android/bionic/libc/stdio/flockfile.c

于 2021-04-06T14:26:18.090 回答