您的位置:首页 > 运维架构

open-iscsi源码分析 -- 日志处理

2012-12-11 22:13 477 查看
在iscsid主函数中有:

log_pid = log_init(program_name, DEFAULT_AREA_SIZE, daemonize ? log_do_log_daemon : log_do_log_std, NULL);

初始化日志系统,现在进入log_init函数,对open-iscsi的日志处理方式进行详细分析:

/*

日志初始化函数

program_name:程序名称

size:日志块大小

func:记录日志的函数(通过syslog或者直接输出到终端)

*/

int log_init(char *program_name, int size,

void (*func)(int prio, void *priv, const char *fmt, va_list ap),

void *priv)

{

logdbg(stderr,"enter log_init\n");

log_name = program_name;

log_func = func;

log_func_priv = priv;

/*通过syslog记录,需要做一些初始化工作*/

if (log_func == log_do_log_daemon) {

struct sigaction sa_old;

struct sigaction sa_new;

pid_t pid;

/*打开syslog*/

openlog(log_name, 0, LOG_DAEMON);

/*设置日志的优先级,这里设置的日志优先级为log_debug*/

setlogmask (LOG_UPTO (LOG_DEBUG));

/*日志守护进程和其他进程进行通信的共享内存区域分配*/

if (logarea_init(size)) {

syslog(LOG_ERR, "logarea init failed");

return -1;

}

pid = fork();/*创建一个子进程记录日志*/

if (pid < 0) {

syslog(LOG_ERR, "starting logger failed");

exit(1);

} else if (pid) { /*父进程返回*/

syslog(LOG_WARNING,

"iSCSI logger with pid=%d started!", pid);

return pid;

}

daemon_init(); /*守护进程初始化*/

/* flush on daemon's crash */

/*设置信号捕捉函数*/

sa_new.sa_handler = (void*)catch_signal;

sigemptyset(&sa_new.sa_mask);

sa_new.sa_flags = 0;

sigaction(SIGSEGV, &sa_new, &sa_old );

sigaction(SIGTERM, &sa_new, &sa_old );

/*日志守护进程定时输出日志*/

while(1) {

log_flush();

sleep(1);

/*关闭标志设置,则退出*/

if (log_stop_daemon)

break;

}

/*结尾工作*/

__log_close();

exit(0);

}

return 0;

}

其实open-iscsi建立了一个守护进程来专门输出日志,同时建立内存共享区作为其他进程和日志守护进程的通信。将内存共享区作为一个循环消息队列,其他进程将输出日志作为一个消息块插入这个队列中,而日志守护进程则从这个队列中取出消息输出到日志文件中,核心主要就是入队列和出队列两个函数:

