2014年1月9日 星期四

Write your own system call

Linux Version 2.6.35.4
(CentOS)
目錄前的Linux代表kernel source code的根目錄
我的Linux= /usr/src/kernels/linux-2.6.35.4

A.      加一個自己的system call
1.      新增一筆syscall service routine name syscall table
Linux/arch/x86/kernel/syscall_table_32.S 中加 “.long sys_process_switch_info”

syscall call發生時kernel會到此table中找到對應的service routine name


2.      定義syscall number
Linux/arch/x86/include/asm/unistd_32.h
把新的number加上去,並將__NR_syscalls +1
(__NR_syscalls代表maximumsyscalls number)


3.      定義syscallprototype
Linux/include/syscalls.h 這裡定義所有的syscall prototype

(asmlinkage是一種語法,用來與不同compiler間溝通)


 4.      Syscall 真正的code
syscall中我自己定義的一個structure (在這裡不重要)
真正的syscallSYSCALL_DEFINE[0-6](….)開始
SYSCALL_DEFINE可以接受06個參數, 第一個參數是SYSCALL NAME不算在內。定義形式是這樣的:
SYSCALL_DEFINE[參數個數](SYSCALL NAME, 參數1型態, 參數1, 參數2型態,參數2….)
因為我的syscall只有一個型態為 process_switch_info*的參數 psi, 因此我是
SYSCALL_DEFINE1(……)
(若沒有參數的話在第3步裡面參數地方要寫void不然會有錯)


5.      加入Makefile(object檔放入 source code tree)
Linux/kernel/Makefile

(新增的syscall放在哪一層,就加在該層的Makefile)




6.   重編kernel
make
make modules_install
make install

B.      測試system call
1.      syscall(__NR_process_switch_info, psi)是呼叫syscall的方法
第一個參數為syscall name, 後面的參數放該syscall所需的參數
可以直接寫在.c中,這裡定義了get_process_switch_info().c中的function name有意義,因此傳了一個自訂的structure進來才呼叫syscall



2014年1月5日 星期日

Ftrace 追蹤kernel process

為了確認process到底是怎麼context switch以及context switch幾次,花了些時間研究了Ftrace。有些地方仍不是很了解,要再查資料研究
Linux version : 2.6.35.4 (CentOS)

1.     Ftrace 是內建的tracker,在編譯核心時可以到選單裡的<kernel hacking>選擇 (因為不太熟悉各選項的用途,因次我全選了= _=)

2.   選完後開始編譯
make à make modules_install à make install àreboot

3.     /sys/kernel/debug , debugfs mount上去
# mount –t debugfs nodev /sys/kernel/debug 

4.     現在可以到 /sys/kernel/debug/tracing, 會看到一大堆檔案
可以查看 available_tracers 看看有那些tracer可以用


