简体   繁体   English

循环/环形缓冲区具有阻塞读取和非阻塞写入?

[英]Circular-/Ring-Buffer with blocking read and non-blocking write?

I am searching for a ringbuffer-implementation in C in userspace, so I can use it in my library. 我正在用户空间中的C中搜索环形缓冲区实现,因此可以在我的库中使用它。

Because I need a ringbuffer with 因为我需要一个ringbuffer

  • non-blocked write (=overwrite oldest data) 无阻塞写入(=覆盖最早的数据)
  • blocked read if empty 如果为空则阻止读取

I searched a while and remembered I have used wait_event_interruptible & wake_up_interruptible to do something like this in kernel-mode. 我搜索了一会儿,并想起我曾经在内核模式下使用过wait_event_interruptiblewake_up_interruptible来做类似的事情。

But what is used in user-space so I maybe can search for a ringbuffer in combination with that method? 但是在用户空间中使用了什么,因此我可能可以结合该方法搜索环形缓冲区? I don't want to re-invent the wheel - there are many ringbuffer-solutions around. 我不想重新发明轮子-周围有很多ringbuffer解决方案。

Thanks in advance & with kind regards! 在此先感谢您的光临!

EDIT: 编辑:

It seems that maybe pthread_cond_wait could be an equivalent of wait_event_interruptible . 似乎pthread_cond_wait可能等效于wait_event_interruptible

Adding another answer with some code, which isn't 1:1 match to pseudocde in my other answer. 用一些代码添加另一个答案,与我的另一个答案中的伪代码不是1:1匹配。 Marking this as wiki answer, in case someone want's to add comments or do other improvements. 如果有人要添加评论或进行其他改进,请将其标记为Wiki答案。 C phtread mutex+condition variable implementation of very simple ringbuffer: C phtread互斥锁+条件变量的实现非常简单的环形缓冲区:

#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;
}

With pthreads, standard way is to use a mutex and use a condition variable for waiting in one thread until woken up by another . 对于pthreads,标准方法是使用互斥锁并使用条件变量在一个线程中等待,直到被另一个 线程 唤醒

Pseudocode, where writer will block briefly but never for indeterminate time, and buffer overflow is handled by throwing away unread data: 伪代码,其中写程序将短暂阻塞,但不会无限期地阻塞,并且通过丢弃未读数据来处理缓冲区溢出:

Writer write: 作家写:

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

Reader read: 读者阅读:

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

Obviously above, writer may briefly block on mutex, but because reader will hold the mutex only briefly (this assumes data in buffer is fairly short), this is probably not a problem. 显然,在上面,编写器可能会短暂阻塞互斥锁,但是由于读取器只会短暂持有互斥锁(假定缓冲区中的数据很短),因此这可能不是问题。

Important details about understanding above code: condition variable and mutex work as a pair. 有关理解上述代码的重要详细信息:条件变量和互斥体可以成对使用。 Waiting on condition variable will unlock the mutex, and once waken up, will continue only after it can re-lock the mutex. 等待条件变量将解锁互斥锁,并且一旦唤醒,只有在可以重新锁定互斥锁之后才会继续。 So reader will not actually continue until writer unlocks the mutex. 因此,在写入者解锁互斥锁之前,读取者实际上不会继续。

It's important to check buffer positions again when condition variable wait returns, and not blindly trust that wakeup was done by writer which just put more data to it. 重要的是在条件变量等待返回时再次检查缓冲区位置,而不是盲目地相信唤醒是由写入程序完成的,写入程序只会向其中添加更多数据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM