要生成原子日志器,就必须用单个的write调用来输出我们希望在日志中同时出现的信息。必须用标识符O_APPEND打开文件。(有这个,每次在写操作之前,文件偏移都会被设置到文件的末尾,而且在修改文件偏移和写操作之间不会出现干扰性的文件修改操作)。使用日志器,首先调用atomic_log_open创建日志文件。在完成了所有的日志操作后由,
atomic_log_close()关闭。日志器提供三种写入日志的格式:atomic_log_array,atomic_log_string ,atomic_log_printf写入的条目存储在一个临时缓冲区中。其中,前两个不处理字符串的结束符('\0'),后一个保存字符串结束符。在调用atomic_log_send时,日志器用单个操作write将整个缓冲区的内容输出,并释放临时缓冲区,atomic_loc_clear清除临时缓冲区。
日志库代码如下:
头文件atomic_logger.h
int atomic_log_array(char *s, int len);
int atomic_log_clear();
int atomic_log_close();
int atomic_log_open(char *fn);
int atomic_log_printf(char *fmt, ...);//可变参数列表
int atomic_log_send();
int atomic_log_string(char *s);
实现文件:atomic_logger.c
//原子日志模块的实现(其实是原子日志库)
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>//string operators function
#include <unistd.h>
#include <sys/stat.h>//file state flags
#define FILE_PERMS (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
#define OPEN_FLAGS (O_WRONLY | O_APPEND | O_CREAT)
typedef struct list
{
char *entry;
int len;
struct list *next;
}list;
static int fd = -1;
static list *first = NULL;
static list *last = NULL;
/*----------------------------------------------------
*private function
*/
/*this is the same as write , but restarts if interrupted by a signal*/
static ssize_t my_write(int fd, void *buf, size_t size)
{
ssize_t bytes;
while(((bytes = write(fd, buf, size)) == -1) && (errno == EINTR));
return bytes;
}
/* Insert an entry with the given len field, but allocate extra bytes */
/* Return a pointer to the new entry on success or NULL on failure. */
static list *insert_new_entry(int len, int extra)
{
char *new_str;
list *new_entry;
new_entry = (list*) malloc(sizeof(list) + len +extra);//?
if(new_entry == NULL)
return NULL;
new_str = (char*) new_entry + sizeof(list);//?
new_entry->entry = new_str;
new_entry->next = NULL;
new_entry->len = len;
if(last == NULL)
first = new_entry;
else
last->next = new_entry;
last = new_entry;
return new_entry;
}
/* Return the sum of the lengths of all the entries. */
static int get_length()
{
int len = 0;
list *current;
current = first;
while (current != NULL)
{
len += current->len;
current = current->next;
}
return len;
}
/* Clear the list and free all the space. */
static void clear()
{
list *current;
list *free_entry;
current = first;
while(current != NULL)
{
free_entry = current;
current = current->next;
free(free_entry);
}
first = NULL;
last = NULL;
}
/*---------------------------------------------------------
* Public functions
*/
/* Open the given file for logging. */
/* If successful, return 0. Otherwise, return -1 with errno set. */
int atomic_log_open(char *fn)
{
while(fd = open(fn, OPEN_FLAGS, FILE_PERMS), fd == -1 && errno == EINTR);//
if(fd < 0) //open operator really is occured.
return -1;
return 0;
}
/* Insert the given array with given size in the list. */
/* If successful , return 0. Othrewise, return -1 with errno set. */
int atomic_log_array(char *s, int len)
{
list *new_entry;
if(fd < 0)
{
errno = EINVAL;
return -1;
}
new_entry = insert_new_entry(len, 1);
if(new_entry == NULL)
return -1;
(void)memcpy(new_entry->entry, s, len);//memcpy will copy len bytes src to dest,
return 0; //without considering the \0 among the string
}
/* Insert the given string in the list. */
/* Do not include the string terminator. */
/* If succesful, return 0. Otherwise, return -1 with errno set. */
int atomic_log_string(char *s)
{
return atomic_log_array(s, strlen(s));
}
/* Insert an entry in the list. */
/* The syntax is similar to printf. */
/* Include the string terminator but do not count it in the length. */
/* If successful, return 0. Otherwise , return -1 with errno set. */
int atomic_log_printf(char *fmt, ...)
{
va_list ap; //可变参数使用 查看链接 http://blog.csdn.net/djinglan/article/details/8425768
char ch;
int len;
list *new_entry;
if(fd < 0)
{
errno = EINVAL;
return -1;
}
va_start(ap, fmt); //
len = vsnprintf(&ch, 1, fmt, ap);//?
new_entry = insert_new_entry(len, 1);
if(new_entry = NULL)
return -1;
vsprintf(new_entry->entry, fmt, ap);//?
return 0;
}
/* Attemp to log the entries list with a single writes. */
/* clear the list if successful. */
/* If successful , return 0, Otherwise, return -1 with errno set. */
/* If the entire list cannot be logged with a single write. this is */
/* considered a failure. */
int atomic_log_send()
{
char *buf;
list *current;
int len;
if(fd < 0)
{
errno = EINVAL;
return -1;
}
len = get_length();
if(len == 0)
return 0;
buf = (char *)malloc(len);
if(buf == NULL)
return -1;
current = first;
len = 0;
while(current != NULL)
{
(void)memcpy(buf+len, current->entry, current->len);
len += current->len;
current = current->next;
}
if(my_write(fd, buf, len) != len)
{
free(buf);
errno = EAGAIN;
return -1;
}
free(buf);
clear();
return 0;
}
/* Clear the list and free all the space without logging anything */
int atomic_log_close()
{
int retval;
clear();
while(retval = close(fd), retval == -1 && errno == EINTR);
return retval;
}
以上是原子日志库,它的使用方法如下:
1、将日志文件的名字作为参数来调用atomic_log_open
2、调用atomic_log_array, atomic_log_string , 或atomic_log_printf任一函数创建消息片。
3、调用atomic_log_send来记录消息。这个日志删除了已经保存在日志器中的消息片。
4、只要需要重复2、3.
5、用atomic_log_clear来丢弃到目前为止产生的消息片,而不发送这些消息。
6、文件日志完成时,用atomic_log_close.
记录下来的每个消息片都被放入一个链表中。函数atomic_log_send分配一个足够大的、连续的块来装载所有的消息片,将消息片拷贝到这个块中去,用单个write将
日志发送到日志文件中去。
日志库的使用:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "atomic_logger.h"
#define BUFSIZE 1024
int main(int argc, char *argv[])
{
char buf[BUFSIZE];
pid_t childpid = 0;
int i, n;
if(argc != 3)
{
fprintf(stderr, "Usage: %s process filename\n", argv[0]);
return 1;
}
n = atoi(argv[1]);
for(i=1; i<n; i++)
if(childpid = fork())
break;
if(childpid == -1)
{
fprintf(stderr,"Failed to fork.\n");
return 1;
}
if(atomic_log_open(argv[2]) == -1)
{
fprintf(stderr, "failed to open log file.\n");
return 1;
}
sprintf(buf, "i:%d process:%ld", i, (long)getpid());
atomic_log_array(buf, strlen(buf));
sprintf(buf,"parent:%ld child:%ld\n", (long)getppid(), (long)childpid);
atomic_log_string(buf);
if(atomic_log_send() == -1)
{
fprintf(stderr, "failed to send to log file.\n");
return 1;
}
atomic_log_close();
return 0;
}