當(dāng)前位置:首頁 > 芯聞號(hào) > 充電吧
[導(dǎo)讀]內(nèi)核態(tài)與用戶態(tài)通信方式 Linux下內(nèi)核空間與用戶空間進(jìn)行通信的方式主要有system call、sysctl、procfs、模塊參數(shù)、debugfs、relayfs、sysfs和netlink等。

內(nèi)核態(tài)與用戶態(tài)通信方式

Linux下內(nèi)核空間與用戶空間進(jìn)行通信的方式主要有system call、sysctl、procfs、模塊參數(shù)、debugfs、relayfs、sysfs和netlink等。

Why NetlinK full-duplex

模塊參數(shù)、sysfs、procfs、debugfs和relayfs都是基于文件系統(tǒng),用于內(nèi)核向用戶發(fā)送消息;sysctl和system call是用戶向內(nèi)核發(fā)送消息。它們都是單工通信方式。netlink是一種特殊的通信方式,用于在內(nèi)核空間和用戶空間傳遞消息,是一種雙工通信方式。使用地址協(xié)議簇AF_NETLINK,使用頭文件include/linux/netlink.h。

simple to add
為新特性添加system call、sysctl或者procfs是一件復(fù)雜的工作,它們會(huì)污染kernel,破壞系統(tǒng)的穩(wěn)定性,這是非常危險(xiǎn)的。Netlink的添加,對內(nèi)核的影響僅在于向netlink.h中添加一個(gè)固定的協(xié)議類型,然后內(nèi)核模塊和應(yīng)用層的通信使用一套標(biāo)準(zhǔn)的API。 Netlink Socket APIs 用戶空間 創(chuàng)建
int socket(int doamin, int type, int protocal);

domain填A(yù)F_NETLINK,type為SOCK_RAW或者SOCK_DGRAM。protocal可選NETLINK_ROUTE、NETLINK_FIREWALL、NETLINK_ARPD、NETLINK_ROUTE6和NETLINK_IP6_FW,或者傳入自定義協(xié)議類型。

綁定
int bind(int fd, (struct sockaddr *)&nladdr, sizeof(nladdr));

netlink地址結(jié)構(gòu)如下

struct sockaddr_nl{
    sa_family_t     nl_family;      //AF_NETLINK
    unsigned short  nl_pad;         //0
    __u32           nl_pid;         //進(jìn)程pid
    __u32           nl_groups;      //多播組掩碼
}

如果進(jìn)程僅僅使用一個(gè)netlink,nl_pid可以傳入該進(jìn)程的pid,

my_pid = getpid();

如果一個(gè)進(jìn)程的多個(gè)線程需要不同的netlink,使用如下方法獲得nl_pid:

pthread_self << 16 | gitpid();
發(fā)送
int sendmsg(fd, &msg, 0);

struct nlmsghdr msg為netlink發(fā)送消息結(jié)構(gòu),

struct msghdr{
    void            *msg_name;      //socket name
    int             msg_namelen;    //len of name
    struct iovec    *msg_iov;       //data block
    __kernel_size_t msg_iovlen;     //num of block
    void            *msg_control;   //per protocol magic
    __kernel_size_t msg_controllen; //len of cmsg list
    unsigned        msg_flags;

}

其中需要填充的是msg_iov和msg_oivlen。
msg與其他結(jié)構(gòu)體的關(guān)系如圖所示

nlmsghdr結(jié)構(gòu)體為消息頭信息

struct nlmsghdr{
    __u32 nlmsg_len;    //len of msg
    __u16 nlmsg_type;   //msg type
    __u16 nlmsg_flags;  //additional flags
    __u32 nlmsg_seq;    //sequence num
    __u32 nlmsg_pid;    //send process pid
}

nlmsg_len是整個(gè)消息的長度,包含頭;
nlmsg_type對內(nèi)核不透明;
nlmsg_flag被用于對消息的附加控制;
nlmsg_seq和nlmsg_pid被用于跟蹤消息,對內(nèi)核不透明。

