Saturday, May 26, 2012

Bootloader與Kernel間參數傳遞機制




Tag list 被用來在 bootloader 和 linux kernel 之間傳遞參數,這裡分析一下相關的數據結構,主要包括兩個部分: Tag list 和 Tag parser list 。
先來看 Tag list :
這個 list 是在 bootloader 中填充的,其數據結構定義在 bootloader 和 linux kernel 中均有定義,是一致的。我們來看 linux kernel 中的定義:

top/arch/arm/include/asm/setup.h
struct tag {
    struct tag_header hdr;
    union {
        struct tag_core        core;
        struct tag_mem32    mem;
        struct tag_videotext    videotext;
        struct tag_ramdisk    ramdisk;
        struct tag_initrd    initrd;
        struct tag_serialnr    serialnr;
        struct tag_revision    revision;
        struct tag_videolfb    videolfb;
        struct tag_cmdline    cmdline;
        /*
         * Acorn specific
         */

        struct tag_acorn    acorn;
        /*
         * DC21285 specific
         */

        struct tag_memclk    memclk;
    } u;
} ;
struct tag_header {
    __u32 size;
    __u32 tag;
} ;

其中 tag 的取值如下,暫且稱之為 tag type :

# define ATAG_CORE            0x54410001
# define ATAG_MEM            0x54410002
# define ATAG_VIDEOTEXT        0x54410003
# define ATAG_RAMDISK        0x54410004
# define ATAG_INITRD            0x54410005
# define ATAG_INITRD2        0x54420005
# define ATAG_SERIAL            0x54410006
# define ATAG_REVISION        0x54410007
# define ATAG_VIDEOLFB        0x54410008
# define ATAG_CMDLINE        0x54410009
# define ATAG_ACORN            0x41000101
# define ATAG_MEMCLK        0x41000402

其數據結構用圖形表示就是:
 
其實就是一個鏈表,通過 Tag size 以及當前 tag 的位置來定位下一個 tag 的位置。而且第一個 tag 的類型必然是 ATAG_CORE 。

參數就是按照這個結構進行傳遞的,那麼 kernel 是如何進行解析的呢?
我們來看 tag parser list:
同樣是在 top/arch/arm/include/asm/setup.h ,有如下定義:

top/arch/arm/include/asm/setup.h
struct tagtable {
    __u32 tag;
    int ( * parse) ( const struct tag * ) ;
} ;
# define __tag __used __attribute__( ( __section__( ".taglist.init" ) ) )
# define __tagtable( tag, fn) /
static struct tagtable __tagtable_# # fn __tag = { tag, fn }

從上面知道, tag parser list 存在於 .taglist.init 段,他們的定義將通過宏 __tagtable(tag, fn) 的形式給出,比如在 top/arch/arm/kernel/setup.c 中:

top/arch/arm/kernel/setup.c
__tagtable( ATAG_CORE, parse_tag_core) ;
__tagtable( ATAG_MEM, parse_tag_mem32) ;
__tagtable( ATAG_VIDEOTEXT, parse_tag_videotext) ;
__tagtable( ATAG_RAMDISK, parse_tag_ramdisk) ;
__tagtable( ATAG_SERIAL, parse_tag_serialnr) ;
__tagtable( ATAG_REVISION, parse_tag_revision) ;
__tagtable( ATAG_CMDLINE, parse_tag_cmdline) ;

通過這樣的定義,每個 tag table 的表項就自 動連接在了一起,而且存在於同一個段中。如圖所示:
可以看到,所有支持的 tag parser 都列在這 裡了。
在 kernel 中,將針對 tag list 中的每一項在這個 tag parser list 中進行查找,如果有 對應的處理項,則調用解析函數,於是就完成了參數的傳遞以及解析!

注意:
在 top/arch/arm/kernel/head-common.s 中會對從 bootloader 傳遞過來的 tag list 進行合法性判斷:
以標號 __vet_atags 開始的一段處理就要是 判斷 tag list 的第一項是否是 ATAG_CORE ,同時判斷長度是否越界!

