精华区 [关闭][返回]

当前位置:网易精华区>>讨论区精华>>电脑技术>>● Linux>>Linux之开发篇>>内核技术>>Linux内核模块编程指南(十一)

主题:Linux内核模块编程指南(十一)
发信人: arira(大天使)
整理人: qiaoqian(2001-12-31 00:01:56), 站内信件
主题:《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看结果的变化。 


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

 
 

[关闭][返回]