要將地址結(jié)構(gòu)體傳入消息頭部:

struct msghdr msg;
msg.msg_name = (void *)&nladdr;
msg.msg_namelen = sizeof(nladdr);

發(fā)送地址同樣使用sockaddr_nl結(jié)構(gòu)體,如果目的是內(nèi)核,nl_pid和nl_groups都為0;如果消息為單播,目的為另一個(gè)進(jìn)程,nl_pid是目的進(jìn)程pid,nl_groups為0;如果消息為多播,目的是一個(gè)或多個(gè)多播組,nl_groups域中需填寫所有目的多播組的掩碼和。

結(jié)構(gòu)體iovce結(jié)構(gòu)如下

struct iovec iov;
iov.iov_base = (void *)nlh;
iov.iov_len = nlh->nlmsg_len;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
接收

接收時(shí)需要?jiǎng)?chuàng)建足夠的buf,

struct sockadddr_nl nladdr;
struct msghdr msg;
struct iovec iov;

iov.iov_base = (void *)nlh;
iov.iov_len = MAX_NL_MSG_LEN;
msg.msg_name = (void *)&nladdr;
msg.msg_namelen = sizeof(nladdr);

msg.msg_iov = &iov;
msg.msg_iovlen = 1;
recvmsg(fd, &msg, 0);
內(nèi)核空間

內(nèi)核空間的API由net/core/af_netlink.c支持。
可以在netlink.h中添加自定義協(xié)議類型:

#define NETLINK_TEST    17
創(chuàng)建
struct sock *netlink_kernel_create(int unit, 
        void (*input)(struct sock *sk, int len));

unit是netlink.h中定義的協(xié)議類型,*input是一個(gè)回調(diào)函數(shù)指針,接收到消息會(huì)觸發(fā)這個(gè)函數(shù)。

發(fā)送

發(fā)送消息使用struct sk_buff *skb。
用以下方式設(shè)置本地地址:

NETLINK_CB(skb).groups = local_groups;
NETLINK_CB(skb).pid = 0;

因?yàn)槭窃趦?nèi)核,pid = 0

用以下方式設(shè)置目的地址:

NETLINK_CB(skb).dst_groups = dst_groups;
NETLINK_CB(skb).dst_pid = dst_pid;

發(fā)送單播函數(shù)為

int netlink_unicast(struct sock *sk, struct sk_buff *skb, u32 pid, int nonblock);

*sk為netlink_kernel_create()返回的結(jié)構(gòu)體指針;
skb->data指向消息;
pid為應(yīng)用程序的pid;
block表明如果當(dāng)接收緩沖非法時(shí)數(shù)據(jù)分塊還是返回錯(cuò)誤值。

發(fā)送多播函數(shù)為

void netlink_broadcast(struct sock *sk, struct sk_buff *skb, u32 pid, u32 group, int allocation);

group是所有需要接收多播消息的組掩碼和;
allocation是內(nèi)核內(nèi)存分配標(biāo)志,中斷上下文中使用GFP_ATOMIC,其他情況使用GFP_KERNEL。

銷毀
sock_release(nl_sk->socket);
example user-space 初始化
#define MAX_NLMSG_PAYLOAD_LEN       128
#define NETLINK_GROUP_MASK(group)   (1 << group)

