操作系统=内核+系统程序

系统程序=编译环境+API+AUI

编译环境=编译程序+连接程序+装载程序

API=系统调用+语言库函数

AUI=Shell+系统服务例程+应用程序

  应用软件是针对最终用户需求编写的,系统软件是为了简化应用程序的开发而存在的,例如编程语言的执行环境为应用程序开发了提供诸如IO操作、图形库等基础服务。

   

 

  POSIX表示可移植操作系统接口,是一个国际标准。

0. linux内核的体系结构

      linux内核除系统调用外,由5个主要的子系统组成:进程调度、内存管理、虚拟文件系统、网络接口、进程间通信。

 

 
1.linux内核的技术特点

  linux内核被设计为单内核(monolithic)结构,支持动态加载内核模块,为保证支持新设备而又不会无限的扩大内核规模,linux系统对驱动和新文件系统采用模块化方式,可动态加载和卸载。linux内核还采用了虚拟内存技术使得内存空间达到4GB.此外,linux文件系统还实现了一种抽象文件模型———虚拟文件系统(VFC),该文件系统属于UNIX风格。用户可以在统一界面上访问各种不同格式的文件系统。

2.liunx内核的版本

   2.6.30.1 第一个数字是主版本号,第二个是从版本号,第三个是修订版本号,第四个可选的数字为稳定版本号

 

3.linux内核源代码的结构

   内核源代码位于/usr/src/linux

   include目录包含了建立内核代码时所需的大部分包含文件

   init目录包含核心的初始化代码

   arch目录包括linux支持的所有硬件结构的内核代码

   drivers目录中是系统中所有的设备驱动程序。

   fs 包含了所有文件系统的代码

           net目录里是核心的网络部分代码

           mm目录包含了所有的内存管理代码

           ipc目录包含了进程间的通信代码

           Kernel包含了主内核代码

4.内核源码分析工具
1.linux超文本交叉代码检索工具LXR
2.windows平台下的源代码阅读工具 Source Insight
5.linux内核模块编程入门

         内核模块全称为动态可加载模块LMK,模块机制弥补了单内核可扩展性和可维护性较差的不足。模块是具有独立功能的程序,可被单独编译,不能独立运行。

        

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

static int __init lkp_init(void) {              //模块初始化函数 
printk ("My module worked!\n");   
  return 0;
 }                 
static void __exit lkp_cleanup(void) {           //模块卸载函数
       printk ("Unloading my module.\n");}
  module_init(lkp_init);
  module_exit(lkp_cleanup);
MODULE_ENSE("GPL");                            //模块许可声明

  (1)moduie.h头文件包含了对模块的结构定义以及模块的版本控制

      kernel。h包含了常用的内核函数

      init.h包含了宏_init 和_exit

  (2)lkp_init ()是模块的初始化函数   lkp_cleanup()是模块的退出和清理函数

(3)printk()是内核定义的,把打印的信息输出到终端。module_init和module_exit是最基本的两个函数。module_init()(ˇˍˇ) 向内核注册模块提供新功能。cleanup_exit注销由模块提供所有的功能

  (4)最后一句告诉内核该模块具有GUN公共许可证

 

     编译模块 Makefile 文件

     假如前面的程序起名 hellomod.c,对于2.6版本的内核模块,其中Makefile文件的基本内容如下

      # Makefile2.6

01obj-m += hellomod.o                             //obj-m : = 是赋值语句,使用目标文件hellomod.o生成模块hellomod.ok 
02#产生hellomod模块的目标文件
03CURRENT_PATH:=$(shell pwd)
04#模块所在的当前路径
05LINUX_KERNEL:=$(shell uname -r)
06#内核源代码的当前版本 
07LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)
08#内核源代码的绝对路径
09all:
10    make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
11#编译模块 
12clean:
13    make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean

14    #  清理模块

              我们首先获得当前的相对路径(可以在终端输入pwd试一下这个命令),然后再获得当前内核的版本号,这样就可以直接获得当前内核的绝对路径。

       完成上述两个文件后,在当前目录下运行make命令,就会生成hello.ko文件,即模块目标文件。 

       运行代码

       insmod命令可以使我们写的这个模块加入到内核中,但是一般我们要加上sudo。rmmod卸载这个模块。

 

 

 

