linux 1.0 内核注解 linux/kernel/printk.c
时间:2009-03-05 来源:taozhijiangscu
/********************************************
*Created By: 陶治江
*Date: 2009-3-3
********************************************/
#include <stdarg.h> #include <asm/segment.h>
#include <asm/system.h> #include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h> #define LOG_BUF_LEN 4096 //循环的日志缓存,总共有4k的日志容量 static char buf[1024]; extern int vsprintf(char * buf, const char * fmt, va_list args);
extern void console_print(const char *); #define DEFAULT_MESSAGE_LOGLEVEL 7 /* KERN_DEBUG */
#define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything more serious than KERN_DEBUG */ unsigned long log_size = 0;
//这个程序里面,其实这个变量是一个很重要的变量
//观察可以发现,当调用sys_syslog的时候它是不断
//递减的(而且如果log_size=0还要等待睡眠),而
//在printk写入日志的时候,它是不断增加的,两者之间
//使用的睡眠进行等待,从而进行了内核的同步了 struct wait_queue * log_wait = NULL;
int console_loglevel = DEFAULT_CONSOLE_LOGLEVEL; static void (*console_print_proc)(const char *) = 0;
static char log_buf[LOG_BUF_LEN]; static unsigned long log_start = 0;
static unsigned long logged_chars = 0;
// Number of chars produced since last read+clear operation
// 也的确是这样的,最终这个变量也只有在clear下才变为0
// 而printk下它是不断长的,记录了新加入的数据 //read kernel message ring buffer
/* Commands to sys_syslog:
* 0 -- Close the log. Currently a NOP.
* 1 -- Open the log. Currently a NOP.
* 2 -- Read from the log.
* 3 -- Read up to the last 4k of messages in the ring buffer.
* 4 -- Read and clear last 4k of messages in the ring buffer
* 5 -- Clear ring buffer.
* 6 -- Disable printk's to console
* 7 -- Enable printk's to console
* 8 -- Set level of messages printed to console*/
asmlinkage int sys_syslog(int type, char * buf, int len)
{
unsigned long i, j, count;
int do_clear = 0;
char c;
int error; if ((type != 3) && !suser()) //Only function 3 is allowed to non-root processes.
return -EPERM;
switch (type) { /*功能号*/
case 0: /* Close log */
return 0;
case 1: /* Open log */
return 0;
case 2: /* Read from log
*这个功能不断等待直到内核日志缓冲不为空
*读取的日志信息将消失(这些信息只能读一次)*/
if (!buf || len < 0)
return -EINVAL;
if (!len) //不读不当做错
return 0;
error = verify_area(VERIFY_WRITE,buf,len);
if (error)
return error;
cli();
while (!log_size) { //如果log_size==0,没有日志
if (current->signal & ~current->blocked) {
/*如果当前进程有非屏蔽信号,就打开中断后返回*/
sti();
return -ERESTARTSYS;
} //否则可中断睡眠等待,是不是在等待日志啊?对的
//printk最后会唤醒它的
interruptible_sleep_on(&log_wait);
}
i = 0;
while (log_size && i < len) {
/*观测来看log_buf是日志4k块的起始,而
*log_start是日志内容的开始(注意的是
*这个4k的缓冲是循环的哈)*/
c = *((char *) log_buf+log_start); /*要读的第一个字符*/
log_start++;
log_size--;
log_start &= LOG_BUF_LEN-1; /*循环的解决方式*/
sti();
put_fs_byte(c,buf); /*将内核中的日志内容放到用户缓冲区中*/
buf++;
i++;
cli(); /*这里看出内核的日志操作是不允许中断的*/
}
sti();
return i; /*返回读取的字符个数*/
case 4: /* Read/clear last kernel messages */
do_clear = 1;
/*FALL THRU 因为4除了3的功能之外还要清除日志的
*fall through是应该的*/
case 3: /* Read last kernel messages */
if (!buf || len < 0)
return -EINVAL;
if (!len)
return 0;
error = verify_area(VERIFY_WRITE,buf,len);
if (error)
return error;
count = len;
if (count > LOG_BUF_LEN)
count = LOG_BUF_LEN; /*最多读取4k的内容*/
//感觉这里似乎是唯一有用到的地方 :-(
//由于count不能比LOG_BUF_LEN大,所以即使
//logged_chars比LOG_BUF_LEN 大,也不回超过LOG_BUF_LEN的
if (count > logged_chars)
count = logged_chars;
//因为是要最新的信息,所以读取的开头不应该是log_start了
//而是计算出来的j了
j = log_start + log_size - count;
for (i = 0; i < count; i++) {
c = *((char *) log_buf+(j++ & (LOG_BUF_LEN-1)));
put_fs_byte(c, buf++);
}
if (do_clear) /*清除标志*/
logged_chars = 0; //这里也可以看出,不需要实际清空,维持一个好的
//清晰的标记是很方便的
return i;
case 5: /* Clear ring buffer */
logged_chars = 0;
return 0;
case 6: /* Disable logging to console
* Disable printk's to console */
console_loglevel = 1; /* only panic messages shown */
return 0;
case 7: /* Enable logging to console
* Enable printk's to console */
console_loglevel = DEFAULT_CONSOLE_LOGLEVEL;
return 0;
case 8:
if (len < 0 || len > 8)
return -EINVAL;
console_loglevel = len;
return 0;
}
return -EINVAL;
}
asmlinkage int printk(const char *fmt, ...)
{
va_list args;
int i;
char *msg, *p, *buf_end;
static char msg_level = -1;
long flags; save_flags(flags);
cli();
va_start(args, fmt);
i = vsprintf(buf + 3, fmt, args); /* hopefully i < sizeof(buf)-4 */
buf_end = buf + 3 + i; //buf的大小最多是1024
va_end(args);
for (p = buf + 3; p < buf_end; p++) {
msg = p;
if (msg_level < 0) { //如果msg_level没有设置-对于刚进来的时候
if ( //这里是从字串的开始<n>进行猜测的,如果失败了就
//使用默认的msg_level
p[0] != '<' ||
p[1] < '0' ||
p[1] > '7' ||
p[2] != '>'
) {
p -= 3; //呃,这里刚好跳到了buf的头部了
p[0] = '<';
p[1] = DEFAULT_MESSAGE_LOGLEVEL - 1 + '0'; /* <6> */
p[2] = '>';
} else
msg += 3; //合法,msg指向的是字串(没有信息级别了)
msg_level = p[1] - '0';
}
for (; p < buf_end; p++) { //这里的p也是在变的,其实感觉外面的for
//用的就不太好!!
//Sorry,I am wrong!!因为这里读到了'\n'就
//跳出循环了,外边的for是要的,主要的原因可能
//是由于\n对屏幕进行的显示的要求了
log_buf[(log_start+log_size) & (LOG_BUF_LEN-1)] = *p; //全局变量
//注意下面的这个句子,因为log_buf是4k的回卷的,
//所以记录了写入的数据,如果超过了log_buf,那么
//log_start就被覆盖了,就必须转移log_start了
if (log_size < LOG_BUF_LEN)
log_size++;
else
log_start++;
logged_chars++;
if (*p == '\n')
break;
}
if (msg_level < console_loglevel && console_print_proc) {
char tmp = p[1];
p[1] = '\0';
(*console_print_proc)(msg); //应该是显示出来吧
p[1] = tmp;
}
if (*p == '\n')
msg_level = -1;
}
restore_flags(flags);
wake_up_interruptible(&log_wait); //唤醒等待的进程
return i;
} /*
* The console driver calls this routine during kernel initialization
* to register the console printing procedure with printk() and to
* print any messages that were printed by the kernel before the
* console driver was initialized.
*/
void register_console(void (*proc)(const char *))
{
int i,j;
int p = log_start;
char buf[16];
char msg_level = -1;
char *q; console_print_proc = proc; for (i=0,j=0; i < log_size; i++) {
buf[j++] = log_buf[p]; //log_start
p++;
p &= LOG_BUF_LEN-1; //回转
if (buf[j-1] != '\n' && i < log_size - 1 && j < sizeof(buf)-1)
continue;
//'\n' 一句完了
buf[j] = 0;
q = buf;
if (msg_level < 0) { //这个好像对<n>结构很自信哦
msg_level = buf[1] - '0';
q = buf + 3;
}
if (msg_level < console_loglevel)
(*proc)(q); //调用这个函数
if (buf[j-1] == '\n') //好像这个msg_level是以换行进行更改的
msg_level = -1;
j = 0;
}
}
文档地址:http://blogimg.chinaunix.net/blog/upfile2/090305140033.pdf
*Created By: 陶治江
*Date: 2009-3-3
********************************************/
#include <stdarg.h> #include <asm/segment.h>
#include <asm/system.h> #include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h> #define LOG_BUF_LEN 4096 //循环的日志缓存,总共有4k的日志容量 static char buf[1024]; extern int vsprintf(char * buf, const char * fmt, va_list args);
extern void console_print(const char *); #define DEFAULT_MESSAGE_LOGLEVEL 7 /* KERN_DEBUG */
#define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything more serious than KERN_DEBUG */ unsigned long log_size = 0;
//这个程序里面,其实这个变量是一个很重要的变量
//观察可以发现,当调用sys_syslog的时候它是不断
//递减的(而且如果log_size=0还要等待睡眠),而
//在printk写入日志的时候,它是不断增加的,两者之间
//使用的睡眠进行等待,从而进行了内核的同步了 struct wait_queue * log_wait = NULL;
int console_loglevel = DEFAULT_CONSOLE_LOGLEVEL; static void (*console_print_proc)(const char *) = 0;
static char log_buf[LOG_BUF_LEN]; static unsigned long log_start = 0;
static unsigned long logged_chars = 0;
// Number of chars produced since last read+clear operation
// 也的确是这样的,最终这个变量也只有在clear下才变为0
// 而printk下它是不断长的,记录了新加入的数据 //read kernel message ring buffer
/* Commands to sys_syslog:
* 0 -- Close the log. Currently a NOP.
* 1 -- Open the log. Currently a NOP.
* 2 -- Read from the log.
* 3 -- Read up to the last 4k of messages in the ring buffer.
* 4 -- Read and clear last 4k of messages in the ring buffer
* 5 -- Clear ring buffer.
* 6 -- Disable printk's to console
* 7 -- Enable printk's to console
* 8 -- Set level of messages printed to console*/
asmlinkage int sys_syslog(int type, char * buf, int len)
{
unsigned long i, j, count;
int do_clear = 0;
char c;
int error; if ((type != 3) && !suser()) //Only function 3 is allowed to non-root processes.
return -EPERM;
switch (type) { /*功能号*/
case 0: /* Close log */
return 0;
case 1: /* Open log */
return 0;
case 2: /* Read from log
*这个功能不断等待直到内核日志缓冲不为空
*读取的日志信息将消失(这些信息只能读一次)*/
if (!buf || len < 0)
return -EINVAL;
if (!len) //不读不当做错
return 0;
error = verify_area(VERIFY_WRITE,buf,len);
if (error)
return error;
cli();
while (!log_size) { //如果log_size==0,没有日志
if (current->signal & ~current->blocked) {
/*如果当前进程有非屏蔽信号,就打开中断后返回*/
sti();
return -ERESTARTSYS;
} //否则可中断睡眠等待,是不是在等待日志啊?对的
//printk最后会唤醒它的
interruptible_sleep_on(&log_wait);
}
i = 0;
while (log_size && i < len) {
/*观测来看log_buf是日志4k块的起始,而
*log_start是日志内容的开始(注意的是
*这个4k的缓冲是循环的哈)*/
c = *((char *) log_buf+log_start); /*要读的第一个字符*/
log_start++;
log_size--;
log_start &= LOG_BUF_LEN-1; /*循环的解决方式*/
sti();
put_fs_byte(c,buf); /*将内核中的日志内容放到用户缓冲区中*/
buf++;
i++;
cli(); /*这里看出内核的日志操作是不允许中断的*/
}
sti();
return i; /*返回读取的字符个数*/
case 4: /* Read/clear last kernel messages */
do_clear = 1;
/*FALL THRU 因为4除了3的功能之外还要清除日志的
*fall through是应该的*/
case 3: /* Read last kernel messages */
if (!buf || len < 0)
return -EINVAL;
if (!len)
return 0;
error = verify_area(VERIFY_WRITE,buf,len);
if (error)
return error;
count = len;
if (count > LOG_BUF_LEN)
count = LOG_BUF_LEN; /*最多读取4k的内容*/
//感觉这里似乎是唯一有用到的地方 :-(
//由于count不能比LOG_BUF_LEN大,所以即使
//logged_chars比LOG_BUF_LEN 大,也不回超过LOG_BUF_LEN的
if (count > logged_chars)
count = logged_chars;
//因为是要最新的信息,所以读取的开头不应该是log_start了
//而是计算出来的j了
j = log_start + log_size - count;
for (i = 0; i < count; i++) {
c = *((char *) log_buf+(j++ & (LOG_BUF_LEN-1)));
put_fs_byte(c, buf++);
}
if (do_clear) /*清除标志*/
logged_chars = 0; //这里也可以看出,不需要实际清空,维持一个好的
//清晰的标记是很方便的
return i;
case 5: /* Clear ring buffer */
logged_chars = 0;
return 0;
case 6: /* Disable logging to console
* Disable printk's to console */
console_loglevel = 1; /* only panic messages shown */
return 0;
case 7: /* Enable logging to console
* Enable printk's to console */
console_loglevel = DEFAULT_CONSOLE_LOGLEVEL;
return 0;
case 8:
if (len < 0 || len > 8)
return -EINVAL;
console_loglevel = len;
return 0;
}
return -EINVAL;
}
asmlinkage int printk(const char *fmt, ...)
{
va_list args;
int i;
char *msg, *p, *buf_end;
static char msg_level = -1;
long flags; save_flags(flags);
cli();
va_start(args, fmt);
i = vsprintf(buf + 3, fmt, args); /* hopefully i < sizeof(buf)-4 */
buf_end = buf + 3 + i; //buf的大小最多是1024
va_end(args);
for (p = buf + 3; p < buf_end; p++) {
msg = p;
if (msg_level < 0) { //如果msg_level没有设置-对于刚进来的时候
if ( //这里是从字串的开始<n>进行猜测的,如果失败了就
//使用默认的msg_level
p[0] != '<' ||
p[1] < '0' ||
p[1] > '7' ||
p[2] != '>'
) {
p -= 3; //呃,这里刚好跳到了buf的头部了
p[0] = '<';
p[1] = DEFAULT_MESSAGE_LOGLEVEL - 1 + '0'; /* <6> */
p[2] = '>';
} else
msg += 3; //合法,msg指向的是字串(没有信息级别了)
msg_level = p[1] - '0';
}
for (; p < buf_end; p++) { //这里的p也是在变的,其实感觉外面的for
//用的就不太好!!
//Sorry,I am wrong!!因为这里读到了'\n'就
//跳出循环了,外边的for是要的,主要的原因可能
//是由于\n对屏幕进行的显示的要求了
log_buf[(log_start+log_size) & (LOG_BUF_LEN-1)] = *p; //全局变量
//注意下面的这个句子,因为log_buf是4k的回卷的,
//所以记录了写入的数据,如果超过了log_buf,那么
//log_start就被覆盖了,就必须转移log_start了
if (log_size < LOG_BUF_LEN)
log_size++;
else
log_start++;
logged_chars++;
if (*p == '\n')
break;
}
if (msg_level < console_loglevel && console_print_proc) {
char tmp = p[1];
p[1] = '\0';
(*console_print_proc)(msg); //应该是显示出来吧
p[1] = tmp;
}
if (*p == '\n')
msg_level = -1;
}
restore_flags(flags);
wake_up_interruptible(&log_wait); //唤醒等待的进程
return i;
} /*
* The console driver calls this routine during kernel initialization
* to register the console printing procedure with printk() and to
* print any messages that were printed by the kernel before the
* console driver was initialized.
*/
void register_console(void (*proc)(const char *))
{
int i,j;
int p = log_start;
char buf[16];
char msg_level = -1;
char *q; console_print_proc = proc; for (i=0,j=0; i < log_size; i++) {
buf[j++] = log_buf[p]; //log_start
p++;
p &= LOG_BUF_LEN-1; //回转
if (buf[j-1] != '\n' && i < log_size - 1 && j < sizeof(buf)-1)
continue;
//'\n' 一句完了
buf[j] = 0;
q = buf;
if (msg_level < 0) { //这个好像对<n>结构很自信哦
msg_level = buf[1] - '0';
q = buf + 3;
}
if (msg_level < console_loglevel)
(*proc)(q); //调用这个函数
if (buf[j-1] == '\n') //好像这个msg_level是以换行进行更改的
msg_level = -1;
j = 0;
}
}
文档地址:http://blogimg.chinaunix.net/blog/upfile2/090305140033.pdf
相关阅读 更多 +