操作系统(UNIX与Linux内核信息输出过程比较)[2]

原创 2003年09月30日 00:25:00

 

. Linux部分(printk<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 

Linux中,完成类似工作的,是 linux/kernel/printk.c 中的过程 printk。这个过程,相对来讲,就复杂的多。其中程序代码为(在过程代码之前,有些全局变量的定义省略,需要指出时我们再指出):

/*

 *  linux/kernel/printk.c

 *

 *  Copyright (C) 1991, 1992  Linus Torvalds

 *

 * Modified to make sys_syslog() more flexible: added commands to

 * return the last 4k of kernel messages, regardless of whether

 * they've been read or not.  Added option to suppress kernel printk's

 * to the console.  Added hook for sending the console messages

 * elsewhere, in preparation for a serial line console (someday).

 * Ted Ts'o, 2/11/93.

 * Modified for sysctl support, 1/8/97, Chris Horn.

 */

spinlock_t console_lock;/*结构体变量,控制台锁*/

001 asmlinkage int printk(const char *fmt, ...)

002 {

003     va_list args;

004     int i;

005     char *msg, *p, *buf_end;

006     int line_feed;

007     static signed char msg_level = -1; /* 当前消息的日志等级*/

008     long flags;

009

010     spin_lock_irqsave(&console_lock, flags);

011     va_start(args, fmt);

012     i = vsprintf(buf + 3, fmt, args); /* hopefully i < sizeof(buf)-4 */

013     buf_end = buf + 3 + i;

014     va_end(args);

015     for (p = buf + 3; p < buf_end; p++) {

016          msg = p;

017          if (msg_level < 0) {

018               if (

019                    p[0] != '<' ||

020                    p[1] < '0' ||

021                    p[1] > '7' ||

022                    p[2] != '>'

023                ) {

024                    p -= 3;

025                    p[0] = '<';

026                    p[1] = default_message_loglevel + '0';

027                    p[2] = '>';

028                } else

029                    msg += 3;

030                msg_level = p[1] - '0';

031           }

032           line_feed = 0;

033           for (; p < buf_end; p++) {

034                log_buf[(log_start+log_size) & (LOG_BUF_LEN-1)] = *p;

035                if (log_size < LOG_BUF_LEN)

036                    log_size++;

037                else {

038                    log_start++;

039                    log_start &= LOG_BUF_LEN-1;

040                }

041                logged_chars++;

042                if (*p == '/n') {

043                    line_feed = 1;

044                    break;

045                }

046           }

047           if (msg_level < console_loglevel && console_drivers) {

048                struct console *c = console_drivers;

049                while(c) {

050                    if ((c->flags & CON_ENABLED) && c->write)

051                         c->write(c, msg, p - msg + line_feed);

052                    c = c->next;

053                }

054           }

055           if (line_feed)

056               msg_level = -1;

057      }

058      spin_unlock_irqrestore(&console_lock, flags);

059      wake_up_interruptible(&log_wait);

060      return i;

061 }

062

这一过程的设计,有些知识需要加以介绍。

首先,printk是内核内部消息日志记录函数。调用printk的一般为紧急事件、调试或普通信息。它与过程printf的参数比较类似,都是一个格式化字符串,并把其后若干个参数(包括0个)加入此字符串中。在printk中,格式化字符串可能是以一组“〈N〉”开始,其中0<=N<=7,而该数字区分了消息的日志等级(log level),只有当日志等级高于当前控制台定义的日志等级(console_loglevel)时,才会打印输出消息。

关于程序,其中要说明的是,spin_lock_irqsave是一个宏,定义如下:

/* 在文件 spinlock.h 中*/

     #define spin_lock_irqsave(lock, flags) /

do { save_flags(flags); cli(); } while (0)

这个宏定义很有意思,使用do-while结构把目标封装起来。这是一个技巧,可以避免宏带来的歧义。而作用是,获取控制态锁,并初始化flagsva_start和va_end也是宏,与一个特殊类型va_list进行处理,把printk参数中的“”部分存入args(011至014)。

全局变量给出如下几个定义:

#define LOG_BUF_LEN (16384)/* log_buf 's length */

static char buf[1024];

unsigned long log_size = 0;

struct console *console_drivers = NULL;

static char log_buf[LOG_BUF_LEN];

static unsigned long log_start = 0;

static unsigned long logged_chars = 0;

而其中console是一个结构体,定义如下:

/* 在文件 console.h 中*/

     struct console

{

         void (*write)(struct console *, const char *, unsigned);

}

如此,您便可以暂时回去继续看一看这段程序,以己之力,来辨伊人。

 

对于Linux内核的这个过程,下面我们来进行详细的分析。

为方便讨论,我们给出下图,作为叙述的参考:

 

<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />

014结束时,我们已经获得控制台锁,并且,printk的参数“”部分(如果有)与fmt部分都装入buf[]内。其中012行的vsprintfLinux内核自己所实现的过程,向buf中写入格式化字符串并返回所写入字符串的长度(不包括最后终止字符0字节),由调用情况来看,忽略了buf的前三个字符,分析后面的代码就会知道原因。

015 057  外层for循环,具体的处理消息字符串。

016  msg记录 buf+3的位置。

017  当前消息日志的等级msg_level是静态变量,被初始化为-1。所以首次调用printk时,017至031肯定会被执行的。

     如果快速的扫描程序,您会发现改变msg_level值的机会只有两个一是在030最后一次在056如果line_feed的值为1,则msg_level的值又赋值为-1

018 029  这个if判断非常有意思。这里重新审视buf[]的前三个字符(一个子串)。只要不是“〈N〉”,其中0<=N<=7,就需要对这个子串重新设置,消息日志的等级被置为default_message_loglevel(024至027);否则,msg后移三位。

030  msg_level记录当前消息日志的等级(为17的数字之一)!

032  line_feed初始化为0line_feed其实记录的是当前已处理消息的行数。

033 046  是内层for循环。这里更有意思,也凸现出c语言的能力,及Linux内核代码编写者运用语言的能力。注意此时开始pmsg的指向,前者指向日志等级序列,后者指向紧跟其后的消息文本。

034  缓冲区log_buf[]是不是看起来容易让人糊涂?这里的按位与(&)运算是什么意思?是快速求模运算(%)!当然,需要有个保障,就是LOG_BUF_LEN的值要为2的幂。这样,log_buf[]其实是个循环缓冲器。它将记录最新的内部消息的一行。

035 040  就是对循环缓冲器访问机制的重新设置,使log_start+log_size在循环取模LOG_BUF_LEN-1)的意义上后移一位。

