本来是希望在嵌入式Linux上做一个内核的日志保留功能,来定位内核无故重启的问题。
将所有的printk信息全部保存到分区文件中,这样,即使系统重启,起来后可以查看相关重启信息
于是就鲁莽的在内核的printk处添加了如下代码(kernel/printk.c)
#include <asm/uaccess.h>
+/*log to file add by zhangjj 2016-7-27*/
+#include <linux/timer.h>
+#include <linux/timex.h>
+#include <linux/rtc.h>
+/*add end*/
/*
- * for_each_console() allows you to iterate on each console
+ * for_eadd endach_console() allows you to iterate on each console
*/
#define for_each_console(con) \
for (con = console_drivers; con != NULL; con = con->next)
@@ -687,6 +692,46 @@ static inline void printk_delay(void)
}
}
+int log2file_flag= 0;
+EXPORT_SYMBOL(log2file_flag);
+#define LOG_FILE "/home/log/logfile"
+struct file* filp = NULL;
+mm_segment_t old_fs;
+
+void print2file(char *buf, int len)
+{
+ int result = 0;
+ struct task_struct *task = NULL;
+ char *path = NULL,*ptr = NULL;
+
+
+ task = current;
+
+ //==================test read write log file=====================
+ if (filp)
+ {
+
+ result = filp->f_op->write(filp,buf,len,&filp->f_pos); //写文件
+ if (result)
+ {
+ //printk("New Log Write OK Length:%d \n",result);
+ }
+ else
+ {
+ //printk("Write Log File Error \n");
+ }
+
+
+ }
+ else
+ {
+ //printk("Create New Log file failtrue!!\n");
+ }
+ //dump_stack();
+ return;
+
+}
+
asmlinkage int vprintk(const char *fmt, va_list args)
{
int printed_len = 0;
@@ -734,7 +779,26 @@ asmlinkage int vprintk(const char *fmt, va_list args)
printed_len += vscnprintf(printk_buf + printed_len,
sizeof(printk_buf) - printed_len, fmt, args);
-
+ if(0x55 == log2file_flag)
+ {
+ if(!filp)
+ {
+ filp = filp_open(LOG_FILE,O_CREAT|O_RDWR|O_APPEND ,0600); //创建文件
+ old_fs = get_fs();
+ set_fs(get_ds());
+ }
+ print2file(printk_buf, printed_len);
+ }
+ else
+ {
+ if(filp)
+ {
+ set_fs(old_fs);
+ filp_close(filp,NULL);
+ filp = NULL;
+ }
+
+ }
p = printk_buf;
/* Do we have a loglevel in the string? */
如上,将每一条printk都写到/home/log/logfile中。简单验证可行。
但,当printk较多,达到4条/s时,内核就崩溃了。原因应该是printk效率较低再加上写文件,导致内核阻塞。
很明显这不可行。
偶然间想到了syslog,但我对这个并不熟悉,以前看过相关资料,大体知道怎样用,但没找到源码。
隐约记得syslog和klog,klog可以保存内核的log信息,这次又学习了一下,下面具体讲一下用法。
busybox中有syslog和klog,syslog是有标准的rfc的http://www.ietf.org/rfc/rfc3164.txt,但busybox里面的实现是简化版的,有些功能并不支持
其中syslog的作用是收集log信息并输出到文件。先看一下结构图
我使用的busybox版本是v1.23.2,看一下帮助信息
[root@Huahuan:home]#syslogd --help
BusyBox v1.23.2 (2016-07-29 16:43:46 CST) multi-call binary.
Usage: syslogd [OPTIONS]
System logging utility
-n Run in foreground
-R HOST[:PORT] Log to HOST:PORT (default PORT:514)
-L Log locally and via network (default is network only if -R)
-C[size_kb] Log to shared mem buffer (use logread to read it)
-K Log to kernel printk buffer (use dmesg to read it)
-O FILE Log to FILE (default:/var/log/messages, stdout if -)//这个选项指定存放log文件的位置
-s SIZE Max size (KB) before rotation (default:200KB, 0=off)//这个选项指定转储时文件的大小
-b N N rotated logs to keep (default:1, max=99, 0=purge)//转储次数
-l N Log only messages more urgent than prio N (1-8)
-S Smaller output
-D Drop duplicates
-f FILE Use FILE as config (default:/etc/syslog.conf)
现只启动syslogd,测试一下用户空间的程序是怎样发出日志并被syslogd记录的
[root@Huahuan:home]#syslogd -O /home/log.txt
编译并执行以下代码
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <syslog.h>
int main(int argc, char *argv[])
{
int ret = 0;
int fd = 0;
openlog("syslogtest", LOG_CONS|LOG_PID, 0);
syslog(LOG_DEBUG, "aaaaaaaaaaaaaa", argv[0]);
closelog();
return 0;
}
[root@Huahuan:home]#cat log.txt
Jan 22 13:23:04 Huahuan syslog.info syslogd started: BusyBox v1.23.2
Jan 22 13:23:13 Huahuan user.debug syslogtest[1247]: aaaaaaaaaaaaaa
由此可见,openlog,syslog,closelog这三个函数是标准C函数,在libc源码中都能找到。
#define _PATH_LOG "/dev/log"
static void
internal_function
openlog_internal(const char *ident, int logstat, int logfac)
{
if (ident != NULL)
LogTag = ident;
LogStat = logstat;
if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
LogFacility = logfac;
int retry = 0;
while (retry < 2) {
if (LogFile == -1) {
SyslogAddr.sun_family = <strong>AF_UNIX</strong>;
(void)strncpy(SyslogAddr.sun_path, _PATH_LOG,
sizeof(SyslogAddr.sun_path));
if (LogStat & LOG_NDELAY) {
if ((LogFile = __socket(AF_UNIX, LogType, 0)) == -1)
return;
(void)__fcntl(LogFile, F_SETFD, 1);
}
}
if (LogFile != -1 && !connected)
{
int old_errno = errno;
if (__connect(LogFile, &SyslogAddr, sizeof(SyslogAddr))
== -1)
{
int saved_errno = errno;
int fd = LogFile;
LogFile = -1;
(void)__close(fd);
__set_errno (old_errno);
if (saved_errno == EPROTOTYPE)
{
/* retry with the other type: */
LogType = (LogType == SOCK_DGRAM
? SOCK_STREAM : SOCK_DGRAM);
++retry;
continue;
}
} else
connected = 1;
}
break;
}
}
void
openlog (const char *ident, int logstat, int logfac)
{
/* Protect against multiple users and cancellation. */
__libc_cleanup_push (cancel_handler, NULL);
__libc_lock_lock (syslog_lock);
openlog_internal (ident, logstat, logfac);
__libc_cleanup_pop (1);
}
openlog函数实现了unix套接字的客户端,并通过/dev/log connect到unix server
unix server在哪创建的呢
就是上面提到的busybox中的syslog进程
下面是syslog源码中创建这个unix socket的代码
static NOINLINE int create_socket(void)
{
struct sockaddr_un sunx;
int sock_fd;
char *dev_log_name;
#if ENABLE_FEATURE_SYSTEMD
if (sd_listen_fds() == 1)
return SD_LISTEN_FDS_START;
#endif
memset(&sunx, 0, sizeof(sunx));
sunx.sun_family = AF_UNIX;
/* Unlink old /dev/log or object it points to. */
/* (if it exists, bind will fail) */
strcpy(sunx.sun_path, _PATH_LOG);
dev_log_name = xmalloc_follow_symlinks(_PATH_LOG);
if (dev_log_name) {
safe_strncpy(sunx.sun_path, dev_log_name, sizeof(sunx.sun_path));
free(dev_log_name);
}
unlink(sunx.sun_path);
sock_fd = xsocket(AF_UNIX, SOCK_DGRAM, 0);
xbind(sock_fd, (struct sockaddr *) &sunx, sizeof(sunx));
chmod(_PATH_LOG, 0666);
return sock_fd;
}
现在结构很明了了,syslogd进程启动时启动一个unix server,当其他进程调用openlog时会创建一个unix client与这个unix server建立连接
调用syslog时会将信息通过socket发送到unix server,syslogd将信息按规则打印到串口或输出到文件。
内核信息需要用到klogd,
可以在kernel/printk.c中找到。因此应该是系统调用。
SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len)
{
return do_syslog(type, buf, len, SYSLOG_FROM_CALL);
}
目前已经实现了用户空间的log存储,下面看一下内核中的log输出。
[root@Huahuan:home]#klogd --help
BusyBox v1.23.2 (2016-07-29 16:43:46 CST) multi-call binary.
Usage: klogd [-c N] [-n]
Kernel logger
-c N Print to console messages more urgent than prio N (1-8)
-n Run in foreground
启动klogd进程,然后查看log.txt,发现系统启动时的打印都输出到log.txt中了。
内核中存在一个环形的buffer,当klogd启动后会将这个buffer的内容get然后发送给syslogd,然后将log内容输出到文件中。