2013年8月31日 星期六

Linux Linked Lists

Linux本身已經實作了doubly linked lists的資料結構,我們可透過linked lists實現一些功能,底下是一個範例:

定義list節點的資料結構
struct _job_list {
 struct list_head all_jobs; /* 工作清單 */
 void (*worker_handle)(char *); /* 執行工作的function */
 void *worker_data; /* 執行工作所需資料 */
 wait_queue_head_t todo; /* 用於同步boss與worker */
 spinlock_t lock; /* 用於保護工作清單的鎖 */
};
boss在工作清單中加入jobs
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <job.h>

extern struct _job_list job_list;

static void run_umode_mkdir_app(char *data)
{
 int result;
 char path[] = "/bin/mkdir";
 char *argv[] = { path, data, NULL };
 char *envp[] = { "HOME=/", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
 
 /* 執行一個user space的程式 */
 result = call_usermodehelper(path, argv, envp, UMH_WAIT_PROC);
 
 if (result == 0) {
  printk("worker kthread: successfully executed %s application\n", path);
 } else {
  printk("worker kthread: failed to execute %s application\n", path);
 }
}

static void run_umode_touch_app(char *data)
{
 int result;
 char path[] = "/bin/touch";
 char *argv[] = { path, data, NULL };
 char *envp[] = { "HOME=/", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
 
 /* 執行一個user space的程式 */
 result = call_usermodehelper(path, argv, envp, UMH_WAIT_PROC);
 
 if (result == 0) {
  printk("worker kthread: successfully executed %s application\n", path);
 } else {
  printk("worker kthread: failed to execute %s application\n", path);
 }
}

static void run_umode_bash_app(char *data)
{
 int result;
 char path[] = "/bin/bash";
 char *argv[] = { path, "-c", strcat("echo Boss said: you did good jobs! > ", data),
                  NULL };
 char *envp[] = { "HOME=/", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
 
 /* 執行一個user space的程式 */
 result = call_usermodehelper(path, argv, envp, UMH_WAIT_PROC);
 
 if (result == 0) {
  printk("worker kthread: successfully executed %s application\n", path);
 } else {
  printk("worker kthread: failed to execute %s application\n", path);
 }
}

static int __init boss_module_init(void)
{
 struct _job_list *job1;
 struct _job_list *job2;
 struct _job_list *job3;
 char *data_folder = "/jobs";
 char *data_file = "/jobs/renee";
 
 /* 為job配置一個大小為sizeof(work structure)核心記憶體空間 */
 job1 = kmalloc(sizeof(struct _job_list), GFP_ATOMIC);
 if (!job1) return -1;
 /* 填寫work structure內容 */
 job1->worker_handle = run_umode_mkdir_app; /* 執行工作的fuction */
 job1->worker_data = data_folder; /* function的參數 */
 
 job2 = kmalloc(sizeof(struct _job_list), GFP_ATOMIC);
 if (!job2) return -1;
 job2->worker_handle = run_umode_touch_app;
 job2->worker_data = data_file;
 
 job3 = kmalloc(sizeof(struct _job_list), GFP_ATOMIC);
 if (!job3) return -1;
 job3->worker_handle = run_umode_bash_app;
 job3->worker_data = data_file;
 
 spin_lock(&job_list.lock); /* 進入critical section存取前要上鎖 */
 /* 將工作加入工作清單 */
 list_add_tail(&job1->all_jobs, &job_list.all_jobs);
 printk("boss: assigned 1st job to the worker\n");
 list_add_tail(&job2->all_jobs, &job_list.all_jobs);
 printk("boss: assigned 2nd job to the worker\n");
 list_add_tail(&job3->all_jobs, &job_list.all_jobs);
 printk("boss: assigned 3rd job to the worker\n");
 wake_up(&job_list.todo); /* 喚醒worker thread */
 spin_unlock(&job_list.lock); /* 離開critical section,進行解鎖 */
 
 printk("Boss module loaded\n");
 return 0; 
}

static void __exit boss_module_exit(void)
{
 printk("Boss module unloaded\n");
}

module_init(boss_module_init);
module_exit(boss_module_exit);

MODULE_DESCRIPTION("boss Module");
MODULE_LICENSE("GPL");
MODULE_ALIAS("boss module");
MODULE_AUTHOR("Renee's Blog");
worker執行工作清單中所有的jobs
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/list.h>
#include <job.h>

struct _job_list job_list;
EXPORT_SYMBOL(job_list);
int worker_pid;

static int worker(void* unused)
{
 DECLARE_WAITQUEUE(wait, current);
 daemonize("worker");
 void (*worker_handle)(char *);
 void *worker_data;
 struct _job_list *job;
 set_current_state(TASK_INTERRUPTIBLE);
 allow_signal(SIGKILL);
 
 while (1) {
  add_wait_queue(&job_list.todo, &wait);
  if (list_empty(&job_list.all_jobs)) {
   schedule();
   if (signal_pending(current)) {
    break;
   }
  } else {
   set_current_state(TASK_RUNNING);
  }
  remove_wait_queue(&job_list.todo, &wait);
  /* 由於list是共享的資料結構,在進入critical section存取前要上鎖 */
  spin_lock(&job_list.lock);
  /* 如果list不為空,則取出節點,並執行節點裡面function */
  while (!list_empty(&job_list.all_jobs)) {
   /* 先存取list中第一個節點 */
   job = list_entry(job_list.all_jobs.next, struct _job_list, all_jobs);
   worker_handle = job->worker_handle;
   worker_data = job->worker_data;
   list_del(&job->all_jobs); /* 此節點已經被處理過,將它從list刪除 */
   kfree(job); /* 釋放節點 */
   spin_unlock(&job_list.lock); /* 離開critical section,進行解鎖 */
   worker_handle(worker_data); /* 執行從節點取出的fuction */
   printk("worker: finished a job\n");
   spin_lock(&job_list.lock); /* 如果list不為空,會再進入critical section,
                                 所以需要再次上鎖 */
  }
  spin_unlock(&job_list.lock); /* 離開critical section,進行解鎖 */
  set_current_state(TASK_INTERRUPTIBLE);
 }
 remove_wait_queue(&job_list.todo, &wait);
 return 0; 
}

static int __init worker_module_init(void)
{
 /* 初始化lock,用於保護list共享資料結構 */
 spin_lock_init(&job_list.lock);
 /* 初始化wait queue,用於同步boss與worker */
 init_waitqueue_head(&job_list.todo);
 /* 初始化list的head節點 */
 INIT_LIST_HEAD(&job_list.all_jobs);
 
 worker_pid = kernel_thread(worker, NULL, CLONE_FS | 
  CLONE_FILES | CLONE_SIGHAND | SIGCHLD);
 if(worker_pid > 0)
 {
  printk("Created the kthread of the worker PID = %d\n", worker_pid);
 }
 else
 {
  printk("Failed to create the kthread of the worker\n");
 }
 
 printk("Worker module loaded\n");
 return 0; 
}

static void __exit worker_module_exit(void)
{
 if(worker_pid)
 {
  kill_pid(find_vpid(worker_pid), SIGKILL, 1);
 }
 printk("Worker module unloaded\n");
}

module_init(worker_module_init);
module_exit(worker_module_exit);

MODULE_DESCRIPTION("Worker Module");
MODULE_LICENSE("GPL");
MODULE_ALIAS("worker module");
MODULE_AUTHOR("Renee's Blog");
操作list的函式列表:
函式說明
INIT_LIST_HEAD()初始化list的head節點
list_add()在head後方加入一個節點
list_add_tail()在head前方加入一個節點
list_del()刪除一個節點
list_replace()一個節點取代另一個
list_entry()list中取得一個節點
list_for_each_entry()走訪list所有節點
list_for_each_entry_safe()走訪list所有節點,並確保不會刪除任何節點
list_empty()確認list是否為空
list_splice()一個list加入到另一個

沒有留言:

張貼留言