041  logged_chars是全局静态变量,记录的是机器启动后由printk写入的所有字符的个数,这里自增1。

042 045  是内循环退出的另一出口,如果当前p所指内容为‘/n’,表明消息一行即将结束。则line_feed记录为1,退出内层for循环。

从这里可以回头看看这个内层for循环所做的工作。可以知道,每次内层for循环开始,都开始一个新的打印行。只不过通常printk只用于打印一行,所以内层for循环通常也只执行一次。

047 054  打印一行消息的工作。047的判断若为真,则表明当前消息的日志等级高于当前控制台定义的日志等级,且控制台驱动链表console_drivers可以打开。其后就是遍历console_drivers告知每一个控制台驱动去打印当前行。我们不讨论这个过程的详细情况,只提醒您051传入的第二个参数是msg而不是p,这样就在没有日志等级序列的情况下写入了消息。以后若需要日志等级,可以访问log_buf[]。

055 056  如果line_feed为1,则一行已经结束。无论是否存在,则都可视为新的一行开始,msg_level又置为-1。

    现在,可以看看msg_level的控制逻辑。如下图(是简略图,只涉及msg_level):

 

 

     对照printk,脉络就会更清楚些。其中包括017031部分的执行,现在可以说是在每一新行的消息处理时。

058  释放控制台锁。

059  唤醒等待被写入控制台日志的所有进程。

060  返回I

061  结束。

 

这个过程虽较UNIXprintf复杂,但是两者相同的是,代码写的都很漂亮。逻辑控制无论简繁而总不失条理,充分的考虑了效率的要求,又没有失去代码的可读性。笔者称其为“清丽之意,乃介乎天生丽质,韵味内涵皆清晰可观“,即指此也。

 