Tuesday, May 08, 2012

schedule_delayed_work()用法

資料來源: schedule_delayed_work()用法

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/net.h>
#include <net/sock.h>
#include <linux/in.h>
#include <linux/types.h>
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/skbuff.h>
#include <linux/string.h>
#include <asm-generic/unaligned.h>
#include <linux/sysctl.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <asm/checksum.h>
#include <linux/ip.h>
#include <linux/workqueue.h>

#define err(msg) printk(KERN_INFO "%s failed.\n", msg)

static void defense_work_handler(struct work_struct *work);

static DECLARE_DELAYED_WORK(defense_work, defense_work_handler);

static void defense_work_handler(struct work_struct *work)
{
    printk(KERN_INFO "defense_work_handler function.\n");
}

static int __init main_init(void)
{
    schedule_delayed_work(&defense_work, 3 * HZ);

    return 0;
}

static void __exit main_exit(void)
{
    cancel_delayed_work_sync(&defense_work);
}

module_init(main_init);
module_exit(main_exit);
MODULE_LICENSE("GPL");


Wednesday, April 18, 2012

100 個程序員必須知道的 Vim 命令

資料來源:100 個程序員必須知道的 Vim 命令


100 Vim commands every programmer should know

Search/搜索
/word Search 「word」 from top to bottom 從上向下搜索』word'
?word Search 「word」 from bottom to top 從上向下搜索』word'
/jo[ha]n Search 「john」 or 「joan」 搜索」john」或」joan」
/\< the Search 「the」, 「theatre」 or 「then」 /the\> Search 「the」 or 「breathe」
/\< the\> Search 「the」
/\< ….\> Search all words of 4 letters
/\/ Search 「fred」 but not 「alfred」 or 「frederick」
/fred\|joe Search 「fred」 or 「joe」
/\<\d\d\d\d\> Search exactly 4 digits
/^\n\{3} Find 3 empty lines
:bufdo /searchstr/ Search in all open files

Replace
:%s/old/new/g Replace all occurences of 「old」 by 「new」 in file
:%s/old/new/gw Replace all occurences with confirmation
:2,35s/old/new/g Replace all occurences between lines 2 and 35
:5,$s/old/new/g Replace all occurences from line 5 to EOF
:%s/^/hello/g Replace the begining of each line by 「hello」
:%s/$/Harry/g Replace the end of each line by 「Harry」
:%s/onward/forward/gi Replace 「onward」 by 「forward」 , case unsensitive
:%s/ *$//g Delete all white spaces
:g/string/d Delete all lines containing 「string」
:v/string/d Delete all lines containing which didn't contain 「string」
:s/Bill/Steve/ Replace the first occurence of 「Bill」 by 「Steve」 in current line
:s/Bill/Steve/g Replace 「Bill」 by 「Steve」 in current line
:%s/Bill/Steve/g Replace 「Bill」 by 「Steve」 in all the file
:%s/\r//g Delete DOS carriage returns (^M)
:%s/\r/\r/g Transform DOS carriage returns in returns
:%s#<[^>]\+>##g Delete HTML tags but keeps text
:%s/^\(.*\)\n\1$/\1/ Delete lines which appears twice
Ctrl+a Increment number under the cursor
Ctrl+x Decrement number under cursor
ggVGg? Change text to Rot13

Case
Vu Lowercase line
VU Uppercase line
g~~ Invert case
vEU Switch word to uppercase
vE~ Modify word case
ggguG Set all text to lowercase
:set ignorecase Ignore case in searches
:set smartcase Ignore case in searches excepted if an uppercase letter is used
:%s/\<./\u&/g Sets first letter of each word to uppercase :%s/\<./\l&/g Sets first letter of each word to lowercase :%s/.*/\u& Sets first letter of each line to uppercase :%s/.*/\l& Sets first letter of each line to lowercase

