netlink_进程与进程_进程与内核间通信

进程与进程间通信方式IPC:

1. 早期unix IPC:管道,FIFO,信号

2. System V IPC(贝尔实验室): system V消息队列,system V信号灯,system V共享内存

3. socket IPC (BSD)

4. Posix IPC: Posix消息队列,Posix信号灯,Posix共享内存

 

线程间通信:

1. 全局变量

2. 线程锁

3. pthread_time_wait

 

进程与内核间通信

1. ioctl()

2. 共享内存:mmap()

3. netlink

4. 其他

其他: 内核启动参数;模块参数;sysfs;procfs;debugfs;relayfs;sysctl;系统调用;copy_from_user()/copy_to_user();get_user()/put_user();信号/信号量/管道等等

 

netlink可以实现

1. 内核向进程广播(如热插拔事件)

2. 进程与内核双向通信

3. 进程与进程通信。

 

参考:

《Linux内核设计与实现》(Linux Kernel Development)第3版 》
Linux 用户态与内核态的交互——netlink 篇:http://bbs.chinaunix.net/thread-2162796-1-1.html
《Linux 系统内核空间与用户空间通信的实现与分析》 陈鑫 http://www-128.ibm.com/developerworks/cn/linux/l-netlink/?ca=dwcn-newsletter-linux
《在 Linux 下用户空间与内核空间数据交换的方式》 杨燚 http://www-128.ibm.com/developerworks/cn/linux/l-kerns-usrs/
http://blog.csdn.net/wangpengqi/article/details/9969599

上: http://blog.chinaunix.net/uid-23069658-id-3400761.html

中: http://blog.chinaunix.net/uid-23069658-id-3405954.html 

下: http://blog.chinaunix.net/uid-23069658-id-3409786.html

 

3.10的内核比2.6的内核,netlink接口函数发生了一些变化。

 

// 内核态
netlink_kernel_create()
netlink_unicast()
netlink_broadcast()

// 用户态
socket()
bind()
sendmsg()
recvmsg()
close()

 

 

 

netlink_进程与进程_进程与内核间通信_第1张图片

 

进程A和子系统1之间是单播通信,进程B、C和子系统2是多播通信。上图还向我们说明了一个信息。从用户空间传递到内核的数据是不需要排队的,即其操作是同步完成;而从内核空间向用户空间传递数据时需要排队,是异步的。

 

Netlink消息由两部分组成:消息头和有效数据载荷,且整个Netlink消息是4字节对齐。消息头为固定的16字节,消息体长度可变,采用TLV(Type-Length-Value)格式

netlink_进程与进程_进程与内核间通信_第2张图片

 

netlink_进程与进程_进程与内核间通信_第3张图片

 

netlink_进程与进程_进程与内核间通信_第4张图片

 

 

// Kernel

#define NETLINK_ATC_SUSPEND 30
#define PM_NETLINK_GROUP 1
#define PM_NETLINK_SUSPEND_KEY "gpio_suspend"
#define ENTER_SUSPEND_GPIO 1 // GPIO1

static struct sock *netlink_sock = NULL;

static int netlink_broadcast(char *msg)
{
    struct sk_buff *skb;
    struct nlmsghdr *nlh;
    int len = strlen(msg)+1;

    printk(KERN_INFO "[%s %d] msg=[%s]\n", __FUNCTION__, __LINE__, msg);
    if(!msg || !netlink_sock){
        return -1;
    }

    skb = nlmsg_new(len, GFP_KERNEL);
    if(!skb){
        printk(KERN_ERR "[%s %d] \n", __FUNCTION__, __LINE__);
        return -1;
    }

    nlh = nlmsg_put(skb, 0, 0, 0, len, 0);
    NETLINK_CB(skb).portid = 0;
    NETLINK_CB(skb).dst_group = PM_NETLINK_GROUP;
    memcpy(NLMSG_DATA(nlh), msg, len);
    
    printk(KERN_ERR "[%s %d] netlink broadcast msg [%s]\n", __FUNCTION__, __LINE__, msg);
    return netlink_broadcast(netlink_sock, skb, 0, PM_NETLINK_GROUP, GFP_KERNEL);
}

