Linux syslog机制

2019-07-12 17:15发布

本来是希望在嵌入式Linux上做一个内核的日志保留功能,来定位内核无故重启的问题。 将所有的printk信息全部保存到分区文件中,这样,即使系统重启,起来后可以查看相关重启信息 于是就鲁莽的在内核的printk处添加了如下代码(kernel/printk.c) #include +/*log to file add by zhangjj 2016-7-27*/ +#include +#include +#include +/*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 ",result); + } + else + { + //printk("Write Log File Error "); + } + + + } + else + { + //printk("Create New Log file failtrue!! "); + } + //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 #include #include #include #include #include #include #include #include #include #include #include #include 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 = AF_UNIX; (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内容输出到文件中。