Read/Write files
:1,10 w outfile Saves lines 1 to 10 in outfile :1,10 w >> outfile Appends lines 1 to 10 to outfile
:r infile Insert the content of infile
:23r infile Insert the content of infile under line 23

File explorer
:e . Open integrated file explorer
:Sex Split window and open integrated file explorer
:browse e Graphical file explorer
:ls List buffers
:cd .. Move to parent directory
:args List files
:args *.php Open file list
:grep expression *.php Returns a list of .php files contening expression
gf Open file name under cursor

Interact with Unix
:!pwd Execute the 「pwd」 unix command, then returns to Vi
!!pwd Execute the 「pwd」 unix command and insert output in file
:sh Temporary returns to Unix
$exit Retourns to Vi

Alignment
:%!fmt Align all lines
!}fmt Align all lines at the current position
5!!fmt Align the next 5 lines

Tabs
:tabnew Creates a new tab
gt Show next tab
:tabfirst Show first tab
:tablast Show last tab
:tabm n(position) Rearrange tabs
:tabdo %s/foo/bar/g Execute a command in all tabs
:tab ball Puts all open files in tabs

Window spliting
:e filename Edit filename in current window
:split filename Split the window and open filename
ctrl-w up arrow Puts cursor in top window
ctrl-w ctrl-w Puts cursor in next window
ctrl-w_ Maximise current window
ctrl-w= Gives the same size to all windows
10 ctrl-w+ Add 10 lines to current window
:vsplit file Split window vertically
:sview file Same as :split in readonly mode
:hide Close current window
nly Close all windows, excepted current
:b 2 Open #2 in this window

Auto-completion
Ctrl+n Ctrl+p (in insert mode) Complete word
Ctrl+x Ctrl+l Complete line
:set dictionary=dict Define dict as a dictionnary
Ctrl+x Ctrl+k Complete with dictionnary

Marks
mk Marks current position as k
『k Moves cursor to mark k
d'k Delete all until mark k

Abbreviations
:ab mail mail@provider.org Define mail as abbreviation of mail@provider.org

Text indent
:set autoindent Turn on auto-indent
:set smartindent Turn on intelligent auto-indent
:set shiftwidth=4 Defines 4 spaces as indent size
ctrl-t, ctrl-d Indent/un-indent in insert mode
>> Indent
<< Un-indent

Syntax highlighting
:syntax on Turn on syntax highlighting
:syntax off Turn off syntax highlighting
:set syntax=perl Force syntax highlighting

Sunday, April 15, 2012

C語言常用宏定義

資料來源: C語言常用宏定義

01: 防止一個頭文件被重複包含

#ifndef COMDEF_H
#define COMDEF_H
//頭文件內容
#endif

02: 重新定義一些類型,防止由於各種平台和編譯器的不同,而產生的類型字節數差異,方便移植。
typedef  unsigned char      boolean;     /* Boolean value type. */
typedef  unsigned long int  uint32;      /* Unsigned 32 bit value */
typedef  unsigned short     uint16;      /* Unsigned 16 bit value */
typedef  unsigned char      uint8;       /* Unsigned 8  bit value */
typedef  signed long int    int32;       /* Signed 32 bit value */
typedef  signed short       int16;       /* Signed 16 bit value */
typedef  signed char        int8;        /* Signed 8  bit value */

//下面的不建議使用
typedef  unsigned char     byte;         /* Unsigned 8  bit value type. */
typedef  unsigned short    word;         /* Unsinged 16 bit value type. */
typedef  unsigned long     dword;        /* Unsigned 32 bit value type. */
typedef  unsigned char     uint1;        /* Unsigned 8  bit value type. */
typedef  unsigned short    uint2;        /* Unsigned 16 bit value type. */
typedef  unsigned long     uint4;        /* Unsigned 32 bit value type. */
typedef  signed char       int1;         /* Signed 8  bit value type. */
typedef  signed short      int2;         /* Signed 16 bit value type. */
typedef  long int          int4;         /* Signed 32 bit value type. */
typedef  signed long       sint31;       /* Signed 32 bit value */
typedef  signed short      sint15;       /* Signed 16 bit value */
typedef  signed char       sint7;        /* Signed 8  bit value */