static irqreturn_t suspend_gpio_irq_handler(int irq, void *data)
{
    struct gpio_desc *desc = (struct gpio_desc *)data;
    int gpio = desc_to_gpio(desc);
    char msg[64];
    int ret = 0;

    snprintf(msg, sizeof(msg), "%s%d", PM_NETLINK_SUSPEND_KEY, gpio);
    ret = atc_netlink_broadcast(msg);

    return IRQ_HANDLED;
}


static int suspend_gpio_request_irq(void)
{
    int gpio = ENTER_SUSPEND_GPIO;
    struct gpio_desc *desc = gpio_to_desc(gpio);
    int irq = gpiod_to_irq(desc);

    if(irq > 0 && gpio_is_valid(gpio)){
        #if 0
        if(!gpio_suspend_workqueue) {
            gpio_suspend_workqueue = create_singlethread_workqueue("gpio_wakeup");
            INIT_WORK(&atc_gpio_suspend_work, atc_gpio_suspend_work_func);
        }
        #else
        if(!netlink_sock){
            struct netlink_kernel_cfg cfg = {
                .input = NULL,
            };

            extern struct net init_net;
            netlink_sock = netlink_kernel_create(&init_net, NETLINK_ATC_SUSPEND, &cfg);
            if(!netlink_sock){  
                printk(KERN_ERR "[ATC %s %d] netlink_kernel_create fail\n", __FUNCTION__, __LINE__);
                return -1;  
            }
        }
        #endif

        gpio_direction_input(gpio);
        irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING);

        /* Regester IRQ */
        if(request_irq(irq, suspend_gpio_irq_handler, IRQF_SHARED, 
                       PM_NETLINK_SUSPEND_KEY, desc)
        ) {
            printk(KERN_ERR "[%s %d] request_irq fail %d\n", __FUNCTION__, __LINE__, irq);
        }
    }
    else{
        printk(KERN_ERR "[%s %d] irq(%d) or gpio is not valid\n", __FUNCTION__, __LINE__, irq);
    }

    return 0;
}

// User

static void *pm_thread (void *arg)
{
    struct sockaddr_nl src_addr;
    struct nlmsghdr *nlh = NULL;
    struct iovec iov;
    struct msghdr msg;
    int sock_fd, retval;
    int fd_count = 0;
    fd_set rfds, efds;

    sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_SUSPEND);
    if(sock_fd < 0){
        goto exit;
    }

    memset(&src_addr, 0, sizeof(src_addr));
    src_addr.nl_family = PF_NETLINK;
    src_addr.nl_pid = 0; 
    src_addr.nl_groups = 1 << (PM_NETLINK_GROUP - 1);
    retval = bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));
    if (retval < 0) {
        goto exit;
    }

    nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(PM_NETLINK_PAYLOAD_MAX));
    if (!nlh) {
        printf("malloc nlmsghdr %d-%s", errno, strerror(errno));
        goto exit;
    }

    memset(nlh, 0, NLMSG_SPACE(PM_NETLINK_PAYLOAD_MAX));
    iov.iov_base = (void *)nlh;
    iov.iov_len = NLMSG_SPACE(PM_NETLINK_PAYLOAD_MAX);
    memset(&msg, 0, sizeof(msg));
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    FD_ZERO(&rfds);
    FD_ZERO(&efds);
    fd_count = sock_fd;
    while (!pm_thread_quit) {
        FD_SET(sock_fd, &rfds);
        FD_SET(sock_fd, &efds);

        retval = select(fd_count + 1, &rfds, NULL, &efds, NULL);
        if (retval < 0) {
            goto exit;
        } else if (retval == 0) {
        } else if (FD_ISSET(sock_fd, &rfds)){
            if (recvmsg(sock_fd, &msg, 0) > 0) {
                const char *s = (char*)NLMSG_DATA(nlh);
                int len = strlen(PM_NETLINK_SUSPEND_KEY);
                if(!strncmp(s, PM_NETLINK_SUSPEND_KEY, len)) {
                    int pin = atoi(s+len);
                    usleep(100000);
                    if(gpio_get_value(pin) == 1) {
                        printf("state");
                    }
                }
            }
        }
    }

exit:
    if(sock_fd > 0)
        close(sock_fd);
    pm_thread_id = 0;
    return NULL;
}
 

 

 

 

 

 

net_link.c