5.     使用 tracer的步驟
(a)  關閉 tracing_enabled (0closed, 1 opened)
# echo 0 > tracing_enabled
(b) 打開 ftraced_enabled (不太清楚原因
# echo 1 > /proc/sys/kernel/ftrace_enabled
(c)  選擇目前要用的tracer
# echo … > current_tracer
(d) 設定要跟蹤的函數到 set_ftrace_filter
# echo … > set_ftrace_filter
(e)  打開 tracing enabled
# echo 1 > tracing_enabled
(f)   執行想要追蹤的程式或做其他事, ftrace會開始追蹤kernel
(g)  結束追蹤
# echo 0 > tracing_enabled
或暫停追蹤(tracer只是暫停沒有結束
# echo 0 > tracing_on
(h) 看結果囉!
# cat trace
可以搭配 grep指令列出想看的東西
# cat trace | grep test (test是我剛剛執行的執行檔)
或是 >> trace輸出成自己的檔案
# cat trace >> /trace1 (輸出到跟目錄下 檔名為 trace1)

6.     我這一次觀察了2 tracer
(a)  Function
會列出function執行的順序
下圖是我執行 test 之後的tracer, 我有用 filter鎖定我要看的函數, 也就是主要做context_switchschedule
欄位意思大概是這樣
TASK-PID
Cpu#
TIMESTAMP
FUNCTION
<test>-5049
000
13211.469334
Scheduleßwork_resched
Process test pid 5049正在執行, 使用編號 0CPU, 時間是timestamp, 正由work_resched呼叫schedule function
看樣子應該是準備進行contest switch


我的test就是會讓他睡8次的一支程式(作業需要XD)
所以可以看到8nanosleep呼叫schedule準備把控制權交出來然後去睡覺

(a)  Sched_switch
列出 context switchinfo
下圖是使用sched_switchprocess context switchc的情況
我用 grep test 過濾出我想看的, 欄位意思大概是

TASK-PID
Cpu#
TIMESTAMP
FUNCTION
<test>-5182
000
14456.847013
(*)
(*): 5182:120:S == > [000] 0:120:R <idle>
   pid 5182, priority 120,目前狀態S代表 TASK_INTERRUPTIBLE可中斷睡眠狀態
== >代表轉移控制權( + 可能是代表正在叫醒某Process)



7.     Reference

2013年10月20日 星期日

week4- 形容詞&副詞子句應用

module 1:
I live in Taiwan, which has plenty of delicious food and beautiful landscapes. Although Taiwan is a tiny island surrounded by ocean, it  is still abundant in natural resources. Due to the limitation of ground where people can live, the cities in Taiwan usually have high population. Therefore, the culture of night market grows rapidly in Taiwan such that people can enjoy  a variety of small eats even though it's midnight. "Taipei travel essentials: passport, cash, extra stomach",  which CNN had published before, recommends 40 Taiwanese foods we can't live without. For instance, Bubble tea, the most fabulous drink with tapioca balls, is one of the most representative small food in Taiwan.  Except for small food, Taiwanese also wins reputation of being hospitable and enthusiastic. You can experience our enthusiasm wherever you go, so come and visit our beautiful island, Taiwan.

module 2 :
In my opinion, natural disasters are a huge disadvantage of Taiwan. One of natural disasters is typhoon, consisting of a tropical cyclone. In summer, typhoons, bringing an enormous rainfall and a strong breeze, will come and cause a serious damage to our crops. On account of the damage, our agricultural economic losses hundreds of million dollars each year. Besides, concerning about the shortage of fruits and vegetables, people will buy lots of food before the coming of typhoon, leading to a rise of food prices. Furthermore, villages beside the mountains will risk losing their houses due to mudflows and landslides. To ensure security of residents, government will withdraw all of them by a strong hand to the safer place. Another natural disaster is earthquake,known for its unpredictable occurrence and intense destructive power. People cannot run out of the house immediately while earthquake occurs.When it comes to an huge earthquake, the ground will rumble with house shaking violently. Few seconds later, the houses will collapse rapidly that almost no one can survive. Natural disasters are unavoidable, the most important thing to do is to reduce the damages as best as we can.

2013年10月13日 星期日

Week3

module1:
This restaurant is famous for its seafood and well-serviced quality.Customers have to make a reservation before dining.There are many kinds of seafood on the menu,such as fried-shrimps,roasted-lobster, and steamed-fish.Everyone enjoys their meals there. Due to the fresh seafood and its low price, this restaurant gains high popularity in this small town.

module2: (underline action verbs)
This restaurant is famous for its seafood and well-serviced quality.Customers have to make a reservation before dining.There are many kinds of seafood on the menu,such as fried-shrimps,roasted-lobster, and steamed-fish.The main chef strongly recommends the  roasted-lobster with butter  for main course. The lobster with fragrant butter tastes delicious that everyone enjoys it there. Due to the fresh seafood and its low price, this restaurant gains high popularity in this small town.

Assignment (improvement) :
This restaurant is famous for its seafood and well-serviced quality. There always has a long line of people outside the door, waiting for tasting seafood expectantly. Customers have better to make a reservation before dining. There are many kinds of seafood on the menu, such as fried-shrimps, roasted-lobster, and steamed-fish. The main chef strongly recommends the roasted-lobster with butter for main course due to its freshness. The lobster with fragrant butter tastes delicious that everyone enjoys it there. Due to the fresh seafood and its low price, this restaurant gains higher and higher popularity in this small town. Therefore, the local tourism also gets stronger and brings economic recovery of the town.

2013年9月1日 星期日

Linux Device Driver - IOCTL (copy_to_user & copy_from_user)

在 linux/include/linux/fs.h 中定義許多file operation能用的handler
這一次我針對的是 read、ioctl做練習
file operation中沒實做到的handler會被定義成NULL
這一支程式會先得到來自kernel的回應,接著kernel 換更改指定的數字

先大概說一下主要概念
我要先寫一個mydev.c 當作 driver
執行完make 之後 就把 mydev.ko 加入到 kernel中
此時的 mydev.ko 就是模組, 一個在kernel中的模組了
那要如何與此模組互動呢?
我要再寫一個 test_mydev.c 當作 user
直接把他 gcc 產生執行檔 再指定我要跟mydev 模組溝通

首先先來看 ioc.h
這裡定義了 user 能夠透過 ioctl 傳達的指令
IOC_MAGIC 是device 的 major number , 而我定義其major number為 k
每一個裝置都有其major number 與 minor number
接著我設置 SETNUM  GETNUM  XNUM 3種指令
_IOW  _IOR  _IOWR 是 kernel 的巨集 , 我們用這3個巨集來定義我們的命令要做哪些事
各聚集裡的1,2,3 是自訂的minor number

#ifndef IOC_H
#define IOC_H

#define IOC_MAGIC 'k'
#define SETNUM _IOW(IOC_MAGIC, 1 , int)
#define GETNUM _IOR(IOC_MAGIC, 2 , int)
#define XNUM _IOWR(IOC_MAGIC,3 , int)
#define IOC_MAXNR 3

#endif


接著看到最主要的 driver code : mydev.c
在file operations 中定義了我自訂的操作函數
dev_open : 在user open  device file 時會被呼叫
dev_close : 在user close device file 時會被呼叫
dev_ioctl  :  switch case 中寫了各種command該執行的動作
         若user 呼叫 ioctl 時傳入 SETNUM 則 driver 會將傳入的資料(args)取出,並指定給val 變數
         若user 呼叫 ioctl 時傳入 GETNUM 則 driver 會將val 變數傳出(args)
         若user 呼叫 ioctl 時傳入XNUM 則 driver 會將 user 輸入的數值指派給val變數達到更改資料
         的目的
         (因為user space 與 kernel space 儲存data的空間不同, 因此要用 copy_to_user 與
         copy_from_user來轉遞資料)
dev_read : user 呼叫 read時會被呼叫, 我在這裡放了一個hello字串, kernel會利用copy_to_user 傳到
                 buffer中給user
dev_write: 我在這裡沒用到
init_modules:  程式進入點 , 建立裝置並註冊到kernel
exit_modules: 程式結束 , 移除裝置

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

#include <linux/fs.h> //chrdev
#include <linux/cdev.h> //cdev_add() / cdev_del()

#include <asm/uaccess.h> //copy_*_user()

#include "ioc.h"

MODULE_LICENSE("GPL");
MODULE_AUTHOR("IRENE 2013/09/01");
#define DEV_BUFSIZE 1024

static int dev_major;
static int dev_minor;
struct cdev *dev_cdevp = NULL;
static int dev_open(struct inode*, struct file*);
static int dev_release(struct inode*, struct file*);
static int dev_ioctl(struct inode*, struct file*, unsigned int, unsigned long);
ssize_t dev_read(struct file*, char __user*, size_t, loff_t*);
ssize_t dev_write(struct file*, char __user*, size_t, loff_t*);
static void __exit exit_modules(void);

//Functions on the right side are the handler we just defined
struct file_operations dev_fops = {
   .owner = THIS_MODULE,
   .open = dev_open,
   .release= dev_release,
   .ioctl = dev_ioctl,
   .read = dev_read,
   .write = dev_write
};


// dev_open will be Called when user open the device file
static int dev_open(struct inode *inode, struct file *filp)
{
       printk("%s():\n",__FUNCTION__);
       return 0;
}

// dev_release will be Called when user close the device file
static int dev_release(struct inode *inode, struct file *filp)
{
       printk("%s():\n",__FUNCTION__);
       return 0;
}

// mainly io control between user and kernel
// args are pointed to user space's buffer
static int val = 0;
static int dev_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long args)
{
      int tmp, err=0,ret=0;

      if(_IOC_TYPE(cmd) != IOC_MAGIC)
              return -ENOTTY;
      if(_IOC_NR(cmd) > IOC_MAXNR)
              return -ENOTTY;
      if(_IOC_DIR(cmd) & _IOC_READ){
              err = !access_ok(VERIFY_WRITE, (void __user*)args, _IOC_SIZE(cmd));
      } else if (_IOC_DIR(cmd) & (_IOC_WRITE)){
              err = !access_ok(VERIFY_READ, (void __user *)args,_IOC_SIZE(cmd));
      }

      if (err)
              return -EFAULT;

      switch (cmd) {
        case SETNUM:
             //copy data from args(user) to val(kernel)
             if(copy_from_user(&val,(int __user *)args,1))
                 return -1;

             printk("%s():get val from user = %d\n",__FUNCTION__,val);
                   break;
        case GETNUM:
                        //copy data from val to args
              if(copy_to_user((int __user *)args,&val,1))
                    return -1;

              printk("%s():set val to %d\n",__FUNCTION__, val);
                    break;
        case XNUM:
                       // exchange data passed by user
              tmp = val;
              if(copy_from_user(&val,(int __user *)args,1))
                        return -1;

              if(copy_to_user((int __user *)args,&val,1))
                        return -1;

           printk("%s():change val from %d to %d\n",__FUNCTION__,tmp,val);
           break;

        default: /* redundant. as cmd was checked against MAXNR */
                 return -ENOTTY;
     }
     return 0;
}


static const char str[]="Hello Irene from kernel!";
static const ssize_t str_size = sizeof(str);
// send messages to user
ssize_t dev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{

     printk("%s():\n",__FUNCTION__);

     if( *f_pos >= str_size)
          return 0;
     if( *f_pos + count > str_size)
        count = str_size - *f_pos;
     if (copy_to_user(buf, str + *f_pos, count))
        return -EFAULT;
     *f_pos += count;
     return count;

}

ssize_t dev_write(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
     printk("%s():\n",__FUNCTION__);
     return 0;
}

static int __init init_modules(void)

   dev_t dev;
   int ret;
   // get major number dynamically
   ret = alloc_chrdev_region(&dev, 0, 1, "mydev");
   if (ret <0){
      printk("can't alloc chrdev\n");
      return ret;
   }
   dev_major = MAJOR(dev);
   dev_minor = MINOR(dev);
   printk("register chrdev(%d,%d)\n",dev_major,dev_minor);

   dev_cdevp = kmalloc(sizeof(struct cdev), GFP_KERNEL);
   if (dev_cdevp == NULL){
        printk("kmalloc failed\n");
        goto failed;
   }
   // system call handler
   cdev_init(dev_cdevp, &dev_fops);
   dev_cdevp->owner = THIS_MODULE;
   //register my device to kernel
   ret = cdev_add(dev_cdevp, MKDEV(dev_major, dev_minor),1);
   if(ret < 0){
        printk("add chr dev failed\n");
        goto failed;
   }
   return 0;

failed:
   if(dev_cdevp) {
   kfree(dev_cdevp);
   dev_cdevp = NULL;
}
  return 0;
}


