内核日志和内核控制台
--
1) 内核控制台可看成内核本身用来进行人机交互的IO设备, 其驱动设备用console结构描述,
用register_console()注册. 内核控制台的文件接口为tty文件接口(/dev/console),
设备号为0x0501, 内核init线程打开此文件作为用户init进程的标准IO文件描述符.
打开/dev/console时, tty_open()通过console设备的device操作获取实际的终端设备号.
可以用内核命令行"console=tty??,参数表"使/dev/console指向特定的终端设备. 另外,
X控制台程序(xconsole)将/dev/console目标终端的输出定向到某个伪终端设备来显示用户写入/de
v/console的信息.
--
2) printk()格式化输出的信息, 首先写入16k字节的环形日志缓冲区(log_buf).
日志缓冲区的每一行文本开头具有级别标记, 级别值越小则重要性越高. 系统定义了8个消息级别,
级别号从0到7分别为致命级(KERN_EMESG), 警戒级(KERN_ALERT), 临界级(KERN_CRIT),
错误级(KERN_ERR), 告警级(KERN_WARN), 注意级(KERN_NOTICE), 通知级(KERN_INFO),
调试级(KERN_DEBUG). 如果printk()输出未定义消息级别,
则使用缺省消息级别(default_message_loglevel)标记, 其初值为告警级.
当消息级别值小于控制台显示级别(console_loglevel)时,
消息体通过console设备的write操作输出到系统所有已注册的内核控制台上.
缺省的显示级(default_console_loglevel)为调试级, 显示除了调试消息的所有信息,
它可以用syslog()设置. 最小允许显示级别由minimum_console_loglevel限定, 初值为警界级,
禁止打印除了致命消息以外的所有消息.
--
3) 对于内核显示控制台来说, 其打印操作为vt_console_print,
与虚拟控制台对VT102转义序列的完整实现不同, 它只处理回车和换行两种控制码,
然后通过显示驱动接口(consw)将消息打印到当前虚拟控制台的屏幕上,
可以用ioctl()将其重定向到特定的虚拟控制台上(在Linux安装程序中可以看到这种特色的使用).
--
4) 内核日志程序klogd利用/proc/kmsg或syslog(2,buf,count)读取内核最新生成的日志信息,
将它们转发到日志服务器(syslogd), 记录到日志文件.
dmesg利用syslog(3,buf,count)读取内核环形缓冲区的所有缓冲消息.
--
#define KERN_EMERG "<0>" /* system is unusable */
#define KERN_ALERT "<1>" /* action must be taken immediately */
#define KERN_CRIT "<2>" /* critical conditions */
#define KERN_ERR "<3>" /* error conditions */
#define KERN_WARNING "<4>" /* warning conditions */
#define KERN_NOTICE "<5>" /* normal but significant condition */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */
/* printk's without a loglevel use this.. */
#define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */
/* We show everything that is MORE important than this.. */
#define MINIMUM_CONSOLE_LOGLEVEL 1 /* Minimum loglevel we let people use */
#define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_DEBUG */
#define MAX_CMDLINECONSOLES 8
#define LOG_BUF_LEN (16384)
#define LOG_BUF_MASK (LOG_BUF_LEN-1)
struct console
{
char name[8];
void (*write)(struct console *, const char *, unsigned);
int (*read)(struct console *, const char *, unsigned);
kdev_t (*device)(struct console *);
int (*wait_key)(struct console *);
void (*unblank)(void);
int (*setup)(struct console *, char *);
short flags;
short index; 内核控制台的终端子设备索引
int cflag;
struct console *next;
};
/*
* Array of consoles built from command line options (console=)
*/
struct console_cmdline
{
char name[8]; /* Name of the driver */
int index; /* Minor dev. to use */
char *options; /* Options for the driver */
};
; kernel/printk.c:
/* Keep together for sysctl support */
int console_loglevel = DEFAULT_CONSOLE_LOGLEVEL;
int default_message_loglevel = DEFAULT_MESSAGE_LOGLEVEL;
int minimum_console_loglevel = MINIMUM_CONSOLE_LOGLEVEL;
int default_console_loglevel = DEFAULT_CONSOLE_LOGLEVEL;
spinlock_t console_lock = SPIN_LOCK_UNLOCKED;
DECLARE_WAIT_QUEUE_HEAD(log_wait);
static char buf[1024]; printk格式化缓冲区
struct console *console_drivers; 所有注册的内核控制台驱动设备链表
static char log_buf[LOG_BUF_LEN]; 环形日志缓冲区
static unsigned long log_start; 日志缓冲区相对头指针
static unsigned long logged_chars; 已记录字符数
struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES]; 命令行参数表
static int preferred_console = -1; 首选的驱动设备
unsigned long log_size; 新日志消息长度
__setup("console=", console_setup);
/*
* Setup a list of consoles. Called from init/main.c
*/
static int __init console_setup(char *str) 命令行参数处理
{
struct console_cmdline *c;
char name[sizeof(c->name)];
char *s, *options;
int i, idx;
/*
* Decode str into name, index, options.
*/
if (str[0] >= '0' && str[0] <= '9') {
strcpy(name, "ttyS"); 如果只指定了序号, 默认为串口内核控制台设备
strncpy(name + 4, str, sizeof(name) - 5);
} else
strncpy(name, str, sizeof(name) - 1);
name[sizeof(name) - 1] = 0;
if ((options = strchr(str, ',')) != NULL)
*(options++) = 0; 取逗号分隔的下一参数
#ifdef __sparc__
if (!strcmp(str, "ttya"))
strcpy(name, "ttyS0");
if (!strcmp(str, "ttyb"))
strcpy(name, "ttyS1");
#endif
for(s = name; *s; s++)
if (*s >= '0' && *s <= '9')
break;
idx = simple_strtoul(s, NULL, 10); 取内核控制台设备序号
*s = 0; 产生内核控制台设备的名称
/*
* See if this tty is not yet registered, and
* if we have a slot free.
*/
for(i = 0; i < MAX_CMDLINECONSOLES && console_cmdline.name[0]; i++)
扫描内核控制台命令行参数表
if (strcmp(console_cmdline.name, name) == 0 && 如果名称相同
console_cmdline.index == idx) { 并且序号相同
preferred_console = i; 重置首选控制台编号
return 1;
}
if (i == MAX_CMDLINECONSOLES)
return 1;
preferred_console = i; 设置首选控制台
c = &console_cmdline;
memcpy(c->name, name, sizeof(c->name)); 建立内核控制台命令行参数结构
c->options = options;
c->index = idx;
return 1;
}
asmlinkage int printk(const char *fmt, ...)
{
va_list args;
int i;
char *msg, *p, *buf_end;
int line_feed;
static
阅读(2549) | 评论(1) | 转发(0) |