----------------------------------------------------------------------
#include 
#include 
#include 
#include 
#include 
#include 
#define NETLINK_TEST 21
struct sock *nl_sk = NULL;
EXPORT_SYMBOL_GPL(nl_sk);
void nl_data_ready (struct sk_buff *__skb)
{
  struct sk_buff *skb;
  struct nlmsghdr *nlh;
  u32 pid;
  int rc;
  int len = NLMSG_SPACE(1200);
  char str[100];
  printk("net_link: data is ready to read.\n");
  skb = skb_get(__skb);
  if (skb->len >= NLMSG_SPACE(0)) {
    nlh = nlmsg_hdr(skb);
    printk("net_link: recv %s.\n", (char *)NLMSG_DATA(nlh));
    memcpy(str,NLMSG_DATA(nlh), sizeof(str)); 
    pid = nlh->nlmsg_pid; /*pid of sending process */
    printk("net_link: pid is %d\n", pid);
    kfree_skb(skb);
    skb = alloc_skb(len, GFP_ATOMIC);
    if (!skb){
      printk(KERN_ERR "net_link: allocate failed.\n");
      return;
    }
    nlh = nlmsg_put(skb,0,0,0,1200,0);
    NETLINK_CB(skb).pid = 0; /* from kernel */
    memcpy(NLMSG_DATA(nlh), str, sizeof(str));
    printk("net_link: going to send.\n");
    rc = netlink_unicast(nl_sk, skb, pid, MSG_DONTWAIT);
    if (rc < 0) {
      printk(KERN_ERR "net_link: can not unicast skb (%d)\n", rc);
    }
    printk("net_link: send is ok.\n");
  }
  return;
}
static int test_netlink(void) {
  nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, 0, nl_data_ready, NULL, THIS_MODULE);
  if (!nl_sk) {
    printk(KERN_ERR "net_link: Cannot create netlink socket.\n");
    return -EIO;
  }
  printk("net_link: create socket ok.\n");
  return 0;
}
int init_module()
{
  test_netlink();
  return 0;
}
void cleanup_module( )
{
  if (nl_sk != NULL){
    sock_release(nl_sk->sk_socket);
  }
  printk("net_link: remove ok.\n");
}
MODULE_LICENSE("GPL");

MODULE_AUTHOR("kidoln");

----------------------------------------------------------------------


sender.c

----------------------------------------------------------------------
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define MAX_PAYLOAD 1024 /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;
int main(int argc, char* argv[])
{
        sock_fd = socket(PF_NETLINK, SOCK_RAW, 21);
        memset(&msg, 0, sizeof(msg));
        memset(&src_addr, 0, sizeof(src_addr));
        src_addr.nl_family = AF_NETLINK;
        src_addr.nl_pid = getpid(); /* self pid */
        src_addr.nl_groups = 0; /* not in mcast groups */
        bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));
        memset(&dest_addr, 0, sizeof(dest_addr));
        dest_addr.nl_family = AF_NETLINK;
        dest_addr.nl_pid = 0; /* For Linux Kernel */
        dest_addr.nl_groups = 0; /* unicast */
        nlh=(struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
        /* Fill the netlink message header */
        nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
        nlh->nlmsg_pid = getpid(); /* self pid */
        nlh->nlmsg_flags = 0;
        /* Fill in the netlink message payload */
        strcpy(NLMSG_DATA(nlh), "Hello you!");
        iov.iov_base = (void *)nlh;
        iov.iov_len = nlh->nlmsg_len;
        msg.msg_name = (void *)&dest_addr;
        msg.msg_namelen = sizeof(dest_addr);
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        printf(" Sending message. ...\n");
        sendmsg(sock_fd, &msg, 0);
        /* Read message from kernel */
        memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
        printf(" Waiting message. ...\n");
        recvmsg(sock_fd, &msg, 0);
        printf(" Received message payload: %s\n",NLMSG_DATA(nlh));
         /* Close Netlink Socket */
        close(sock_fd);
}

----------------------------------------------------------------------


Makefile

----------------------------------------------------------------------
MODULE_NAME :=net_link
obj-m :=$(MODULE_NAME).o
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
    $(MAKE) -C $(KERNELDIR) M=$(PWD)
    gcc -o sender sender.c
clean:

    rm -fr *.ko *.o *.cmd sender $(MODULE_NAME).mod.c

----------------------------------------------------------------------

 

 

 

 

 

你可能感兴趣的:(源码解析)