/*增加一个消息到共享内粗块中*/
int log_enqueue (int prio, const char * fmt, va_list ap)
{
int len, fwd;
/*消息缓冲区*/
char buff[MAX_MSG_SIZE];
struct logmsg * msg;
struct logmsg * lastmsg;

/*最后一个消息的位置*/
lastmsg = (struct logmsg *)la->tail;

/*共享内存块不为空,则将消息尾的位置后移*/
if (!la->empty) {
fwd = sizeof(struct logmsg) +
strlen((char *)&lastmsg->str) *
sizeof(char) + 1;
la->tail += fwd;
}
/*将日志记录格式化到缓冲区中*/
vsnprintf(buff, MAX_MSG_SIZE, fmt, ap);
len = strlen(buff) * sizeof(char) + 1;

/* not enough space on tail : rewind
*/
/*共享内存块的尾部没有足够的空间存放这个新消息,则将回到块的起始地址开始存放*/
if (la->head <= la->tail &&
(len + sizeof(struct logmsg)) > (la->end
- la->tail)) {
logdbg(stderr, "enqueue: rewind tail
to %p\n", la->tail);
la->tail = la->start;
}

/* not enough space on head : drop msg
*/
/*若写入消息会覆盖其他消息,则抛弃这个消息,不存放*/
if (la->head > la->tail &&
(len + sizeof(struct logmsg)) > (la->head
- la->tail)) {
logdbg(stderr, "enqueue: log area overrun,
drop msg\n");

if (!la->empty)
la->tail = lastmsg;

return 1;
}

/* ok, we can stage the msg in the area
*/
/*存入消息后,设置非空*/
la->empty = 0;
msg = (struct logmsg *)la->tail;
msg->prio = prio;
/*拷贝消息内容*/
memcpy((void *)&msg->str, buff, len);
/*设置消息循环队列*/
lastmsg->next = la->tail;
msg->next = la->head;

logdbg(stderr, "enqueue: %p, %p, %i,
%s\n", (void *)msg, msg->next,
msg->prio, (char *)&msg->str);

#if LOGDBG
dump_logarea();
#endif
return 0;
}

/*从共享内存区中取日志*/
int log_dequeue (void * buff)
{
struct logmsg * src = (struct logmsg
*)la->head;
struct logmsg * dst = (struct logmsg
*)buff;
struct logmsg * lst = (struct logmsg
*)la->tail;
int len;

/*日志为空*/
if (la->empty)
return 0;

/*第一个消息的长度*/
len = strlen((char *)&src->str) * sizeof(char)
+
sizeof(struct logmsg) + 1;

dst->prio = src->prio;
/*取出第一个消息*/
memcpy(dst, src, len);

/*改变第一个块的指针*/
if (la->tail == la->head)
la->empty = 1; /* purge the last log
msg */
else {
la->head = src->next;
lst->next = la->head;
}
logdbg(stderr, "dequeue: %p, %p, %i,
%s\n",
(void *)src, src->next, src->prio,
(char *)&src->str);

/*将取出消息的内存块清零*/
memset((void *)src, 0, len);

return len;
}

/*共享内存块的分配和释放*/

/*释放共享内存等*/
static void free_logarea (void)
{
int shmid;

if (!la)
return;

/*释放信号量集*/
if (la->semid != -1)
semctl(la->semid, 0, IPC_RMID, la->semarg);
/*释放消息缓冲区*/
if (la->buff) {
shmdt(la->buff);
shmctl(la->shmid_buff, IPC_RMID, NULL);
la->buff = NULL;
la->shmid_buff = -1;
}
/*释放消息循环队列的共享内存块*/
if (la->start) {
shmdt(la->start);
shmctl(la->shmid_msg, IPC_RMID, NULL);
la->start = NULL;
la->shmid_msg = -1;
}

/*释放la结构本身*/
shmid = la->shmid;
shmdt(la);
shmctl(shmid, IPC_RMID, NULL);
la = NULL;
}

/*创建共享内存作为其他进程和日志进程之间的通信*/
static int logarea_init (int size)
{
int shmid;

logdbg(stderr,"enter logarea_init\n");

/*创建一个共享内存区域来保存全局变量la,返回值是共享内存id*/
if ((shmid = shmget(IPC_PRIVATE, sizeof(struct
logarea),
0644 | IPC_CREAT | IPC_EXCL)) ==
-1) {
syslog(LOG_ERR, "shmget logarea failed
%d", errno);
return 1;
}

/*允许本进程访问这块共享内存区域*/
la = shmat(shmid, NULL, 0);
if (!la) {
syslog(LOG_ERR, "shmat logarea failed
%d", errno);
shmctl(shmid, IPC_RMID, NULL);
return 1;
}
/*使用logearea结构的全局变量保存共享内存块的id,起始地址、缓冲区等信息*/
la->shmid = shmid;
la->start = NULL;
la->buff = NULL;
la->semid = -1;

/*创建消息通信的共享内存块大小必须大于最大消息的大小*/
if (size < MAX_MSG_SIZE)
size = DEFAULT_AREA_SIZE;

/*创建消息通信的共享内存块*/
if ((shmid = shmget(IPC_PRIVATE, size,
0644 | IPC_CREAT | IPC_EXCL)) ==
-1) {
syslog(LOG_ERR, "shmget msg failed %d",
errno);
free_logarea();
return 1;
}

/*保存消息通信的共享内存id*/
la->shmid_msg = shmid;

/*保存消息通信的共享内存块起始地址*/
la->start = shmat(la->shmid_msg, NULL,
0);
if (!la->start) {
syslog(LOG_ERR, "shmat msg failed %d",
errno);
free_logarea();
return 1;
}
/*将消息通信的共享内存块清零*/
memset(la->start, 0, size);

/*设置消息通信的共享内存块的状态和起始、结束地址*/
la->empty = 1;
la->end = la->start + size;
la->head = la->start;
la->tail = la->start;

/*创建一块共享内存块来作为消息的缓冲区*/
if ((shmid = shmget(IPC_PRIVATE, MAX_MSG_SIZE
+ sizeof(struct logmsg),
0644 | IPC_CREAT | IPC_EXCL)) ==
-1) {
syslog(LOG_ERR, "shmget logmsg failed
%d", errno);
free_logarea();
return 1;
}
/*允许本进程访问这块共享内存,并返回块的起始地址*/
la->buff = shmat(shmid, NULL, 0);
if (!la->buff) {
syslog(LOG_ERR, "shmat logmsgfailed %d",
errno);
free_logarea();
return 1;
}

/*创建一个新的信号量集,这里这个创建的信号量集只有一个信号量*/
if ((la->semid = semget(SEMKEY, 1, 0600
| IPC_CREAT)) < 0) {
syslog(LOG_ERR, "semget failed %d", errno);
free_logarea();
return 1;
}

/*设置创建的信号量集只指定信号量的值,这里设置这个唯一的信号量的值为0*/
la->semarg.val=1;
if (semctl(la->semid, 0, SETVAL, la->semarg)
< 0) {
syslog(LOG_ERR, "semctl failed %d", errno);
free_logarea();
return 1;
}

/*设置消息缓冲区*/
la->shmid_buff = shmid;
la->ops[0].sem_num = 0;
la->ops[0].sem_flg = 0;

return 0;

}

还有关于日志处理的其他一些函数,在这里一起分析:

/*调用syslog输出日志*/
static void log_syslog (void * buff)
{
struct logmsg * msg = (struct logmsg
*)buff;

syslog(msg->prio, "%s", (char *)&msg->str);
}

/*通过守护进程模式输出日志*/
void log_do_log_daemon(int prio, void *priv, const char *fmt, va_list ap)
{
struct sembuf ops[1];

ops[0].sem_num = la->ops[0].sem_num;
ops[0].sem_flg = la->ops[0].sem_flg;

ops[0].sem_op = -1;
if (semop(la->semid, ops, 1) < 0) {
syslog(LOG_ERR, "semop down failed %d",
errno);
return;
}

log_enqueue(prio, fmt, ap);

ops[0].sem_op = 1;
if (semop(la->semid, ops, 1) < 0)
syslog(LOG_ERR, "semop up failed");
}

/*直接通过标准输出,输出日志*/
void log_do_log_std(int prio, void *priv, const char *fmt, va_list ap)
{
if (prio == LOG_INFO) {
vfprintf(stdout, fmt, ap);
fprintf(stdout, "\n");
} else {
fprintf(stderr, "%s: ", log_name);
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
fflush(stderr);
}
}
/*根据设置的日志处理函数,处理log_warn日志*/
void log_warning(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
log_func(LOG_WARNING, log_func_priv,
fmt, ap);
va_end(ap);
}
/*根据设置的日志处理函数,处理log_err日志*/
void log_error(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
log_func(LOG_ERR, log_func_priv, fmt,
ap);
va_end(ap);
}

/*根据设置的日志处理函数,处理log_debug日志*/
void log_debug(int level, const char *fmt, ...)
{
if (log_level > level) {
va_list ap;
va_start(ap, fmt);
log_func(LOG_DEBUG, log_func_priv, fmt,
ap);
va_end(ap);
}
}

/*根据设置的日志处理函数,处理log_info日志*/
void log_info(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
log_func(LOG_INFO, log_func_priv, fmt,
ap);
va_end(ap);
}

#if 0 /* Unused */
static void __dump_line(int level, unsigned char *buf, int *cp)
{
char line[16*3+5], *lp = line;
int i, cnt;

cnt = *cp;
if (!cnt)
return;
for (i = 0; i < 16; i++) {
if (i < cnt)
lp += sprintf(lp, " %02x", buf[i]);
else
lp += sprintf(lp, " ");
if ((i % 4) == 3)
lp += sprintf(lp, " |");
if (i >= cnt || !isprint(buf[i]))
buf[i] = ' ';
}
log_debug(level, "%s %.16s |", line,
buf);
*cp = 0;
}

static void __dump_char(int level, unsigned char *buf, int *cp, int ch)
{
int cnt = (*cp)++;

buf[cnt] = ch;
if (cnt == 15)
__dump_line(level, buf, cp);
}

#define dump_line() __dump_line(level, char_buf, &char_cnt)
#define dump_char(ch) __dump_char(level, char_buf, &char_cnt, ch)
#endif /* Unused */

/*将共享内存区的日志通过syslog输出到文件中*/
static void log_flush(void)
{
int msglen;
struct sembuf ops[1];

ops[0].sem_num = la->ops[0].sem_num;
ops[0].sem_flg = la->ops[0].sem_flg;

while (!la->empty) {
/*获取信号量*/
ops[0].sem_op = -1;
if (semop(la->semid, ops, 1) < 0) {
syslog(LOG_ERR, "semop down failed %d",
errno);
exit(1);
}
/*从共享内存区获取一个新消息*/
msglen = log_dequeue(la->buff);

/*释放信号量*/
ops[0].sem_op = 1;
if (semop(la->semid, ops, 1) < 0) {
syslog(LOG_ERR, "semop up failed");
exit(1);
}
/*取到消息则输出*/
if (msglen)
log_syslog(la->buff);
}
}

/*消息捕捉函数,处理终止信号*/
static void catch_signal(int signo)
{
switch (signo) {
case SIGSEGV:
log_flush();
break;
case SIGTERM:
log_stop_daemon = 1;
break;
}

log_debug(1, "pid %d caught signal -%d",
getpid(), signo);
}

/*关闭日志*/
static void __log_close(void)
{
if (log_func == log_do_log_daemon) {
log_flush();
closelog();
free_logarea();
}
}

/*关闭日志*/
void log_close(pid_t pid)
{
int status;

/*非守护进程模式,则输出日志,然后关闭日志*/
if (log_func != log_do_log_daemon ||
pid < 0) {
__log_close();
return;
}

/*守护进程模式,则发消息,让守护进程自己处理*/
if (pid > 0) {
kill(pid, SIGTERM);
waitpid(pid, &status, 0);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: