参考文章Linux进程PID分配策略 PID位图算法
1.耗尽PID -> 释放PID X(例如X = 15) -> 申请PID(申请到X) -> 释放PID Y(例如Y = 10) -> 申请PID(申请不到Y)
2.unsigned long *p = ((unsigned long *) addr) + (offset >> (sizeof(unsigned long) + 1)); // 仅在sizeof(unsigned long)等于4时成立
功能扩展
Linux C ID分配 由 张向龙 创作,采用 知识共享 署名-非商业性使用-相同方式共享 3.0 中国大陆 许可协议进行许可。
基于 http://blog.chinaunix.net/uid-22566367-id-2845765.html上的作品创作。
作者详细分析了PID位图算法,请先阅读原文。
本文修正了两处BUG并进行了功能扩展。
BUGS1.耗尽PID -> 释放PID X(例如X = 15) -> 申请PID(申请到X) -> 释放PID Y(例如Y = 10) -> 申请PID(申请不到Y)
2.unsigned long *p = ((unsigned long *) addr) + (offset >> (sizeof(unsigned long) + 1)); // 仅在sizeof(unsigned long)等于4时成立
功能扩展
添加了id binding parameter
C代码如下
/*
* xid.h
*
* Created on: 2013-1-4
* Author: edgar
*/
#ifndef XID_H_
#define XID_H_
#include "xdeclare.h"
EXTERN_C_OPENING
/* page size = 2^12 = 4096 = 4K */
#define PAGE_SHIFT 12
#define PAGE_SIZE (1UL << PAGE_SHIFT)
/* bits per byte = 8 */
#define BITS_PER_BYTE 8
/* bits per page = 2^15 = 32768 = 32K */
#define BITS_PER_PAGE (PAGE_SIZE * BITS_PER_BYTE)
/* bits per page mask = 7fff = 0111 1111 1111 1111 */
#define BITS_PER_PAGE_MASK (BITS_PER_PAGE - 1)
/* invalid id = -1 */
#define ID_INVALID -1
/* max id = 2^15 = 32768 */
#define ID_MAX BITS_PER_PAGE
/* is id valid */
#define ID_IS_VALID(ID) ((ID) >= 0 && (ID) < ID_MAX)
/* is with binding */
#define WITH_BINDING(PRODUCER) \
(NULL != (PRODUCER)->args)
#define WITH_ARG_AUTO_FREE(PRODUCER) \
(NULL != (PRODUCER)->arg_auto_free_func)
typedef int (*arg_auto_free)(void *);
struct _idproducer {
int last_id; /* last allocate id */
unsigned int nfree; /* number of remainder id */
unsigned int nshift; /* right shift number of offset */
unsigned int ncolumn; /* number of columns */
char page[PAGE_SIZE]; /* see as a table */
void **args; /* id binding */
arg_auto_free arg_auto_free_func;
};
typedef struct _idproducer * idproducer;
/* If arg1 is TRUE, set arg2 NULL or custom arg_auto_free function.
* If arg1 is FALSE, set arg2 NULL. */
extern idproducer idproducer_new(int, arg_auto_free);
extern int idproducer_del(idproducer);
/* If id producer is created without binding, set arg2 NULL.
* If with binding, set arg2 NULL or custom argument. */
extern int idproducer_alloc(idproducer, void *);
/* If id producer is created without binding, set arg3 NULL.
* If with binding and
* 1. (arg3 == NULL && arg_auto_free_func == NULL) is true,
* do nothing with binding(may cause memory leaks).
* 2. (arg3 == NULL && arg_auto_free_func != NULL) is true,
* auto free binding.
* 3. (arg3 != NULL) is true, set *arg3 = binding rather
* than free it.
*
* Note: id must have been returned by an earlier call to idproducer_alloc */
extern int idproducer_free(idproducer, int, void **);
EXTERN_C_CLOSING
#endif /* XID_H_ */
/*
* xid.c
*
* Created on: 2013-1-4
* Author: edgar
*/
#include "xmemory.h"
#include "xlog.h"
#include "xid.h"
#define Row(Offset, Producer) \
(((unsigned long *) Producer->page) + (Offset >> Producer->nshift))
#define Col(Offset, Producer) \
(1UL << (Offset & (Producer->ncolumn - 1)))
/* Only find next zero bit between offset and BITS_PER_PAGE, and
* return BITS_PER_PAGE if no zero bit. */
static int find_next_zero_bit(int offset, idproducer producer) {
unsigned long *row;
unsigned long col;
for (; offset < BITS_PER_PAGE; ++offset) {
row = Row(offset, producer);
col = Col(offset, producer);
if (~(*row) & col)
break;
}
return offset;
}
/* On success, return 0. */
static int test_and_set_bit(int offset, idproducer producer) {
unsigned long *row = Row(offset, producer);
unsigned long col = Col(offset, producer);
unsigned long old = *row;
*row = old | col;
return (old & col) != 0;
}
/* On success, return 0. */
static int test_and_clr_bit(int offset, idproducer producer) {
unsigned long *row = Row(offset, producer);
unsigned long col = Col(offset, producer);
unsigned long old = *row;
*row = old & ~col;
return (old & col) == 0;
}
idproducer idproducer_new(int binding, arg_auto_free arg_auto_free_func) {
idproducer producer = (idproducer) XCALLOC(0, sizeof(struct _idproducer));
producer->last_id = ID_INVALID;
producer->nfree = ID_MAX;
producer->ncolumn = sizeof(unsigned long) * BITS_PER_BYTE;
producer->nshift = __builtin_ctz(~(producer->ncolumn - 1));
/* because of using calloc in XCALLOC we need not clear
* other members in producer here */
if (TRUE == binding) {
producer->args = (void **) XCALLOC(0, sizeof(void *) * BITS_PER_PAGE);
if (NULL == (producer->arg_auto_free_func = arg_auto_free_func))
WARNING("This is an id producer with binding, no argument "
"auto free function warning.");
}
return producer;
}
int idproducer_del(idproducer producer) {
unsigned int i;
do {
if (!WITH_BINDING(producer))
break;
if (WITH_ARG_AUTO_FREE(producer))
for (i = 0; i < BITS_PER_PAGE; ++i)
if (NULL != producer->args[i])
(*producer->arg_auto_free_func)(producer->args[i]);
XFREE(0, producer->args);
} while (0);
XFREE(0, producer);
return 0;
}
int idproducer_alloc(idproducer producer, void *arg) {
/* set offset to zero if last_id + 1 equals to BITS_PER_PAGE */
int offset = (producer->last_id + 1) & BITS_PER_PAGE_MASK;
while (producer->nfree) {
offset = find_next_zero_bit(offset, producer);
if (BITS_PER_PAGE == offset) {
offset = 0;
continue;
}
if (!test_and_set_bit(offset, producer)) {
--producer->nfree;
producer->last_id = offset;
if (WITH_BINDING(producer))
producer->args[offset] = arg;
return offset;
}
break;
}
return ID_INVALID;
}
int idproducer_free(idproducer producer, int id, void **arg) {
if (test_and_clr_bit(id & BITS_PER_PAGE_MASK, producer))
return -1;
++producer->nfree;
if (WITH_BINDING(producer)) {
if (NULL != arg)
*arg = producer->args[id];
else if (WITH_ARG_AUTO_FREE(producer) && NULL != producer->args[id])
(*producer->arg_auto_free_func)(producer->args[id]);
producer->args[id] = NULL;
}
return 0;
}
Linux C ID分配 由 张向龙 创作,采用 知识共享 署名-非商业性使用-相同方式共享 3.0 中国大陆 许可协议进行许可。
基于 http://blog.chinaunix.net/uid-22566367-id-2845765.html上的作品创作。