0

我正在用户空间中搜索 C 中的环形缓冲区实现,因此我可以在我的库中使用它。

因为我需要一个环形缓冲区

  • 非阻塞写入(=覆盖最旧的数据)
  • 如果为空,则阻止读取

我搜索了一段时间并记得我曾在内核模式下使用wait_event_interruptible&wake_up_interruptible来做类似的事情。

但是在用户空间中使用了什么,所以我也许可以结合该方法搜索环形缓冲区?我不想重新发明轮子——周围有很多环形缓冲解决方案。

提前致谢并致以诚挚的问候!

编辑:

似乎可能pthread_cond_wait相当于wait_event_interruptible.

4

2 回答 2

6

用一些代码添加另一个答案,这与我的另一个答案中的伪代码不是 1:1 匹配。将此标记为 wiki 答案,以防有人想要添加评论或进行其他改进。C phtread mutex+条件变量实现非常简单的ringbuffer:

#include <stdio.h>
#include <pthread.h>

#define RINGBUFFER_SIZE (5)
int ringbuffer[RINGBUFFER_SIZE];
unsigned reader_unread = 0;
unsigned writer_next = 0;
pthread_mutex_t ringbuffer_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t ringbuffer_written_cond = PTHREAD_COND_INITIALIZER;

void process_code(int ch) {
    int counter;
    printf("Processing code %d", ch);
    for(counter=5; counter>0; --counter) {
        putchar('.');
        fflush(stdout);
        sleep(1);
    }
    printf("done.\n");

}

void *reader() {
    pthread_mutex_lock(&ringbuffer_mutex);
    for(;;) {
        if (reader_unread == 0) {
            pthread_cond_wait(&ringbuffer_written_cond, &ringbuffer_mutex);
        }
        if (reader_unread > 0) {

            int ch;
            int pos = writer_next - reader_unread;
            if (pos < 0) pos += RINGBUFFER_SIZE;
            ch = ringbuffer[pos];
            --reader_unread;

            if (ch == EOF) break;

            pthread_mutex_unlock(&ringbuffer_mutex);
            process_code(ch);
            pthread_mutex_lock(&ringbuffer_mutex);
        }
    }
    pthread_mutex_unlock(&ringbuffer_mutex);

    puts("READER THREAD GOT EOF");
    return NULL;
}

void *writer() {
    int ch;
    do {
        int overflow = 0;
        ch = getchar();

        pthread_mutex_lock(&ringbuffer_mutex);

        ringbuffer[writer_next] = ch;

        ++writer_next;
        if (writer_next == RINGBUFFER_SIZE) writer_next = 0;

        if (reader_unread < RINGBUFFER_SIZE) ++reader_unread;
        else overflow = 1;

        pthread_cond_signal(&ringbuffer_written_cond);
        pthread_mutex_unlock(&ringbuffer_mutex);

        if (overflow) puts("WARNING: OVERFLOW!");

    } while(ch != EOF);

    puts("WRITER THREAD GOT EOF");
    return NULL;
}

int main(void)
{
    pthread_t reader_thread, writer_thread;

    puts("Starting threads. Type text and press enter, or type ctrl-d at empty line to quit.");
    pthread_create(&reader_thread, NULL, reader, NULL);
    pthread_create(&writer_thread, NULL, writer, NULL);

    pthread_join(writer_thread, NULL);
    pthread_join(reader_thread, NULL);

    return 0;
}
于 2013-02-07T19:44:07.537 回答
0

对于 pthreads,标准方法是使用互斥体并使用条件变量在一个线程中等待,直到被另一个线程唤醒

伪代码,其中 writer 将短暂阻塞,但不会在不确定的时间内阻塞,并且通过丢弃未读数据来处理缓冲区溢出:

作家写道:

acquire new data to write
lock mutex
get current writing position in buffer
compare to current reading position and check for overflow
    in case of overflow, update reading position (oldest data lost)
write new data to buffer
update writing position
do wakeup on condition variable
unlock mutex

读者阅读:

lock mutex
loop:
    get current reading position in buffer
    compare to current writing position in buffer
    if there's new data, break loop
    wait (possibly with timeout) on condition variable
    goto loop:
copy data from buffer
update reading position
unlock mutex
process copied data

显然,在上面,writer 可能会短暂地阻塞 mutex,但因为 reader 只会短暂地持有 mutex(这假设缓冲区中的数据相当短),这可能不是问题。

关于理解上述代码的重要细节:条件变量和互斥锁成对工作。等待条件变量将解锁互斥锁,一旦唤醒,只有在它可以重新锁定互斥锁后才会继续。因此,在作者解锁互斥锁之前,阅读器实际上不会继续。

当条件变量等待返回时再次检查缓冲区位置很重要,不要盲目地相信唤醒是由写入器完成的,它只是向它添加了更多数据。

于 2013-02-05T09:42:12.643 回答