1. 程式人生 > >深入理解Linux網路技術內幕——使用者空間與核心空間互動

深入理解Linux網路技術內幕——使用者空間與核心空間互動

概述:

    核心空間與使用者空間經常需要進行互動。舉個例子:當用戶空間使用一些配置命令如ifconfig或route時,核心處理程式就要響應這些處理請求。
    使用者空間與核心有多種互動方式,最常用的有以下四種:通過/proc虛擬檔案系統,通過/sys虛擬檔案系統,通過ioctl系統呼叫,通過Netlink socket。 其中編寫程式時最常使用ioctl,這四種方式中有兩種是通過虛擬檔案系統。

 procfs 與 sysctl

    procfs掛載/proc  sysctl掛載在/proc/sys(與後面介紹的/sys不同)。(注意,《Understanding Linux Network Internal》一書中使用的Linux核心已經比較陳舊,procfs與sysfs等核心互動機制已經有較大變化,本文雖然給出了一些新核心的程式碼,但主要功能介紹仍是來源於
《Understanding Linux Network Internal》關於舊核心的說明。)

procfs

procfs是一個虛擬檔案系統,掛載在/proc目錄下,編譯核心時,可以通過配置核心選項make menuconfig->Filesystems  Pseudo filesystems  /proc file system support 來啟用/關閉它。它不能以模組形式載入。 procfs是一個虛擬檔案系統,在磁碟上並沒有真實存在。但是我們卻可以對它進行讀、寫、重定向甚至改變訪問許可權(procfs大部分是隻讀的內容,可寫資料主要是存在於/proc/sys中,具體看sysctl介紹)。     網路功能模組在註冊初始化的時候,會在procfs下注冊一些檔案(網路相關的註冊在/proc/net),用來進行核心與使用者空間的資料互動。

    /proc下的目錄通過proc_mkdir建立,/proc/net下的檔案通過proc_net_fops_create 和 proc_net_remove進行建立和刪除。這兩個函式被封裝成create_proc_entry和remove_proc_entry。(書中是使用的似乎是Linux2.x版本的Linux核心,在新版本<我用的Linux3.12.32>已經找不到這幾個函數了)。書中還給出了arp的例子,新核心中也做了改動,下面給出新核心的Linux在/proc建立arp檔案的實現:
static const struct file_operations arp_seq_fops = {                            
    .owner      = THIS_MODULE,
    .open           = arp_seq_open,
    .read           = seq_read,
    .llseek         = seq_lseek,
    .release    = seq_release_net,
};

static int __net_init arp_net_init(struct net *net)
{
    if (!proc_create("arp", S_IRUGO, net->proc_net, &arp_seq_fops))
        return -ENOMEM;
    return 0;
} 

static inline struct proc_dir_entry *proc_create(const char *name, umode_t mode, struct proc_dir_entry *parent, const struct file_operations *proc_fops)
{
    return proc_create_data(name, mode, parent, proc_fops, NULL);
}


proc_create三個引數表示要建立的檔名為arp,許可權為S_IRUGO(只讀),父目錄介面為net,檔案操作控制代碼集合為arp_seq_fops。 arp_seq_fop 在初始化是會做另一個初始化arp_seq_open 其定義了一個結構體arp_seq_ops,該結構體使得當使用者請求資料時,能只返回一條路徑,就把多個例程的資料返回給使用者。
static int arp_seq_open(struct inode *inode, struct file *file)
{
    return seq_open_net(inode, file, &arp_seq_ops, sizeof(struct neigh_seq_state));
}

static const struct seq_operations arp_seq_ops = {
    .start  = arp_seq_start,
    .next   = neigh_seq_next,
    .stop   = neigh_seq_stop,
    .show   = arp_seq_show,
};


 sysctl:/proc/sys目錄

/proc/sys包含一些核心變數。這些變數可讀、可寫。對於這裡面任何一個變數,核心可以決定在/proc/sys的那個目錄下存放變數、變數名字、變數許可權。     存放在/proc/sys中的檔案和目錄都由 ctl_table結構體進行建立。ctl_table例項通過register_sysctl_table和unregister_sysctl_table進行註冊和刪除。
struct ctl_table 
{
    const char *procname;       /* Text ID for /proc/sys, or zero */
    void *data;
    int maxlen;
    umode_t mode;
    struct ctl_table *child;    /* Deprecated */
    proc_handler *proc_handler; /* Callback for text formatting */
    struct ctl_table_poll *poll;
    void *extra1;
    void *extra2;
};


sysfs

    由於/proc目錄和/proc/sys目錄的濫用,出現了用於替代其檔案功能的sysfs。
    具體另作介紹。

ioctl

    ioctl一般通過socket與核心進行通訊。以ifconfig eth0 mtu 1250為例
struct ifreq data;
fd = socket(PF_INET, SOCK_DGRAM, 0);
< ... initialize "data" ...>
err = ioctl(fd, SIOCSIFMTU, &data);


ifconfig先在本地初始化要傳入核心的資料,然後通過ioctl與核心進行互動。     網路中ioctl普遍使用sock_ioctl來搜尋正確的核心 處理程式處理傳入核心的資料。

Netlink

Netlink使用標準socket與核心進行通訊
int socket(int domain, int type, int protocol)
其中Netlink使用新的協議族PF_NETLINK(domain),並且type只能為SOCK_DGRAM。使用多種protocol,每個protocol代表一種或多種TCP/IP協議棧的元件。比如NETLINK_ROUTE表示網路協議棧的路由功能和鄰居發現協議功能。NETLINK_FIREWALL用於firewall (Netfilter)。
#define NETLINK_ROUTE       0   /* Routing/device hook              */          
#define NETLINK_UNUSED      1   /* Unused number                */
#define NETLINK_USERSOCK    2   /* Reserved for user mode socket protocols  */
#define NETLINK_FIREWALL    3   /* Unused number, formerly ip_queue     */
.....
    Netlink相對於其他(ioctl等)使用者空間與核心互動方式的優點在於,使用Netlink,核心可以主動傳輸核心資料給使用者,而不僅僅是作為使用者請求的一種應答。