static void __exit exit_modules(void)
{
   dev_t dev;
   dev = MKDEV(dev_major, dev_minor);
   if (dev_cdevp){
      cdev_del(dev_cdevp);
      kfree(dev_cdevp);
    }
   unregister_chrdev_region(dev,1);
   printk("unregister chrdev\n");
}

module_init(init_modules);
module_exit(exit_modules);


接著 是 user 端部分啦 test_mydev.c
執行時要輸入的參數有 devicefile num
device file 就是 在 /dev下的裝置檔名
num 是 XNUM命令時要change的value (default 是 10 )
首先利用 open 打開 device file
然後 read(fd, buffer, sizeof(buffer));
buffer 是 user 拿來放kernel 回傳值的地方
此時kernel 會回傳 Hello Irene from kernel!
接著 user 開始下達 ioctl command
--> ioctl(fd, SETNUM, &ret)
這是其中一個例子 , SETNUM 便是user 下達的command
而&ret 是 回傳值的指標

#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "ioc.h"

int main(int argc, char *argv[])
{
       int fd, ret=10;
       unsigned char buffer[50]={};
       if(argc < 2)
       {
         printf("Usage: /dev/mydev param1.. \n");
         return -1;
       }
       // open dev file
       fd=open(argv[1], O_RDWR);
       if(fd < 0)
       {
           printf("open %s failed\n", argv[1]);
           return -1;
       }
       //read from kernel 
       read(fd, buffer, sizeof(buffer));
       printf("%s\n",buffer);
       // set num to 10
       if (ioctl(fd, SETNUM, &ret)< 0)
       {
           printf("set num failed\n");
           return -1;
       }
       if(ioctl(fd, GETNUM , &ret)< 0)
       {
           printf("get num failed\n");
       }
       printf("get default value=%d\n",ret);
       // exchange number ret =atoi(argv[2]); 
       if (ioctl(fd, XNUM, &ret)<0)
       {
           printf("exchange num failed\n");
           return -1;
        }
       printf("get value = %d\n",ret);
       return 0;
}


最後是 Makefile








上面全部的檔案都存在mydev 資料夾中
/usr/src/kernels/linux-2.6.18/mydev/..

開始執行
make 之後的指令如下












在 insmod之後 dev會被賦予 major number 與 minor number 
開啟新的cmd 執行dmesg就可以看到kernel傳來一條msg
register  chrdev(252,0)
或是可以到 /proc/devices中查看 major number 與 minor number
在mknod時一定要給定對的major 與 minor number
否則會有 找不到 file 的錯誤
執行後可以看到
kernel 傳回一條 hello messages
而kernel也將預設為10 的value改為 user 設定的 100
最後dmesg中可以看到kernel的訊息












研究好多天仍有很多疑問, 有不完備之處之後繼續修改
參考:
 http://csw17.pixnet.net/blog/post/11506143
http://nano-chicken.blogspot.tw/2009/12/linux-modulesv-ioctl.html
http://www.codeproject.com/Articles/112474/A-Simple-Driver-for-Linux-OS
強烈建議大家在寫這個之前先到jollen的網站看相關的 device 觀念教學

全國推廣動物認領養平台串聯貼紙

全國推廣動物認領養平台串聯貼紙