Linux syslog机制

本来是希望在嵌入式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内容输出到文件中。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值