操作系统(UNIX与Linux内核信息输出过程比较)[1]

 操作系统在启动之初,或检测到内部错误时,就需要向控制台输出有关信息。可以想象,这在操作系统中是潜在的常用过程。无论是UNIX,还是Linux,作为操作系统的内核部分,都尤其注意了程序的执行效率。我们...
  • PercyLee
  • PercyLee
  • 2003年09月30日 00:21
  • 989

宏内核与微内核、Linux内核与Unix内核的区别

操作系统内核可能是微内核,也可能是单内核(后者有时称之为宏内核Macrokernel)。按照类似封装的形式,这些术语定义如下: 单内核:也称为宏内核。将内核从整体上作为一个大过程实现,并同时运行在一...
  • Silencegll
  • Silencegll
  • 2016年05月25日 10:06
  • 3468

学linux内核与学linux操作系统有什么区别!?

linux内核包括:进程管理,存储管理,IO管理,文件系统等功能。linux操作系统则是linux内核再加上像shell或图形界面和其他的实用软件,比内核庞大的多。建议先学shell命令和linux下...
  • meegomeego
  • meegomeego
  • 2013年06月19日 16:34
  • 2344

UNIX操作系统中Shell程序设计

在UNIX操作系统中,若用户键入的命令参数的个数为1时,执行cat$1命令:若用户键入的命令个数为2时,执行cat>>$2 1 2 3 4 5 ...
  • lk798362252
  • lk798362252
  • 2016年04月12日 19:53
  • 592

Linux内核和传统Unix内核的比较

  所有的Unix内核都同宗同源,并且提供相同的API,现代的Unix内核存在许多设计上的相似之处。Unix内核几乎毫无例外的都是一个不可分割的静态可执行块(文件)。也就是说,它们必须以完整、单独的可...
  • armman
  • armman
  • 2007年04月13日 17:11
  • 1009

比较了几种主流unix系统的优劣

Saker新一代IT资源智能集中监管系统 得知您负责管理本单位的IT设备,贵单位的IT设备可能包括多种UNIX系统、NT(包括Windows2000)等系统;网络设备可能包括交换机、路由器等;数据库可...
  • wonderful19891024
  • wonderful19891024
  • 2010年08月06日 10:32
  • 7142

Windows Linux Unix 系统的兼容性能与操作系统之间的相对比较

  关于操作系统与什么样的平台架够什么样的软件系统,还有处理器和硬件和软件的问题,使用什么操作系统,使用哪一套操作系统和软件,关于软件平台的可操作性在什么样的平台和什么样的操作系统之下使用什么处理器,...
  • wuweiliuming
  • wuweiliuming
  • 2007年10月10日 14:46
  • 539

Unix操作系统基础:Unix文件系统之目录

Unix操作系统基础:Unix文件系统之目录 主要内容: 1. Unix文件的概念 2. Unix的目录操作 3. Unix的文件结构 4. Unix的文件操作 一、文件的概念     ...
  • sheqiguo
  • sheqiguo
  • 2012年03月13日 23:24
  • 583

UNIX操作系统类型

UNIX操作系统类型 由于Unix操作系统众所周知的稳定性、可靠性,用来提供各种Internet服务的计算机运行的操作系统占很大比例的是Unix及Unix类操作系统。目前比较常见的运行在PC机上的U...
  • zxxSsdsd
  • zxxSsdsd
  • 2014年03月26日 11:27
  • 2164

LINUX系统的调试信息是如何从串口输出的

(最近在调试安桌系统,发现安桌系统层的调试信息不能在串口终端上看到,但可以在ADB端的LOGCAT中看到,同时内核层LINUX的调试信息可以在串口终端中看到。不知道其中原因,因此决定来分析LINUX内...
  • YACHL882828
  • YACHL882828
  • 2016年09月13日 09:14
  • 1733
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:操作系统(UNIX与Linux内核信息输出过程比较)[2]
举报原因:
原因补充:

(最多只允许输入30个字)