details of printk

详解printk

clip_image002

比较简单的一个内核程序:


#include
<linux/init.h>
#include
<linux/module.h>

MODULE_LICENSE("Dual
BSD/GPL"
);

#define KERN_LEVEL_NO
"<0>"

static int
hello_init(void)
{

printk(KERN_LEVEL_NO "Hello ,
world\n"
);

return
0;
}

static void
hello_exit(void)
{

printk(KERN_LEVEL_NO "Goodbye , curl
world\n"
);
}

module_init(hello_init);
module_exit(hello_exit);

关于printk

函数printk的使用方法和printf相似,用于内核打印消息。printk根据日志级别(loglevel)对消息进行分类。

日志级别用宏定义,日志级别宏展开为一个字符串,在编译时由预处理器将它和消息文本拼接成一个字符串,因此printk 函数中日志级别宏和格式字符串间不能有逗号。

 

比如下面是两个printk的例子,一个用于打印调试信息,另一个用于打印临界条件信息。


printk(KERN_DEBUG "Here
I am: %s:%i\n"
, _ _FILE_ _, _ _LINE_ _);
printk(KERN_CRIT "I'm
trashed; giving up on %p\n"
, ptr);

 

       我们可以查看kern_level.h文件(/usr/src/kernels/`uname -r`/include/linux,


#ifndef
__KERN_LEVELS_H__
#define
__KERN_LEVELS_H__

#define KERN_SOH
"\001"
/* ASCII Start Of Header */
#define KERN_SOH_ASCII '\001'

#define KERN_EMERG KERN_SOH
"0" /* system is
unusable */
#define KERN_ALERT KERN_SOH
"1" /* action must be
taken immediately */
#define KERN_CRIT KERN_SOH
"2" /* critical
conditions */
#define KERN_ERR
KERN_SOH "3" /*
error conditions */
#define KERN_WARNING KERN_SOH "4" /* warning conditions
*/
#define KERN_NOTICE KERN_SOH "5" /* normal but significant
condition */
#define KERN_INFO KERN_SOH
"6" /* informational
*/
#define KERN_DEBUG KERN_SOH
"7" /* debug-level
messages */

#define KERN_DEFAULT KERN_SOH "d" /* the default kernel
loglevel */

/*
*
Annotation for a "continued" line of log printout (only done after
a
* line
that had no enclosing \n). Only to be used by core/arch
code
*
during early bootup (a continued line is not SMP-safe
otherwise).
*/
#define KERN_CONT
""

#endif

 

内核可把消息打印到当前控制台上,可以指定控制台为字符模式的终端或打印机等。默认情况下,控制台就是当前的虚拟终端。

为了更好地控制不同级别的信息显示在控制台上,内核设置了控制台的日志级别console_loglevelprintk日志级别的作用是打印一定级别的消息,与之类似,控制台只显示一定级别的消息。

当日志级别小于console_loglevel时,消息才能显示出来。控制台相应的日志级别定义如下:

如果系统运行了klogdsyslogd,则无论console_loglevel为何值,内核消息都将追加到/var/log/messages中。如果klogd没有运行,消息不会传递到用户空间,只能查看/proc/kmsg。其中syslogd负责记录系统运作中,kernel或应用程式产生的各种讯息。这些讯息被写入到系统的纪录档中,让管理人员,进行故障排除、追踪尝试非法入侵的使用者、进行使用者的分析等等。而kernel产生的讯息交由klogd处理,klogd再交由syslogd处理。

所以如果需要看到相关信息,我们需要安装yum install klogsyslog

Fedora使用的syslogd叫做rsyslogd,配置文件为/etc/rsyslog.conf,在该文件中指定了syslog记录日志的信息来源、信息类型以及保存位置。

klogd所记录的信息会比syslogd 的顺序优先,原因是klogd所记录的是尚未进入操作系统的信息,但其实一开始的这些信息并不是由klogd所记录的,而是自加载kernel 时,就已经开始记录的。在系统尚未进入操作系统阶段,还在加载kernel及执行initrd时,会将信息先记录在/proc/kmsg文件中(因为在initrd阶段没有实体硬盘可供记录),等进入操作系统执行完klogd后,klogd再将/proc/kmsg的所有内容全数填入/var/log/message文件中,这也是为何在/var/log/message文件的一开始,依然可以看到刚开机在加载kernel以及initrd阶段的信息。

       查看/proc/kmsg如下所示(每次操作模块时都有消息输出):

clip_image004

The difference between library functions and system calls

库函数和系统调用的区别

       库函数是更高级别的,完全在用户空间里运行,并为程序员提供了更方便的做实际工作的函数接口。Higher levelrun in user spacemore convenient

       系统调用代表用户以内核模式工作,由操作系统本身的内核提供。In kernel mode

       库函数printf看上去类似于一般输出函数,但是它实际上只是格式化你提供给字符串的数据,并用低级系统调用write编写字符串数据,然后将数据发送到一个与终端的标准输出关联的文件中。

关于linux内核空间

关于linux内核空间

linux内核对整个系统的物理内存是通过类型为struct page的数组mem_map来管理的。系统中的伙伴系统分配算法最终是通过操作这个数组来记录物理内存的分配、回收等操作。在这里不要被系统的高端内存、低端内存等概念搞混淆了,高、低端内存的分类主要在于区分物理内存地址是否可以直接映射到内核线性地址空间中。

我们知道,linux的内核地址空间大小为1G用户空间0~3G,内核空间3G~4G,这种分法最常见),因此如果把这1G线性地址空间全部拿来直接一一映射物理内存的话,在内核态的所有进程(线程)能使用的物理内存总共最多只有1G,为了能使在内核态的所有进程能使用更多的物理内存,linux采取了一种变通的形式:它将1G内核线性地址空间分为几部分,第一部分为1G的前896M,这部分内核线性空间与物理内存的0~896M一一映射(相差一个为0xc00000003G的常数),后面128M的线性空间拿来动态映射剩下的所有物理内存,由于动态映射的方法不一样,后面的128M又分成了几个部分,有兴趣的可以查看相关资料。在这里,前面896M线性空间对应的物理内存就是所谓的低端物理内存,剩下的物理内存就是高端物理内存。