03: 得到指定地址上的一個字節或字
#define  MEM_B(x) (*((byte *)(x)))
#define  MEM_W(x) (*((word *)(x)))

04: 求最大值和最小值
#define  MAX(x,y) (((x)>(y)) ? (x) : (y))
#define  MIN(x,y) (((x) < (y)) ? (x) : (y))  05: 得到一個field在結構體(struct)中的偏移量 #define FPOS(type,field) ((dword)&((type *)0)->field)

06: 得到一個結構體中 field 所佔用的字節數
#define FSIZ(type,field) sizeof(((type *)0)->field)

07: 按照 LSB 格式把兩個字節轉化為一個 Word

#define FLIPW(ray) ((((word)(ray)[0]) * 256) + (ray)[1])
 

08: 按照 LSB 格式把一個 Word 轉化為兩個字節
#define FLOPW(ray,val) (ray)[0] = ((val)/256); (ray)[1] = ((val) & 0xFF)

09: 得到一個變量的地址(word寬度)
#define B_PTR(var)  ((byte *) (void *) &(var))
#define W_PTR(var)  ((word *) (void *) &(var))

10: 得到一個字的高位和低位字節
#define WORD_LO(xxx)  ((byte) ((word)(xxx) & 255))
#define WORD_HI(xxx)  ((byte) ((word)(xxx) >> 8))

11: 返回一個比X大的最接近的8的倍數
#define RND8(x) ((((x) + 7)/8) * 8)

12: 將一個字母轉換為大寫
#define UPCASE(c) (((c)>='a' && (c) <= 'z') ? ((c) - 0x20) : (c))

13: 判斷字符是不是 10 進值的數字
#define  DECCHK(c) ((c)>='0' && (c)<='9')

14: 判斷字符是不是16進值的數字
#define HEXCHK(c) (((c) >= '0' && (c)<='9') ((c)>='A' && (c)<= 'F') \ ((c)>='a' && (c)<='f'))

15: 防止溢出的一個方法
#define INC_SAT(val) (val=((val)+1>(val)) ? (val)+1 : (val))

16: 返回數組元素的個數
#define ARR_SIZE(a)  (sizeof((a))/sizeof((a[0])))

17: 返回一個無符號數 n 尾的值
#define MOD_BY_POWER_OF_TWO(X,n)=X%(2^n)
#define MOD_BY_POWER_OF_TWO( val, mod_by ) ((dword)(val) & (dword)((mod_by)-1))

18: 對於IO空間映射在存儲空間的結構,輸入輸出處理
#define inp(port) (*((volatile byte *)(port)))
#define inpw(port) (*((volatile word *)(port)))
#define inpdw(port) (*((volatile dword *)(port)))
#define outp(port,val) (*((volatile byte *)(port))=((byte)(val)))
#define outpw(port, val) (*((volatile word *)(port))=((word)(val)))
#define outpdw(port, val) (*((volatile dword *)(port))=((dword)(val)))

19: 使用一些宏跟蹤調試
ANSI標準說明了五個預定義的宏名。它們是:
__LINE__
__FILE__
__DATE__
__TIME__
__STDC__
C++中還定義了 __cplusplus

如果編譯器不是標準的,則可能僅支持以上宏名中的幾個,或根本不支持。記住編譯程序也許還提供其它預定義的宏名。

__LINE__ 及 __FILE__ 宏指示,#line指令可以改變它的值,簡單的講,編譯時,它們包含程序的當前行數和文件名。

