最近由于项目需要在两个线程之间传递数据,就想到了内核的kfifo无锁队列,于是移植到应用层。为了使用更加方便,添加了拆包功能。原生的kfifo是字节流类型,改造之后变成了数据报类型。就是说接收方获取数据的时候要么是获取到一个完整的数据报,要么就什么都没获取到。不会有粘包问题。
头文件:
#ifndef _NO_LOCK_QUEUE_H_
#define _NO_LOCK_QUEUE_H_
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include <iostream>
using namespace std;
struct mypackage
{
unsigned int len;
unsigned char data[0];
};
class Kfifo
{
public:
Kfifo(unsigned int isize);
~Kfifo();
unsigned int get(unsigned char* buffer, unsigned int len);
unsigned int getvalue(unsigned char* buffer, unsigned int len);
unsigned int put(const unsigned char* buffer, unsigned int len);
unsigned int usedsize();
unsigned int packlen();
unsigned int getsize() { return size; };
bool packavaliable();
unsigned int getpack(unsigned char* buffer, unsigned int len);
unsigned int putpack(mypackage* pack);
unsigned int putpack(const unsigned char* _buffer, unsigned int len);
static unsigned long roundup_power_of_two(unsigned long val);
private:
inline bool is_power_of_2(unsigned long n)
{
return (n != 0 && ((n & (n - 1)) == 0));
};
inline unsigned int unused()
{
return size - (in - out);
}
private:
unsigned int size;
unsigned int in;
unsigned int out;
unsigned int mask;
unsigned char* buffer;
};
#endif
实现文件:
#include "kfifo.h"
Kfifo::~Kfifo()
{
if (buffer) free(buffer);
size = in = out = 0;
}
unsigned long Kfifo::roundup_power_of_two(unsigned long val)
{
if (val & (val - 1) == 0)
{
return val;
}
unsigned long maxulong = (unsigned long)((unsigned long)~0);
unsigned long andv = ~(maxulong & (maxulong >> 1));
while ((andv & val) == 0)
andv = andv >> 1;
return andv << 1;
}
Kfifo::Kfifo(unsigned int isize) :size(isize), in(0), out(0), mask(size - 1)
{
if (!is_power_of_2(isize))
{
size = roundup_power_of_two(isize);
}
buffer = (unsigned char*)malloc(isize);
}
unsigned int Kfifo::get(unsigned char* _buffer, unsigned int len)
{
unsigned int l;
len = min(len, in - out);
l = min(len, size - (out & (size - 1)));
memcpy(_buffer, buffer + (out & (size - 1)), l);
memcpy(_buffer + l, buffer, len - l);
unsigned char(*vec)[256] = (unsigned char(*)[256])buffer;
out += len;
return len;
}
unsigned int Kfifo::getvalue(unsigned char* _buffer, unsigned int len)
{
unsigned int l;
len = min(len, in - out);
__sync_synchronize();
l = min(len, size - (out & (size - 1)));
memcpy(_buffer, buffer + (out & (size - 1)), l);
memcpy(_buffer + l, buffer, len - l);
__sync_synchronize();
return len;
}
unsigned int Kfifo::put(const unsigned char* _buffer, unsigned int len)
{
unsigned int l;
len = min(len, size - in + out);
__sync_synchronize();
l = min(len, size - (in & (size - 1)));
memcpy(buffer + (in & (size - 1)), _buffer, l);
memcpy(buffer, _buffer + l, len - l);
__sync_synchronize();
in += len;
return len;
}
unsigned int Kfifo::usedsize()
{
return in - out;
};
bool Kfifo::packavaliable()
{
if (usedsize() < sizeof(mypackage))
{
return false;
}
unsigned int pack_len = packlen();
unsigned char(*vec)[1024] = (unsigned char(*)[1024])buffer;
return (usedsize() >= pack_len);
}
unsigned int Kfifo::putpack(mypackage* pack)
{
if (unused() < pack->len)
return 0;
return put((unsigned char*)pack, pack->len);
};
unsigned int Kfifo::putpack(const unsigned char* _buffer, unsigned int len)
{
unsigned int head_len = sizeof(mypackage);
unsigned int total_len = len + head_len;
if (unused() < total_len)
return 0;
put((unsigned char*)&total_len, head_len);
return put(_buffer, len);
};
unsigned int Kfifo::getpack(unsigned char* outbuffer, unsigned int len)
{
if (!packavaliable())
return 0;
unsigned int pack_len = packlen();
if (pack_len > len)
return 0;
return get(outbuffer, pack_len);
}
unsigned int Kfifo::packlen() {
unsigned int len;
if (getvalue((unsigned char*)&len, sizeof(len)) == 0)
return 9;
return len;
};
使用示例:
void testkfifosent(Kfifo* fifo)
{
static int count = 0;
unsigned char buf[100];
unsigned int len;
while (1)
{
for (int i = 0; i < 100; i++)
{
buf[i] = i;
count++;
}
//mypackage* pack = (mypackage*)&buf;
//pack->len = 100;
len = fifo->putpack(buf,sizeof(buf));
}
}
void testkfiforecv(Kfifo* fifo)
{
unsigned char recvbuf[200];
unsigned int len;
while (1)
{
len = fifo->getpack(recvbuf, 200);
}
}