从上面高、低端物理内存命名的由来我们可以知道,高、低端物理内存与具体的内存分配算法无关,它们都是被mem_map数组控制起来,再由伙伴分配系统实施管理。

 

关于进程及其内存分配

首先要明白一个概念:进程中使用的所有地址都是虚地址linux中进程可运行在用户态和内核态,(典型配置情况下)当进程运行在用户态时,它使用的线性地址只能位于0~3G范围内,当进程运行于内核态时,它使用的线性地址地址范围为3G~4G

为了把线性地址转化为物理地址,每个进程都有自己私有的页目录和页表。linux在建立进程页目录时,把用户地址空间的页目录项(0~767项)清空而将内核页目录表(swapper_pg_dir)的第768项到1023项拷贝到进程的页目录表的第768项到1023项中。由于内核在初始化时也只映射了物理内存的前896M,我们可以知道内核也目录表只能保证第768项开始的224项中有有效映射。从这里我们可以知道,所有的进程都共享了其内核线性地址空间。

当一个进程在内核空间发生缺页故障的时候,这主要发生在访问内核空间动态映射区线性地址,在其处理程序中,就要通过0号进程的页目录(swapper_pg_dir)来同步本进程的内核页目录,实际上就是拷贝0号进程的内核页目录到本进程中(内核页表与进程0共享,故不需要复制)。如果进程0的该地址处的内核页目录也不存在,则出错,具体代码可以参考vmalloc的实现源码。

当进程运行于用户态时,若其需要申请内存空间,内核首先会在其用户线性空间中分配需要的线性地址空间,再通过伙伴分配系统分配物理内存并把分配的物理内存跟用户空间线性地址映射起来,最后再修改进程的页目录项及页表项写入这些映射关系。

 

注;