5.linux内核中链表的实现(http://edsionte.com/techblog/archives/1074

        C语言中一个基本的双向链表定义如下

        struct   my_list{

              void  *mydata;

              struct my_list *next;

              struct my_list *prev;

              };

         选取双向链表作为基本数据结构并将其镶嵌到其他数据结构中,从而演化成其他复杂数据结构。

         1.linux内核对链表的抽象定义

        struct list-head {

               struct list_head *next , *prev

              }

        这个不含数据域的链表可以嵌入到任何结构,例如: struct my_list {

                                                                                                        void my_data;

                                                                                                        struct list_head list;

                                                                                                          }

        2.链表的声明与定义

        struct list_head只定义了链表结点,没有专门定义链表头。内核代码list.h定义了两个宏

        #define LIST_HEAD_INIT(name) { &(name), &(name) }                                                        初始化
        #define LIST_HEAD(name)  struct list_head name = LIST_HEAD_INIT(name)                    声明并初始化
        如果要声明并初始化自己的链表头,直接调用 LIST_HEAD

         LIST_HEAD( mylist_head)

        调用之后,next和prev指针都初始化指向自己。

      3.链表中增加一个结点

          static inline void list_add(struct list_head *new, struct list_head *head);

                                        {

         next->prev = new;
        new->next = next;
        new->prev = prev;

        prev->next = new;

            }


          static inline void list_add_tail(struct list_head *new, struct list_head *head);

 

         调用这个内部函数以分别子链表头和尾增加结点

          

             static inlinevoid list_add(structlist_head *new,struct list_head *head)
                    {
                        __list_add(new, head, head->next);              }

         该函数向指定链表的head结点后插入new结点。可将任何结点传递给head,但是如果传递最后一个元素给head,该函数可以用来实现一个栈

 

               static inline void list_add_tail(struct list_head *new, struct list_head *head);

                        {

                                 _list_add(new,  head - >prev,head);                      }

           该函数向指定链表的head结点前插入new结点。可将任何结点传递给head,但是如果将第一个元素给head,该函数可以用来实现一个队列。

        4.遍历链表

        list.h中定义了如下遍历链表。

           #define list_for_each(pos, head) \

                     for (pos = (head)->next; pos != (head); \

                         pos = pos->next )

          这种遍历仅仅找到一个个结点在链表中的偏移位置,问题是如何通过pos获得结点的起始地址,从而引用该结点的域。list.h中定义了

             #define list_entry(ptr, type, member)\

              ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) 
            指针ptr指向结构体type中的成员member;通过指针ptr,返回结构体type的起始地址,也就是list—entry返回指向type类型的指针。

              ((unsigned long)(&((type *)0)->member把0地址转化为type结构的指针,然后获取该结构中的成员member域的指针,也就是获得了member在type结构中的偏移量。   其中(char*)(ptr)求出的是ptr的绝对地址,二者相减,于是得到了type类型结构体的起始地址 。

6.链表的应用

                           

  1. #include<linux/kernel.h>
  2. #include<linux/module.h>
  3. #include<linux/slab.h>
  4. #include<linux/list.h>
  5. MODULE_LICENSE("GPL");
  6. MODULE_AUTHOR("XIYOU");
  7. #define N 10                                               //l链表结点数
  8. struct numlist{
  9.    
  10.         int num;                                                      //数据
  11.         struct list_head list;                                      //指向双链表前后结点的指针
  12. };
  13. struct numlist numhead;                                   //头结点
  14. static int __init doublelist_init(void)
  15. {     //初始化头结点
  16.     struct numlist *listnode;                                //每次申请链表结点时所有的指针
  17.     struct list_head *pos;
  18.     struct numlist *p;
  19.     int i;
  20.     printk("doublelist is starting..\n");
  21.     INIT_LIST_HEAD(&numhead.list);
  22. //建立N个结点,依次加入到链表当中
  23.     for(i=0;i<N;i++)
  24.     {
  25.         listnode=(struct numlist*)kmalloc(sizeof(struct numlist),GFP_KERNEL);   //kmallox在内核空间中申请内存,类似于malloc()
  26.         listnode->num=i+1;
  27.         list_add_tail(&listnode->list,&numhead.list);
  28.         printk("node %d has added to the doublelist\n",i+1);
  29.     }
  30.     //遍历链表
  31.     i=1;
  32.     list_for_each(pos,&numhead.list){
  33.         p=list_entry(pos,struct numlist,list);
  34.         printk("node %d's data :%d\n",i,p->num);
  35.         i++;
  36.     }
  37.     return 0;
  38. }
  39. static void __exit doublelist_exit(void)
  40. {
  41.     struct list_head *pos,*n;
  42.     struct numlist *p;
  43.     //依次删除N个节点
  44.     int i;
  45.     list_for_each_safe(pos,n,&numhead.list)                                    //为了安全删除结点而进行的遍历
  46.     {
  47.         list_del(pos);                                                                               //从双链表中删除当前结点
  48.         p=list_entry(pos,struct numlist,list);                                             //得到当前数据结点的首地址,即指针
  49.         kfree(p);                                                                                                //释放数据结点所占空间
  50.         printk("node %d has removed from the doublelist\n",i++);
  51.     }
  52.     printk("doublelist is exiting\n");

  53. }
  54. module_init(doublelist_init);
    module_exit(doublelist_exit);

 

           Makefile

           

  1. obj-m:=list.o
  2. CURRENT_PATH:=$(shell pwd)
  3. LINUX_KERNEL:=$(shell uname-r)
  4. LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)
  5. all:
  6.     make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
  7. clean:
  8.     make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean

 

详细阅读 linclude/linux/list.h中的代码

尽管其是内核代码的头文件,但稍加修改可移植到用户空间使用。

 

 

           

 

 

 

   

 

 

 

 

Logo

更多推荐