// This approach adds one bit to end and start pointers// Circular buffer objecttypedefstruct{intsize;// maximum number of elementsintstart;// index of oldest elementintend;// index at which to write new elementElemType*elems;// vector of elements}CircularBuffer;voidcbInit(CircularBuffer*cb,intsize){cb->size=size;cb->start=0;cb->end=0;cb->elems=(ElemType*)calloc(cb->size,sizeof(ElemType));}voidcbPrint(CircularBuffer*cb){printf("size = 0x%x, start = %d, end = %d\n",cb->size,cb->start,cb->end);}intcbIsFull(CircularBuffer*cb){returncb->end==(cb->start^cb->size);// This inverts the most significant bit of start before comparison}intcbIsEmpty(CircularBuffer*cb){returncb->end==cb->start;}intcbIncr(CircularBuffer*cb,intp){return(p+1)&(2*cb->size-1);// start and end pointers incrementation is done modulo 2*size}voidcbWrite(CircularBuffer*cb,ElemType*elem){cb->elems[cb->end&(cb->size-1)]=*elem;if(cbIsFull(cb))// full, overwrite moves start pointercb->start=cbIncr(cb,cb->start);cb->end=cbIncr(cb,cb->end);}voidcbRead(CircularBuffer*cb,ElemType*elem){*elem=cb->elems[cb->start&(cb->size-1)];cb->start=cbIncr(cb,cb->start);}
#include<sys/mman.h>#include<stdlib.h>#include<unistd.h>#define report_exceptional_condition() abort ()structring_buffer{void*address;unsignedlongcount_bytes;unsignedlongwrite_offset_bytes;unsignedlongread_offset_bytes;};// Warning order should be at least 12 for Linuxvoidring_buffer_create(structring_buffer*buffer,unsignedlongorder){charpath[]="/dev/shm/ring-buffer-XXXXXX";intfile_descriptor;void*address;intstatus;file_descriptor=mkstemp(path);if(file_descriptor<0)report_exceptional_condition();status=unlink(path);if(status)report_exceptional_condition();buffer->count_bytes=1UL<<order;buffer->write_offset_bytes=0;buffer->read_offset_bytes=0;status=ftruncate(file_descriptor,buffer->count_bytes);if(status)report_exceptional_condition();buffer->address=mmap(NULL,buffer->count_bytes<<1,PROT_NONE,MAP_ANONYMOUS|MAP_PRIVATE,-1,0);if(buffer->address==MAP_FAILED)report_exceptional_condition();address=mmap(buffer->address,buffer->count_bytes,PROT_READ|PROT_WRITE,MAP_FIXED|MAP_SHARED,file_descriptor,0);if(address!=buffer->address)report_exceptional_condition();address=mmap(buffer->address+buffer->count_bytes,buffer->count_bytes,PROT_READ|PROT_WRITE,MAP_FIXED|MAP_SHARED,file_descriptor,0);if(address!=buffer->address+buffer->count_bytes)report_exceptional_condition();status=close(file_descriptor);if(status)report_exceptional_condition();}voidring_buffer_free(structring_buffer*buffer){intstatus;status=munmap(buffer->address,buffer->count_bytes<<1);if(status)report_exceptional_condition();}void*ring_buffer_write_address(structring_buffer*buffer){// void pointer arithmetic is a constraint violation.returnbuffer->address+buffer->write_offset_bytes;}voidring_buffer_write_advance(structring_buffer*buffer,unsignedlongcount_bytes){buffer->write_offset_bytes+=count_bytes;}void*ring_buffer_read_address(structring_buffer*buffer){returnbuffer->address+buffer->read_offset_bytes;}voidring_buffer_read_advance(structring_buffer*buffer,unsignedlongcount_bytes){buffer->read_offset_bytes+=count_bytes;if(buffer->read_offset_bytes>=buffer->count_bytes){// 如果读指针大于等于缓冲区长度,那些读写指针同时折返回[0, buffer_size]范围内buffer->read_offset_bytes-=buffer->count_bytes;buffer->write_offset_bytes-=buffer->count_bytes;}}unsignedlongring_buffer_count_bytes(structring_buffer*buffer){returnbuffer->write_offset_bytes-buffer->read_offset_bytes;}unsignedlongring_buffer_count_free_bytes(structring_buffer*buffer){returnbuffer->count_bytes-ring_buffer_count_bytes(buffer);}voidring_buffer_clear(structring_buffer*buffer){buffer->write_offset_bytes=0;buffer->read_offset_bytes=0;}/* Note, that initial anonymous mmap() can be avoided - after initial mmap() for descriptor fd, you can try mmap() with hinted address as (buffer->address + buffer->count_bytes) and if it fails - another one with hinted address as (buffer->address - buffer->count_bytes). Make sure MAP_FIXED is not used in such case, as under certain situations it could end with segfault. The advantage of such approach is, that it avoids requirement to map twice the amount you need initially (especially useful e.g. if you want to use hugetlbfs and the allowed amount is limited) and in context of gcc/glibc - you can avoid certain feature macros (MAP_ANONYMOUS usually requires one of: _BSD_SOURCE, _SVID_SOURCE or _GNU_SOURCE). */