逻辑地址(Logical Address  指由程序产生的与段相关的偏移地址部分。例如,你在进行C语言指针编程中,可以读取指针变量本身值(&操作),实际上这个值就是逻辑地址,它是相 对于你当前进程数据段的地址,不和绝对物理地址相干。只有在Intel实模式下,逻辑地址才和物理地址相等(因为实模式没有分段或分页机制,Cpu不进行 自动地址转换);逻辑也就是在Intel 保护模式下程序执行代码段限长内的偏移地址(假定代码段、数据段如果完全一样)。应用程序员仅需与逻辑地址打交道,而分段和分页机制对您来说是完全透明 的,仅由系统编程人员涉及。应用程序员虽然自己可以直接操作内存,那也只能在操作系统给你分配的内存段操作。

线性地址(Linear Address 是逻辑地址到物理地址变换之间的中间层。程序代码会产生逻辑地址,或者说是段中的偏移地址,加上相应段的基地址就生成了一个线性地址。如果启用了分页机 制,那么线性地址可以再经变换以产生一个物理地址。若没有启用分页机制,那么线性地址直接就是物理地址。Intel 80386的线性地址空间容量为4G232次方即32根地址总线寻址)。

物理地址(Physical Address 是指出现在CPU外部地址总线上的寻址物理内存的地址信号,是地址变换的最终结果地址。如果启用了分页机制,那么线性地址会使用页目录和页表中的项变换成物理地址。如果没有启用分页机制,那么线性地址就直接成为物理地址了。

虚拟内存(Virtual Memory 是指计算机呈现出要比实际拥有的内存大得多的内存量。因此它允许程序员编制并运行比实际系统拥有的内存大得多的程序。这使得许多大型项目也能够在具有有限 内存资源的系统上实现。一个很恰当的比喻是:你不需要很长的轨道就可以让一列火车从上海开到北京。你只需要足够长的铁轨(比如说3公里)就可以完成这个任 务。采取的方法是把后面的铁轨立刻铺到火车的前面,只要你的操作足够快并能满足要求,列车就能象在一条完整的轨道上运行。这也就是虚拟内存管理需要完成的 任务。在Linux 0.11内核中,给每个程序(进程)都划分了总容量为64MB的虚拟内存空间。因此程序的逻辑地址范围是0x00000000x4000000

与物理地址空间类似,线性地址空间也是平坦的4GB地址空间,地址范围从00xFFFFFFFF,线性空间中含有为系统定义的所有段和系统表。

有时我们也把逻辑地址称为虚拟地址。因为与虚拟内存空间的概念类似,逻辑地址也是与实际物理内存容量无关的。

逻辑地址与物理地址的差距0xC0000000,是由于虚拟地址->线性地址->物理地址映射正好差这个值。这个值是由操作系统指定的。

 

虚拟地址到物理地址的转化方法是与体系结构相关的。一般来说有分段、分页两种方式。以现在的x86 cpu为例,分段分页都是支持的。Memory Mangement Unit负责从虚拟地址到物理地址的转化。逻辑地址是段标识+段内偏移量的形式,MMU通过查询段表,可以把逻辑地址转化为线性地址。如果cpu没有开启 分页功能,那么线性地址就是物理地址;如果cpu开启了分页功能,MMU还需要查询页表来将线性地址转化为物理地址:
逻辑地址 —-(段表)—> 线性地址(页表)—> 物理地址
不同的逻辑地址可以映射到同一个线性地址上;不同的线性地址也可以映射到同一个物理地址上;所以是多对一的关系。另外,同一个线性地址,在发生换页以后,也可能被重新装载到另外一个物理地址上。所以这种多对一的映射关系也会随时间发生变化。

ubuntu删除多余内核

ubuntu删除多余内核

如果升级到了一个新的内核,并且还比较稳定,那么老的内核就可以清理了,放在电脑里也占位置。方法(命令行比较通用)如下:

1.查看系统内存在的内核版本列表:

sudo dpkg –get-selections |grep linux

结果:

libselinux1                              install

linux-firmware                              install

linux-generic                                 install

linux-headers-3.2.0-33                       install

linux-headers-3.2.0-33-generic                install

linux-headers-3.2.0-33-generic-pae        install

linux-headers-3.2.0-34                       install

linux-headers-3.2.0-34-generic                install

linux-headers-3.2.0-34-generic-pae        install

linux-headers-3.2.0-35                       install

linux-headers-3.2.0-35-generic                install

linux-headers-3.2.0-35-generic-pae        install

linux-headers-3.2.0-37                       install

linux-headers-3.2.0-37-generic                install

linux-headers-3.2.0-37-generic-pae        install

linux-headers-3.2.0-38                       install

linux-headers-3.2.0-38-generic                install

linux-headers-3.2.0-38-generic-pae        install

linux-headers-3.2.0-40                       install

linux-headers-3.2.0-40-generic                install

linux-headers-3.2.0-40-generic-pae        install

linux-headers-generic                         install

linux-headers-generic-pae                 install

linux-image-2.6.32-21-generic                 deinstall

linux-image-2.6.32-40-generic                 deinstall

linux-image-2.6.32-41-generic                 deinstall

linux-image-2.6.32-42-generic                 install

linux-image-3.2.0-33-generic                   install

linux-image-3.2.0-34-generic                   install

linux-image-3.2.0-35-generic                   install

linux-image-3.2.0-37-generic                   install

linux-image-3.2.0-38-generic                   install

linux-image-3.2.0-40-generic                   install

linux-image-generic                            install

linux-libc-dev                                 install

linux-sound-base                          install

pptp-linux                               install

syslinux                                   install

syslinux-common                                install

syslinux-legacy                              install

util-linux                                  install

2.查看当前Ubuntu系统使用的内核版本

uname -a

结果:
Linux linux 3.2.0-40-generic #64-Ubuntu SMP Mon Mar 25 21:22:26 UTC 2013 i686 i686 i386 GNU/Linux

3.删除多余内核:
sudo apt-get purge

linux-image-2.6.32-21-generic linux-image-2.6.32-40-generic linux-image-2.6.32-41-generic linux-image-2.6.32-42-generic linux-image-3.2.0-33-generic linux-image-3.2.0-34-generic linux-image-3.2.0-35-generic linux-image-3.2.0-37-generic linux-image-3.2.0-38-generic

linux-headers-3.2.0-33 linux-headers-3.2.0-34 linux-headers-3.2.0-35 linux-headers-3.2.0-37 linux-headers-3.2.0-38

更新grub

sudo update-grub

再次查看一下内核列表,就发现旧版本已经不存在了!

study Linux kernel deeply

深入研究Linux内核

Linux系统的核心成为内核kernel,内核控制计算机上的所有硬件和软件,并在必要的时候分配硬件,在需要时执行软件。

       内核主要有以下4中功能:

l  系统内存管理

n  内存管理是操作系统内核的主要功能之一,内核不仅可以管理服务器上可用的物理内存,而且能够创建并管理虚拟内存,或者说非实际存在的内存。内存管理必须要使用硬盘空间,该空间成为交换空间swap space,内核不断地在交换空间和实际物理内存之间交换虚拟内存位置的内容,这样系统认为可用的内存比实际存在的内存多。使用ipcs可以查看系统当前的共享内存分页。

l  软件程序管理

n  Linux操作系统将正在运行的程序成为进程,进程可以在前台运行,也可以在后台运行,内核控制Linux系统如何管理在系统中运行的所有进程。内核创建的第一个进程,成为初始进程init process,该进程可在系统上启动所有其他进程,内核启动时,它将初始进程加载到虚拟内存中。内核每启动一个其他进程,都将在虚拟内存中为其分配一个唯一的空间,用于存储该进程使用的数据和代码。

l  硬件管理

n  Linux系统需要与之通信的设备都必须在内核代码中插入驱动程序代码、驱动程序代码使内核能够向设备传输数据,它的作用就像是应用程序与硬件之间的中间人、在Linux内核中插入设备驱动程序代码有两种方法:

u  在内核中编译驱动程序;

u  向内核添加驱动程序模块;

n  目前Linux系统将硬件设备标示为特殊文件,成为设备文件,大致分为3类:

u  字符设备

l  主要用于哪些一次仅处理一个字符的设备,比如调制解调器和终端类型

u  块设备

l  主要用于哪些一次可以处理大量数据块的设备,比如磁盘驱动器

u  网络设备

l  主要用于使用数据包发送和接收数据的设备,包括网卡和特殊的回路设备,允许Linux系统使用通用网络编程协议与自身通信。

l  文件系统管理

Linux内核可以使用不同类型的文件系统与硬盘传输数据,Linux内核使用虚拟文件系统(Virtual File SystemVFS)与每个文件爱你系统进行连接。这为内核与其他文件系统类型的通信提供了一个标准接口。