__DATE__ 宏指令含有形式為月/日/年的串,表示源文件被翻譯到代碼時的日期。
__TIME__ 宏指令包含程序編譯的時間。時間用字符串表示,其形式為: 分:秒
__STDC__ 宏指令的意義是編譯時定義的。一般來講,如果__STDC__已經定義,編譯器將僅接受不包含任何非標準擴展的標準C/C++代碼。如果實現是標準的,則宏__STDC__含有十進制常量1。如果它含有任何其它數,則實現是非標準的。
__cplusplus 與標準c++一致的編譯器把它定義為一個包含至少6為的數值。與標準c++不一致的編譯器將使用具有5位或更少的數值。


可以定義宏,例如:
當定義了_DEBUG,輸出數據信息和所在文件所在行
#ifdef _DEBUG
#define DEBUGMSG(msg,date) printf(msg);printf(「%d%d%d」,date,_LINE_,_FILE_)
#else
#define DEBUGMSG(msg,date) 
#endif

20: 宏定義防止錯誤使用小括號包含。
例如:
有問題的定義:#define DUMP_WRITE(addr,nr) {memcpy(bufp,addr,nr); bufp += nr;}
應該使用的定義: #difne DO(a,b) do{a+b;a++;}while(0)
例如:
if(addr)
DUMP_WRITE(addr,nr);
else
do_somethong_else();
宏展開以後變成這樣:
if(addr)
{memcpy(bufp,addr,nr); bufp += nr;};
else
do_something_else();

gcc 在碰到else前面的「;」時就認為if語句已經結束,因而後面的else不在if語句中。而採用do{} while(0)的定義,在任何情況下都沒有問題。而改為 #difne DO(a,b) do{a+b;a++;}while(0) 的定義則在任何情況下都不會出錯。

__FILE__,__LINE__,FUNCTION__實現代碼跟蹤調試(linux下c語言編程)

資料來源:__FILE__,__LINE__,FUNCTION__實現代碼跟蹤調試(linux下c語言編程)



先看下簡單的初始代碼:注意其編譯運行後的結果。
root@xuanfei-desktop:~/cpropram/2# cat global.h //頭文件
#ifndef GLOBAL_H
#define GLOBAL_H

#include <stdio.h>

int funca(void);
int funcb(void);

#endif
root@xuanfei-desktop:~/cpropram/2# cat funca.c //函數a
#include "global.h"

int funca(void)
{
   printf ("this is function\n");
   return 0;
}
root@xuanfei-desktop:~/cpropram/2# cat funcb.c //函數b
#include "global.h"

int funcb(void)
{
   printf ("this is function\n");
   return 0;
}
root@xuanfei-desktop:~/cpropram/2# gcc -Wall funca.c funcb.c main.c //聯合編譯
root@xuanfei-desktop:~/cpropram/2# ./a.out //運行
this is main
this is function
this is main
this is function
this is main
相同結果很難讓人看出那裡出錯,下面我們用用 __FILE__,__LINE__,__FUNCTION__加入代碼,看看有什麼區別嗎.
把 __FILE__,__LINE__,__FUNCTION__加入到mail.c中
root@xuanfei-desktop:~/cpropram/2# cat main.c
#include "global.h"

int main(int argc, char **argv)
{
   printf("%s(%d)-%s: this is main\n",__FILE__,__LINE__,__FUNCTION__);
   funca();
   printf("%s(%d)-%s: this is main\n",__FILE__,__LINE__,__FUNCTION__);
   funcb();
   printf("%s(%d)-%s: this is main\n",__FILE__,__LINE__,__FUNCTION__);
   return 0;
}
root@xuanfei-desktop:~/cpropram/2# gcc -Wall funca.c funcb.c main.c
root@xuanfei-desktop:~/cpropram/2# ./a.out
main.c(4)-main: this is main
this is function
main.c(6)-main: this is main
this is function
main.c(8)-main: this is main
上面的結果main.c(4)-main:this is main 表示在mian.c源代碼的第四行main函數裡邊打印出來的 this is main
那樣的話就很方便的讓程序員對自己的程序進行排錯!
為了更方便的使用它我們可以通過在global.h代碼中進行宏定義
root@xuanfei-desktop:~/cpropram/2# cat global.h
#ifndef GLOBAL_H
#define GLOBAL_H
#include <stdio.h>

