精华区 [关闭][返回]

当前位置:网易精华区>>讨论区精华>>编程开发>>C/C++>>unix,linux,free bsd>>Linux内核编程指南>>《Linux内核模块编程指南》(十一)

主题:《Linux内核模块编程指南》(十一)
发信人: kevintz()
整理人: kevintz(2000-06-24 00:44:14), 站内信件
《Linux内核模块编程指南》
《Linux Kernel Module Programming Guide》
作者:Ori Pomerantz 中译者:谭志([email protected])
  
译者注:
1、LKMPG是一本免费的书,英文版的发行和修改遵从GPL version 2的许可。为了
节省时间,我只翻译了其中的大部分的大意,或者说这只是我学习中的一些中文
笔记吧,不能算是严格上的翻译,但我认为这已经足够了。本文也允许免费发布
,但发布前请和我联系,但不要把本文用于商业目的。鉴于本人的水平,文章中
难免有错误,请大家不吝指正。
2、本文中的例子在Linux(kernel version 2.2.10)上调试通过。你用的Linux必
须支持内核模块的加载,如果不支持,请在编译内核时选上内核模块的支持或升
级你的内核到一个支持内核模块的版本。     


     

                          第十一章  任务调度


    通常,我们会有一些程序是在某一个特定时间运行的。如果任务是用进程来
完成,我们可以把它放在crontab文件里,如果任务是由内核模块完成的,我们有
两种可行的方法。一是将一个进程放到crontab文件中,由这个进程在需要的时候
用系统调用唤醒模块,例如打开一个文件。这是十分低效的,我们要生成一个脱
离crontab的新进程,读入新的可执行文件到内存里,然后唤醒在内存中的内核模
块。
        
    另一个方法是我们建立一个函数,当时钟每中断一次,它都被调用一次。这
可以通过建立一个任务(task),放进tq_struct结构体(tq_struct结构体含有这个
函数的指针)里,然后用queue_task把任务放到tq_timer的任务队列来实现。tq_
timer任务队列中的任务将会在下一次时钟中断时被执行,由于我们想一直执行该
函数,所以当该任务被调度执行时,我们要把它放回到tq_timer队列,以便下一
次中断被执行。

    我们还有一点要记住。当一个模块用rmmod卸载时,先是它的参考计数器(re
ference count)被检查。如果为零,module_cleanup被调用,然后模块从内存中
移去,函数不再存在内存里。时钟任务队列tq_timer中的任务是不会检查函数指
针指向的函数是否还在内存里。很长一段时间后(这是从计算机的角度看。如果从
人的角度看,少于百分之一秒),内核产生时钟中断并尝试调用任务队列里的函数
。但函数已经不再存在,大多数情况下,函数原来使用的内存页没被使用,你会
得到一个错误。但一个新函数占据了相同的位置,事情将变得不可预测。不幸的
是,我们没有一个简单的方法移去任务列表里的任务。

    由于cleanup_module不能返回错误码(它是声明为void型的函数),解决方法
不是让它就这样返回,而是调用sleep_on或module_sleep_on来使rmmod进程进入
睡眠状态。在这之前,它用一个全局变量的方法通知中断队列里的函数不再需要
与自身关联。下一次时钟中断时,rmmod被唤醒,我们的函数不再在队列里(因为
已经被调用一次,并且不再放到tq_timer中了),就可以安全地移去内核模块了。



例子sched.c    

 
/* sched.c - scheduale a function to be called on 
 * every timer interrupt. */

/* Copyright (C) 1998 by Ori Pomerantz */

/* The necessary header files */

/* Standard in kernel modules */
#include <linux/kernel.h>   /* We're doing kernel work */
#include <linux/module.h>   /* Specifically, a module */

/* Deal with CONFIG_MODVERSIONS */
#if CONFIG_MODVERSIONS==1
#define MODVERSIONS
#include <linux/modversions.h>
#endif        

/* Necessary because we use the proc fs */
#include <linux/proc_fs.h>

/* We scheduale tasks here */
#include <linux/tqueue.h>

/* We also need the ability to put ourselves to sleep 
 * and wake up later */
#include <linux/sched.h>

/* 记录中断函数已被调用的次数*/
static int TimerIntrpt = 0;

/* This is used by cleanup, to prevent the module from 
 * being unloaded while intrpt_routine is still in 
 * the task queue */