int usr_netlink_init(int netlink_type, int *fd, int group)
{
    struct nlmsghdr *nlh = NULL;
    struct sockaddr_nl addr;

    memset((void *)&addr, 0x0, sizeof(addr));

    if ((*fd = socket(PF_NETLINK, SOCK_RAW, netlink_type)) < 0)
    {
        /* error process*/
        return -1;
    }

    addr.nl_family = AF_NETLINK;
    addr.nl_pid = getpid();
    addr.nl_groups = NETLINK_GROUP_MASK(group);

    if (bind(*fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        /* error process */
        close(*fd);
        *fd = -1;
        return -2;
    }

    /* send pid to kernel */
    if (us_netlink_send(*fd, 0, NULL, 0) < 0)
    {
        /* error process */
        close(*fd);
        *fd = -1;
        return -3;
    }

    return 0;
}
發(fā)送
int usr_netlink_send(int fd, uint16 msg_type, char *data, int data_len)
{
    struct nlmsghdr *nlh;
    struct msghdr msg;
    struct iovec iov;
    int msg_len;

    if (data && (msg_len > MAX_NLMSG_PAYLOAD_LEN))
    {
        return -1;
    }

    memset((void*)&iov, 0x0, sizeof(iov));
    memset((void*)&msg, 0x0, sizeof(msg));        
    msg_len = NLMSG_SPACE(data_len);

    nlh = (struct nlmsghdr *)malloc(msg_len);
    if (NULL = nlh)
    {
        return -2;
    }

    /* fill nlmsghdr */
    memset(nlh, 0x0, msg_len);
    nlh->nlmsg_len = msg_len;
    nlh->nlmsg_pid = getpid();
    nlh->nlmsg_type = msg_type;
    nlh->nlmsg_flags = 0;

    if (data)
    {
        memcpy(NLMSG_DATA(nlh), data, msg_len);
    }

    /* fill iovec */
    iov.iov_base = (void *)nlh;
    iov.iov_len = nlh->nlmsg_len;
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    if (sendmsg(fd, &msg, 0) < 0)
    {
        /* error process */
        free(nlh);
        return -3;
    }

    free(nlh);
    return 0;
}
接收
int usr_netlink_recv(int fd, char *buf, int len)
{
    struct iovec iov = {buf, len};
    struct sockaddr_nl nl_src_addr;
    struct msghdr msg;
    int recv_len;

    memset(&msg, 0x0, sizeof(struct msghdr));
    memset(&nl_src_addr, 0x0, sizeof(struct sockaddr_nl));

    msg.msg_name = (void *)&nl_src_addr;
    msg.msg_namelen = sizeof(nl_src_addr);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    recv_len = recvmsg(fd, &msg, 0);
    if (recv_len < 0)
    {
        /* recv error */
    }
    else if (recv_len == 0)
    {
        /* recv EOF */
    }

    return recv_len;
}
kernel 初始化
static struct sock *sg_knl_xxx_sk = NULL;
static int sg_knl_xxx_pid = -1;

void knl_netlink_init()
{   
    struct net init_net;

    sg_knl_xxx_sk = netlink_kernel_create(&init_net, NETLINK_TEST, 0, knl_neklink_recv, NULL, THIS_MODULE);
}
接收
void knl_netlink_recv(struct sock *skb)
{
    struct nlmsghdr *nlh = nlmsg_hdr(skb);
    sg_knl_xxx_pid = nlh->nlmsg_pid;
}
發(fā)送
int group_mask(int group)
{
    return (1 << group);
}

void knl_netlink_send(int msg_type, char *data, int data_len, int group)
{
    struct sk_buff *skb = NULL;
    struct nlmsghdr *nlh = NULL;
    unsigned int msg_len = NLMSG_SPACE(data_len);

    if(data && (data_len > MAX_PAYLOAD_LEN))
    {
        /* error process */
        return;
    } 

    skb = alloc_skb(msg_len, GFP_KERNEL);
    if (!skb)
    {
        /* erro process */
        return;
    }

    memset((void *)skb, 0x0, msg_len);
    nlh = nlmsg_put(skb, 0, 0, msg_type, msg_len, 0);

    if(data)
    {
        memcpy(NLMSG_DATA(nlh), data, data_len);
    } 

    NETLINK_CB(skb).pid = 0; /*from kernel */
    NETLINK_CB(skb).dst_group = group_mask(group); 

    /* if unicast */
    netlink_unicast(sg_knl_xxx_sk, skb, sg_knl_xxx_pid, MSG_DONTWAIT);
    /* if mulicast */
    //nlmsg_multicast(sg_knl_xxx_sk, skb, 0, group_mask(group), 0);
}
本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運(yùn)營商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡稱"軟通動(dòng)力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