int funca(void);
int funcb(void);

#define DEBUGFMT  "%s(%d)-%s"
#define DEBUGARGS __FILE__,__LINE__,__FUNCTION__
#endif
root@xuanfei-desktop:~/cpropram/2# cat funca.c
#include "global.h"

int funca(void)
{
   printf (DEBUGFMT " this is function\n",DEBUGARGS);
   return 0;
}
root@xuanfei-desktop:~/cpropram/2# cat funcb.c
#include "global.h"

int funcb(void)
{
   printf (DEBUGFMT " this is function\n",DEBUGARGS);
   return 0;
}
root@xuanfei-desktop:~/cpropram/2# cat main.c
#include "global.h"

int main(int argc, char **argv)
{
   printf(DEBUGFMT "this is main\n", DEBUGARGS);
   funca();
   printf(DEBUGFMT "this is main\n", DEBUGARGS);
   funcb();
   printf(DEBUGFMT "this is main\n", DEBUGARGS);
   return 0;
}
root@xuanfei-desktop:~/cpropram/2# gcc -Wall funca.c funcb.c main.c
root@xuanfei-desktop:~/cpropram/2# ./a.out
main.c(4)-mainthis is main
funca.c(4)-funca this is function
main.c(6)-mainthis is main
funcb.c(4)-funcb this is function
main.c(8)-mainthis is main
root@xuanfei-desktop:~/cpropram/2#
這就是通過定義__FILE__,__LINE__,FUNCTION__的宏來簡單實現代碼的跟蹤調試:)

下面是一個可供調試用的頭文件
#ifndef _GOLD_DEBUG_H
#define _GOLD_DEBUG_H

#ifdef __cplusplus
#if __cplusplus
extern "C"{
#endif
#endif /* __cplusplus */

//#define GI_DEBUG

#ifdef GI_DEBUG

#define GI_DEBUG_POINT()   printf("\n\n[File:%s Line:%d] Fun:%s\n\n", __FILE__, __LINE__, __FUNCTION__)
#define dbg_printf(arg...)   printf(arg);

#define GI_ASSERT(expr)                                     \
    do{                                                     \
        if (!(expr)) { \
            printf("\nASSERT failed at:\n  >File name: %s\n  >Function : %s\n  >Line No. : %d\n  >Condition: %s\n", \
                    __FILE__,__FUNCTION__, __LINE__, #expr);\
        } \
    }while(0);

/*調試宏, 用於暫停*/
#define GI_DEBUG_PAUSE()           \
 do               \
 {               \
  GI_DEBUG_POINT();          \
  printf("pause for debug, press 'q' to exit!\n");  \
  char c;             \
  while( ( c = getchar() ) )        \
   {             \
    if('q' == c)         \
     {           \
      getchar();        \
      break;         \
     }           \
   }             \
 }while(0);
#define GI_DEBUG_PAUSE_ARG(arg...)          \
  do               \
  {               \
   printf(arg);           \
   GI_DEBUG_PAUSE()          \
  }while(0);


#define GI_DEBUG_ASSERT(expression)      \
if(!(expression))                        \
{                                  \
    printf("[ASSERT],%s,%s:%d\n", __FILE__,  __FUNCTION__, __LINE__);\
    exit(-1);             \
}
#else
#define GI_ASSERT(expr)
#define GI_DEBUG_PAUSE()
#define GI_DEBUG_PAUSE_ARG(arg...)
#define GI_DEBUG_POINT()
#define dbg_printf(arg...)
#define GI_DEBUG_ASSERT(expression)

#endif

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */


#endif