/*由本模块形成的等待队列,本例是把rmmod放到WaitQ中*/
static struct wait_queue *WaitQ = NULL;

static void intrpt_routine(void *);

/* The task queue structure for this task, from tqueue.h */
/* 本任务的任务队列结构体*/
static struct tq_struct Task = {
  NULL,   /* Next item in list - queue_task will do 
           * this for us */
          /*队列中的下一项,由queue_task自动填充*/

  0,      /* A flag meaning we haven't been inserted 
           * into a task queue yet */
          /*表明还没放到任务列表的标志*/
  intrpt_routine, /* The function to run */
            /*中断要执行的函数*/
  NULL    /* The void* parameter for that function */
          /*函数的参数*/
};


/* This function will be called on every timer 
 * interrupt. Notice the void* pointer - task functions 
 * can be used for more than one purpose, each time 
 * getting a different parameter. */
static void intrpt_routine(void *irrelevant)
{
  /* Increment the counter */
  TimerIntrpt++;

  /* If cleanup wants us to die */
  if (WaitQ != NULL) 
  /*有rmmod调用进程在WaitQ队列里,说明下次不再被中断*/
    wake_up(&WaitQ);   /* Now cleanup_module can return */
  else
    /* Put ourselves back in the task queue */
    queue_task(&Task, &tq_timer);  
}

/* Put data into the proc fs file. */
int procfile_read(char *buffer, 
                  char **buffer_location, off_t offset, 
                  int buffer_length, int zero)
{
  int len;  /* The number of bytes actually used */

  /* This is static so it will still be in memory 
   * when we leave this function */
  static char my_buffer[80];  

  static int count = 1;

  /* We give all of our information in one go, so if 
   * the anybody asks us if we have more information 
   * the answer should always be no. 
   */
  if (offset > 0)
    return 0;

  /* Fill the buffer and get its length */
  len = sprintf(my_buffer, 
                "Timer was called %d times so far\n", 
                TimerIntrpt);
  count++;

  /* Tell the function which called us where the 
   * buffer is */
  *buffer_location = my_buffer;

  /* Return the length */
  return len;
}


struct proc_dir_entry Our_Proc_File = 
  {
    0, /* Inode number - ignore, it will be filled by 
        * proc_register_dynamic */
    5, /* Length of the file name */
    "sched", /* The file name */
    S_IFREG | S_IRUGO, 
    /* File mode - this is a regular file which can
     * be read by its owner, its group, and everybody
     * else */
    1,  /* Number of links (directories where 
         * the file is referenced) */
    0, 0,  /* The uid and gid for the file - we give 
            * it to root */
    80, /* The size of the file reported by ls. */
    NULL, /* functions which can be done on the 
           * inode (linking, removing, etc.) - we don't 
           * support any. */
    procfile_read, 
    /* The read function for this file, the function called
     * when somebody tries to read something from it. */
    NULL 
    /* We could have here a function to fill the 
     * file's inode, to enable us to play with 
     * permissions, ownership, etc. */
  }; 


/* Initialize the module - register the proc file */
int init_module()
{
  /* Put the task in the tq_timer task queue, so it 
   * will be executed at next timer interrupt */
  /* 把任务放到时钟中断队列里*/
  queue_task(&Task, &tq_timer);
  /* 注册proc文件*/
  return proc_register(&proc_root, &Our_Proc_File);

}


/* Cleanup */
void cleanup_module()
{
  /* Unregister our /proc file */
  proc_unregister(&proc_root, Our_Proc_File.low_ino);
  
  /* Sleep until intrpt_routine is called one last 
   * time. This is necessary, because otherwise we'll 
   * deallocate the memory holding intrpt_routine and
   * Task while tq_timer still references them. 
   * Notice that here we don't allow signals to 
   * interrupt us. 
   *
   * Since WaitQ is now not NULL, this automatically 
   * tells the interrupt routine it's time to die. */
 /* 将调用本函数的当前进程放到等待队列里睡眠,*
  * 这里是rmmod进程*/
 sleep_on(&WaitQ);
}  


编译和测试:
编译后,insmod,然后不断cat /proc/sched看结果的变化。


--
那一刹那,我开始用心去看这个世界,所有的事物真的可以看得前
所未有的那么清楚…… 

※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 202.105.73.203]

[关闭][返回]