首页 > 系统相关 >Linux用户态/内核态通信方式汇总

Linux用户态/内核态通信方式汇总

时间:2024-03-19 23:45:55浏览次数:30  
标签:return int 汇总 内核 Linux device include my

Linux用户态/内核态通信方式汇总

目录

用户态和内核态之间的通信方式主要有以下几种:

系统调用(System Call):这是最常见的方式,用户态程序通过系统调用接口(如open、read、write、fork等)请求内核执行特定的动作。系统调用是用户态和内核态之间进行交互的桥梁,它允许用户态程序请求内核提供服务。
中断(Interrupts):中断包括软中断和硬中断。当中断到来时,CPU会暂停当前执行的用户态代码,切换到内核态来处理中断。中断机制允许内核在适当的时候介入用户态程序的执行,处理一些紧急或特殊的情况。
信号(Signal):内核通过信号通知用户态进程发生了某些事件,用户态程序可以注册信号处理函数来响应特定的信号事件。例如,SIGTERM和SIGINT等信号就是用于通知进程终止或中断的信号。
共享内存(Share Memory):允许多个进程在它们的地址空间中共享一块内存区域,从而实现用户态和内核态之间的高效通信。这种方式避免了频繁的用户态和内核态切换,但也需要处理数据同步和一致性的问题。
IOCTL:这是内核较早的一种用户态和内核态的交互方式。用户态程序通过命令的方式调用ioctl函数,然后内核态分发到对应驱动处理,最后将处理结果返回到用户态。
Netlink:本质上是一种特殊的socket,用于内核与多种用户进程之间的消息传递系统。netlink支持内核与用户态之间的双向通信,是一种全双工通信方式。
procfs/sysfs:在Linux中,procfs和sysfs是特殊的文件系统,用于内核与用户空间之间的信息交互。procfs提供了内核和进程的各种信息,而sysfs则提供了设备和驱动的信息。用户态程序可以通过读取这些文件系统中的文件来获取内核信息,也可以通过写入特定的文件来配置内核或驱动。
每种通信方式都有其特定的应用场景和优缺点,开发者需要根据具体需求选择适合的通信方式。

实现用户态和内核态之间的通信通常涉及编写内核模块(在内核态运行)以及用户空间程序(在用户态运行)。我将提供一个简单的例子,展示如何编写一个内核模块,它提供一个系统调用接口,以及一个用户空间程序来调用这个系统调用。

系统调用(System Call)

首先,我们需要编写一个内核模块,它注册一个新的系统调用。这个系统调用将简单地返回传递给它的参数值。

// syscall_example.c - 内核模块源代码  
#include <linux/kernel.h>  
#include <linux/syscalls.h>  
#include <linux/unistd.h>  
  
// 定义新的系统调用号(这通常是自动分配的,但在这个例子中为了简单起见我们手动定义)  
#define __NR_my_syscall 333  
  
// 定义系统调用的原型  
SYSCALL_DEFINE1(my_syscall, int, arg)  
{  
    printk(KERN_INFO "my_syscall called with arg %d\n", arg);  
    return arg;  
}  
  
// 初始化模块  
static int __init syscall_example_init(void)  
{  
    // 注册系统调用  
    if (syscall_register(__NR_my_syscall, (sys_call_ptr_t)my_syscall) < 0) {  
        printk(KERN_ALERT "Failed to register syscall!\n");  
        return -1;  
    }  
    printk(KERN_INFO "syscall_example module loaded\n");  
    return 0;  
}  
  
// 清理模块  
static void __exit syscall_example_exit(void)  
{  
    // 注销系统调用(这通常不是必需的,因为模块卸载时系统会自动清理)  
    syscall_unregister( );  
    printk(KERN_INFO "syscall_example module unloaded\n");  
}  
  
module_init(syscall_example_init);  
module_exit(syscall_example_exit);  
  
MODULE_LICENSE("GPL");

编译这个内核模块需要Makefile文件:

# Makefile - 用于编译内核模块的Makefile  
obj-m += syscall_example.o  
  
all:  
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules  
  
clean:  
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

在编译并加载这个模块后,新的系统调用就可以通过__NR_my_syscall号来调用了。

接下来,我们编写一个用户空间程序来调用这个新的系统调用。

// user_program.c - 用户空间程序源代码  
#include <unistd.h>  
#include <stdio.h>  
#include <sys/syscall.h>  
  
// 定义新的系统调用号,这个值必须与内核模块中定义的值相匹配  
#define __NR_my_syscall 333  
  
int main() {  
    int arg = 42; // 要传递给系统调用的参数  
    long result;  
  
    // 使用syscall函数来调用系统调用  
    result = syscall(__NR_my_syscall, arg);  
    if (result >= 0) {  
        printf("my_syscall returned: %ld\n", result);  
    } else {  
        perror("my_syscall failed");  
    }  
  
    return 0;  
}

编译并运行用户空间程序:

gcc -o user_program user_program.c  
sudo insmod syscall_example.ko  # 加载内核模块  
./user_program                  # 运行用户空间程序  
sudo rmmod syscall_example     # 卸载内核模块(当不再需要时)

请注意,加载和卸载内核模块通常需要root权限。此外,由于内核模块和系统调用的开发涉及到对内核的深入理解和潜在的风险,因此在实际应用中应该非常小心,并确保遵循正确的开发和测试流程。此外,系统调用号的分配通常由内核管理,因此在真实环境中,你不会手动分配系统调用号,而是使用内核提供的机制来注册你的系统调用。

中断(Interrupts)

这个示例,提供一个简单的C语言实现,用于在用户态和内核态之间通信,并使用中断来实现。这个示例将使用字符设备驱动程序和ioctl系统调用。请注意,这个示例仅用于演示目的,实际应用可能需要更多的错误处理和功能。

内核态(字符设备驱动程序)

首先,我们需要创建一个简单的字符设备驱动程序。这个驱动程序将实现ioctl系统调用,以便用户态程序可以与内核态通信。

#include<linux/init.h>
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/cdev.h>
#include<linux/ioctl.h>
#include<linux/uaccess.h>

#define DEVICE_NAME "my_device"
#define DEVICE_MAJOR 240
#define DEVICE_MINOR 0

static int my_device_open(struct inode *inode, struct file *file) {
    return 0;
}

static int my_device_release(struct inode *inode, struct file *file) {
    return 0;
}

static long my_device_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
    int data;

    switch (cmd) {
        case 1: // 读取数据
            data = 42; // 示例数据
            if (copy_to_user((void *)arg, &data, sizeof(int))) {
                return -EFAULT;
            }
            break;
        default:
            return -ENOTTY;
    }

    return 0;
}

static struct file_operations my_device_fops = {
    .owner = THIS_MODULE,
    .open = my_device_open,
    .release = my_device_release,
    .unlocked_ioctl = my_device_ioctl,
};

static struct cdev my_device_cdev;

static int __init my_device_init(void) {
    int ret;
    dev_t devno = MKDEV(DEVICE_MAJOR, DEVICE_MINOR);

    ret = register_chrdev_region(devno, 1, DEVICE_NAME);
    if (ret < 0) {
        printk(KERN_ERR "Failed to register device region\n");
        return ret;
    }

    cdev_init(&my_device_cdev, &my_device_fops);
    my_device_cdev.owner = THIS_MODULE;

    ret = cdev_add(&my_device_cdev, devno, 1);
    if (ret < 0) {
        printk(KERN_ERR "Failed to add device\n");
        unregister_chrdev_region(devno, 1);
        return ret;
    }

    printk(KERN_INFO "My device initialized\n");
    return 0;
}

static void __exit my_device_exit(void) {
    dev_t devno = MKDEV(DEVICE_MAJOR, DEVICE_MINOR);

    cdev_del(&my_device_cdev);
    unregister_chrdev_region(devno, 1);

    printk(KERN_INFO "My device exited\n");
}

module_init(my_device_init);
module_exit(my_device_exit);
MODULE_LICENSE("GPL");

用户态(应用程序)

接下来,我们需要创建一个简单的用户态应用程序,用于与内核态通信。这个应用程序将使用ioctl系统调用来读取数据。

#include<stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>

#define DEVICE_NAME "/dev/my_device"

int main() {
    int fd, data;

    fd = open(DEVICE_NAME, O_RDWR);
    if (fd < 0) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    if (ioctl(fd, 1, (unsigned long)&data) < 0) {
        perror("ioctl");
        exit(EXIT_FAILURE);
    }

    printf("Data from kernel: %d\n", data);

    close(fd);

    return 0;
}

这个示例,我们使用了ioctl系统调用来实现用户态和内核态之间的通信。用户态应用程序通过ioctl系统调用发送命令(在这个例子中是1),内核态驱动程序根据命令执行相应的操作并返回数据。

信号(Signal)

这个示例,提供一个简单的C语言实现,用于在用户态和内核态之间通信,并使用信号来实现。这个示例将使用字符设备驱动程序和sigaction系统调用。内核态(字符设备驱动程序)

首先,我们需要创建一个简单的字符设备驱动程序。这个驱动程序将实现信号处理,以便用户态程序可以与内核态通信。

#include<linux/init.h>
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/cdev.h>
#include<linux/uaccess.h>
#include<linux/signal.h>

#define DEVICE_NAME "my_device"
#define DEVICE_MAJOR 240
#define DEVICE_MINOR 0

static int my_device_open(struct inode *inode, struct file *file) {
    return 0;
}

static int my_device_release(struct inode *inode, struct file *file) {
    return 0;
}

static ssize_t my_device_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos) {
    int pid;

    if (copy_from_user(&pid, buf, sizeof(int))) {
        return -EFAULT;
    }

    struct siginfo info;
    memset(&info, 0, sizeof(struct siginfo));
    info.si_signo = SIGIO;
    info.si_code = SI_QUEUE;
    info.si_int = 42; // 示例数据

    send_sig_info(SIGIO, &info, current);

    return count;
}

static struct file_operations my_device_fops = {
    .owner = THIS_MODULE,
    .open = my_device_open,
    .release = my_device_release,
    .write = my_device_write,
};

static struct cdev my_device_cdev;

static int __init my_device_init(void) {
    int ret;
    dev_t devno = MKDEV(DEVICE_MAJOR, DEVICE_MINOR);

    ret = register_chrdev_region(devno, 1, DEVICE_NAME);
    if (ret < 0) {
        printk(KERN_ERR "Failed to register device region\n");
        return ret;
    }

    cdev_init(&my_device_cdev, &my_device_fops);
    my_device_cdev.owner = THIS_MODULE;

    ret = cdev_add(&my_device_cdev, devno, 1);
    if (ret < 0) {
        printk(KERN_ERR "Failed to add device\n");
        unregister_chrdev_region(devno, 1);
        return ret;
    }

    printk(KERN_INFO "My device initialized\n");
    return 0;
}

static void __exit my_device_exit(void) {
    dev_t devno = MKDEV(DEVICE_MAJOR, DEVICE_MINOR);

    cdev_del(&my_device_cdev);
    unregister_chrdev_region(devno, 1);

    printk(KERN_INFO "My device exited\n");
}

module_init(my_device_init);
module_exit(my_device_exit);
MODULE_LICENSE("GPL");

用户态(应用程序)

接下来,我们需要创建一个简单的用户态应用程序,用于与内核态通信。这个应用程序将使用信号处理来接收来自内核态的信号。

#include<stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include<signal.h>

#define DEVICE_NAME "/dev/my_device"

void signal_handler(int sig, siginfo_t *info, void *ucontext) {
    if (sig == SIGIO) {
        printf("Data from kernel: %d\n", info->si_int);
    }
}

int main() {
    int fd, pid;
    struct sigaction sa;

    memset(&sa, 0, sizeof(sa));
    sa.sa_sigaction = signal_handler;
    sa.sa_flags = SA_SIGINFO;

    if (sigaction(SIGIO, &sa, NULL) < 0) {
        perror("sigaction");
        exit(EXIT_FAILURE);
    }

    pid = getpid();

    fd = open(DEVICE_NAME, O_WRONLY);
    if (fd < 0) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    if (write(fd, &pid, sizeof(int)) < 0) {
        perror("write");
        exit(EXIT_FAILURE);
    }

    close(fd);

    while (1) {
        sleep(1);
    }

    return 0;
}

这个示例,我们使用了sigaction系统调用来实现信号处理。用户态应用程序通过sigaction系统调用注册信号处理函数,当接收到信号时,信号处理函数将被调用。内核态驱动程序通过send_sig_info函数发送信号,用户态应用程序接收到信号后,信号处理函数将打印数据。

共享内存(Share Memory)

这个示例,提供一个简单的C语言实现,用于在用户态和内核态之间通信,并使用共享内存来实现。这个示例将使用字符设备驱动程序和mmap系统调用。

内核态(字符设备驱动程序)

首先,我们需要创建一个简单的字符设备驱动程序。这个驱动程序将实现共享内存的映射,以便用户态程序可以与内核态通信。

#include<linux/init.h>
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/cdev.h>
#include<linux/mm.h>
#include<linux/slab.h>

#define DEVICE_NAME "my_device"
#define DEVICE_MAJOR 240
#define DEVICE_MINOR 0

static int my_device_open(struct inode *inode, struct file *file) {
    return 0;
}

static int my_device_release(struct inode *inode, struct file *file) {
    return 0;
}

static int my_device_mmap(struct file *file, struct vm_area_struct *vma) {
    unsigned long size = vma->vm_end - vma->vm_start;
    void *data = kmalloc(size, GFP_KERNEL);

    if (!data) {
        return -ENOMEM;
    }

    if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(data) >> PAGE_SHIFT, size, vma->vm_page_prot)) {
        kfree(data);
        return -EAGAIN;
    }

    file->private_data = data;
    return 0;
}

static struct file_operations my_device_fops = {
    .owner = THIS_MODULE,
    .open = my_device_open,
    .release = my_device_release,
    .mmap = my_device_mmap,
};

static struct cdev my_device_cdev;

static int __init my_device_init(void) {
    int ret;
    dev_t devno = MKDEV(DEVICE_MAJOR, DEVICE_MINOR);

    ret = register_chrdev_region(devno, 1, DEVICE_NAME);
    if (ret < 0) {
        printk(KERN_ERR "Failed to register device region\n");
        return ret;
    }

    cdev_init(&my_device_cdev, &my_device_fops);
    my_device_cdev.owner = THIS_MODULE;

    ret = cdev_add(&my_device_cdev, devno, 1);
    if (ret < 0) {
        printk(KERN_ERR "Failed to add device\n");
        unregister_chrdev_region(devno, 1);
        return ret;
    }

    printk(KERN_INFO "My device initialized\n");
    return 0;
}

static void __exit my_device_exit(void) {
    dev_t devno = MKDEV(DEVICE_MAJOR, DEVICE_MINOR);

    cdev_del(&my_device_cdev);
    unregister_chrdev_region(devno, 1);

    printk(KERN_INFO "My device exited\n");
}

module_init(my_device_init);
module_exit(my_device_exit);
MODULE_LICENSE("GPL");

用户态(应用程序)

接下来,我们需要创建一个简单的用户态应用程序,用于与内核态通信。这个应用程序将使用mmap系统调用来映射共享内存,并在共享内存中存储和读取数据。

#include<stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>

#define DEVICE_NAME "/dev/my_device"

int main() {
    int fd;
    void *data;
    size_t size = sysconf(_SC_PAGESIZE);

    fd = open(DEVICE_NAME, O_RDWR);
    if (fd < 0) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (data == MAP_FAILED) {
        perror("mmap");
        exit(EXIT_FAILURE);
    }

    // 在共享内存中存储数据
    *(int *)data = 42;

    // 在共享内存中读取数据
    printf("Data from kernel: %d\n", *(int *)data);

    munmap(data, size);
    close(fd);

    return 0;
}

这个示例,我们使用了mmap系统调用来实现共享内存。用户态应用程序通过mmap系统调用映射共享内存,并在共享内存中存储和读取数据。内核态驱动程序通过remap_pfn_range函数将物理内存映射到用户空间。

IOCTL

这个示例,提供一个简单的C语言实现,用于在用户态和内核态之间通信,并使用ioctl系统调用。这个示例将使用字符设备驱动程序和ioctl系统调用。

内核态(字符设备驱动程序)

首先,我们需要创建一个简单的字符设备驱动程序。这个驱动程序将实现ioctl系统调用,以便用户态程序可以与内核态通信。

#include<linux/init.h>
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/cdev.h>
#include<linux/uaccess.h>
#include<linux/ioctl.h>

#define DEVICE_NAME "my_device"
#define DEVICE_MAJOR 240
#define DEVICE_MINOR 0

#define IOCTL_CMD_GET_DATA _IOR('d', 1, int)
#define IOCTL_CMD_SET_DATA _IOW('d', 2, int)

static int my_device_open(struct inode *inode, struct file *file) {
    return 0;
}

static int my_device_release(struct inode *inode, struct file *file) {
    return 0;
}

static long my_device_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
    int data;

    switch (cmd) {
        case IOCTL_CMD_GET_DATA:
            data = 42; // 示例数据
            if (copy_to_user((void *)arg, &data, sizeof(int))) {
                return -EFAULT;
            }
            break;
        case IOCTL_CMD_SET_DATA:
            if (copy_from_user(&data, (void *)arg, sizeof(int))) {
                return -EFAULT;
            }
            // 示例数据处理
            break;
        default:
            return -ENOTTY;
    }

    return 0;
}

static struct file_operations my_device_fops = {
    .owner = THIS_MODULE,
    .open = my_device_open,
    .release = my_device_release,
    .unlocked_ioctl = my_device_ioctl,
};

static struct cdev my_device_cdev;

static int __init my_device_init(void) {
    int ret;
    dev_t devno = MKDEV(DEVICE_MAJOR, DEVICE_MINOR);

    ret = register_chrdev_region(devno, 1, DEVICE_NAME);
    if (ret < 0) {
        printk(KERN_ERR "Failed to register device region\n");
        return ret;
    }

    cdev_init(&my_device_cdev, &my_device_fops);
    my_device_cdev.owner = THIS_MODULE;

    ret = cdev_add(&my_device_cdev, devno, 1);
    if (ret < 0) {
        printk(KERN_ERR "Failed to add device\n");
        unregister_chrdev_region(devno, 1);
        return ret;
    }

    printk(KERN_INFO "My device initialized\n");
    return 0;
}

static void __exit my_device_exit(void) {
    dev_t devno = MKDEV(DEVICE_MAJOR, DEVICE_MINOR);

    cdev_del(&my_device_cdev);
    unregister_chrdev_region(devno, 1);

    printk(KERN_INFO "My device exited\n");
}

module_init(my_device_init);
module_exit(my_device_exit);
MODULE_LICENSE("GPL");

用户态(应用程序)

接下来,我们需要创建一个简单的用户态应用程序,用于与内核态通信。这个应用程序将使用ioctl系统调用来读取数据。

#include<stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>

#define DEVICE_NAME "/dev/my_device"
#define IOCTL_CMD_GET_DATA _IOR('d', 1, int)
#define IOCTL_CMD_SET_DATA _IOW('d', 2, int)

int main() {
    int fd, data;

    fd = open(DEVICE_NAME, O_RDWR);
    if (fd < 0) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    if (ioctl(fd, IOCTL_CMD_GET_DATA, (unsigned long)&data) < 0) {
        perror("ioctl");
        exit(EXIT_FAILURE);
    }

    printf("Data from kernel: %d\n", data);

    close(fd);

    return 0;
}

这个示例,我们使用了ioctl系统调用来实现用户态和内核态之间的通信。用户态应用程序通过ioctl系统调用发送命令(在这个例子中是IOCTL_CMD_GET_DATA),内核态驱动程序根据命令执行相应的操作并返回数据。

这个示例,提供一个简单的C语言实现,用于在用户态和内核态之间通信,并使用netlink来实现。这个示例将使用内核模块和用户空间应用程序。

内核态(内核模块)

首先,我们需要创建一个简单的内核模块。这个模块将实现一个简单的netlink套接字,用于与用户空间通信。

#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/netlink.h>
#include<linux/skbuff.h>
#include <net/sock.h>

#define NETLINK_USER 31

static void netlink_recv_msg(struct sk_buff *skb) {
    struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data;
    int data = 42; // 示例数据

    if (nlh->nlmsg_type == NLMSG_NOOP) {
        struct sk_buff *skb_out;
        struct nlmsghdr *nlh_out;
        int *data_out;

        skb_out = nlmsg_new(NLMSG_SPACE(sizeof(int)), GFP_ATOMIC);
        if (!skb_out) {
            printk(KERN_ERR "Failed to allocate new skb\n");
            return;
        }

        nlh_out = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, sizeof(int));
        if (!nlh_out) {
            printk(KERN_ERR "Failed to put new message\n");
            kfree_skb(skb_out);
            return;
        }

        data_out = nlmsg_data(nlh_out);
        *data_out = data;

        netlink_unicast(skb_out, nlh->nlmsg_pid, MSG_DONTWAIT);
    }
}

static int __init netlink_init(void) {
    struct netlink_kernel_cfg cfg = {
        .input = netlink_recv_msg,
    };

    struct sock *sock = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);
    if (!sock) {
        printk(KERN_ERR "Failed to create netlink socket\n");
        return -ENOMEM;
    }

    printk(KERN_INFO "Netlink initialized\n");
    return 0;
}

static void __exit netlink_exit(void) {
    netlink_kernel_release(netlink_kernel_create(&init_net, NETLINK_USER, NULL));
    printk(KERN_INFO "Netlink exited\n");
}

module_init(netlink_init);
module_exit(netlink_exit);
MODULE_LICENSE("GPL");

用户态(应用程序)

接下来,我们需要创建一个简单的用户态应用程序,用于与内核态通信。这个应用程序将使用netlink套接字来读取数据。

#include<stdio.h>
#include <stdlib.h>
#include<string.h>
#include <unistd.h>
#include <sys/socket.h>
#include<linux/netlink.h>

#define NETLINK_USER 31

int main() {
    int sockfd, n;
    struct sockaddr_nl src_addr, dest_addr;
    struct msghdr msg;
    struct iovec iov;
    struct nlmsghdr *nlh;
    int data;
    char buffer[NLMSG_SPACE(sizeof(int))];

    sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_USER);
    if (sockfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    memset(&src_addr, 0, sizeof(src_addr));
    src_addr.nl_family = AF_NETLINK;
    src_addr.nl_pid = getpid();
    src_addr.nl_groups = 0;

    if (bind(sockfd, (struct sockaddr *)&src_addr, sizeof(src_addr)) < 0) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    memset(&dest_addr, 0, sizeof(dest_addr));
    dest_addr.nl_family = AF_NETLINK;
    dest_addr.nl_pid = 0; // Kernel
    dest_addr.nl_groups = 0;

    nlh = (struct nlmsghdr *)buffer;
    nlh->nlmsg_len = NLMSG_SPACE(sizeof(int));
    nlh->nlmsg_type = NLMSG_NOOP;
    nlh->nlmsg_flags = 0;
    nlh->nlmsg_seq = 0;
    nlh->nlmsg_pid = src_addr.nl_pid;

    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;
    msg.msg_control = NULL;
    msg.msg_controllen = 0;
    msg.msg_flags = 0;

    if (sendmsg(sockfd, &msg, 0) < 0) {
        perror("sendmsg");
        exit(EXIT_FAILURE);
    }

    n = recv(sockfd, buffer, sizeof(buffer), 0);
    if (n < 0) {
        perror("recv");
        exit(EXIT_FAILURE);
    }

    data = *(int *)NLMSG_DATA(nlh);
    printf("Data from kernel: %d\n", data);

    close(sockfd);

    return 0;
}

这个示例,我们使用了netlink套接字来实现用户态和内核态之间的通信。用户态应用程序通过netlink套接字发送请求,内核态驱动程序根据请求执行相应的操作并返回数据。

procfs/sysfs

这个示例提供一个简单的C语言实现,用于在用户态和内核态之间通信,并使用procfs来实现读写。这个示例将使用内核模块和用户空间应用程序。

内核态(内核模块)

首先,我们需要创建一个简单的内核模块。这个模块将实现一个简单的procfs文件,用于与用户空间通信。

#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/proc_fs.h>
#include<linux/seq_file.h>

#define PROC_FILE_NAME "my_proc_file"

static int data = 42; // 示例数据

static int my_proc_show(struct seq_file *m, void *v) {
    seq_printf(m, "%d\n", data);
    return 0;
}

static int my_proc_open(struct inode *inode, struct file *file) {
    return single_open(file, my_proc_show, NULL);
}

static ssize_t my_proc_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos) {
    char kbuf[16];
    int value;

    if (count > sizeof(kbuf) - 1) {
        return -EINVAL;
    }

    if (copy_from_user(kbuf, buf, count)) {
        return -EFAULT;
    }

    kbuf[count] = '\0';
    if (sscanf(kbuf, "%d", &value) != 1) {
        return -EINVAL;
    }

    data = value;
    return count;
}

static const struct file_operations my_proc_fops = {
    .owner = THIS_MODULE,
    .open = my_proc_open,
    .read = seq_read,
    .write = my_proc_write,
    .llseek = seq_lseek,
    .release = single_release,
};

static int __init my_proc_init(void) {
    proc_create(PROC_FILE_NAME, 0666, NULL, &my_proc_fops);
    printk(KERN_INFO "My proc file initialized\n");
    return 0;
}

static void __exit my_proc_exit(void) {
    remove_proc_entry(PROC_FILE_NAME, NULL);
    printk(KERN_INFO "My proc file exited\n");
}

module_init(my_proc_init);
module_exit(my_proc_exit);
MODULE_LICENSE("GPL");

用户态(应用程序)

接下来,我们需要创建一个简单的用户态应用程序,用于与内核态通信。这个应用程序将使用procfs文件来读取和写入数据。

#include<stdio.h>
#include <stdlib.h>
#include<string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define PROC_FILE_PATH "/proc/my_proc_file"

int main() {
    int fd, n;
    char buffer[256];

    // 读取数据
    fd = open(PROC_FILE_PATH, O_RDONLY);
    if (fd < 0) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    n = read(fd, buffer, 255);
    if (n < 0) {
        perror("read");
        exit(EXIT_FAILURE);
    }

    buffer[n] = '\0';
    printf("Data from kernel: %s\n", buffer);

    close(fd);

    // 写入数据
    fd = open(PROC_FILE_PATH, O_WRONLY);
    if (fd < 0) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    n = write(fd, "24\n", 3);
    if (n < 0) {
        perror("write");
        exit(EXIT_FAILURE);
    }

    close(fd);

    // 再次读取数据
    fd = open(PROC_FILE_PATH, O_RDONLY);
    if (fd < 0) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    n = read(fd, buffer, 255);
    if (n < 0) {
        perror("read");
        exit(EXIT_FAILURE);
    }

    buffer[n] = '\0';
    printf("Data from kernel: %s\n", buffer);

    close(fd);

    return 0;
}

在这个示例中,我们使用了procfs文件来实现用户态和内核态之间的读写通信。用户态应用程序通过procfs文件读取和写入数据,内核态驱动程序将数据读取到内核空间并写入文件。

标签:return,int,汇总,内核,Linux,device,include,my
From: https://www.cnblogs.com/adam-ma/p/18084237

相关文章

  • Linux——网络通信
    一.引入Linux进程间通信包括多种机制,如管道、消息队列、信号、共享内存和信号量。这些机制都依赖于Linux内核提供的支持,用于实现不同进程之间的数据交换和同步。然而,这些通信方式在本地进程间通信中非常有用,但无法直接用于跨机器间的通信。二.网络通信在网络通信中,通信的......
  • Linux进程通信——信号
    一.信号的名字和编号1.每个信号都有一个编号和名称,这些名字都以“SIG”开头。  (kill-l命令可显示出当前系统支持的信号的编号和名称)二.信号的处理1.信号的处理有三种方法:  忽略,捕捉,默认动作忽略:有俩种信号不可被忽略(SIGKILL和SIGSTOP),其向 内核 和 超......
  • Linux脏牛提权漏洞复现(DirtyCow)
    #简述脏牛(DirtyCow)是Linux中的一个提权漏洞。主要产生的原因是Linux系统的内核中Copy-on-Write(COW)机制产生的竞争条件问题导致,攻击者可以破坏私有只读内存映射,并提升为本地管理员权限。#前期准备靶机:vulnhub——Lampiao192.168.230.217攻击机:Kali192.168.230.128#复现......
  • Flink实战之Flink乱序场景汇总
    目录一数据乱序场景1数据源乱序2ETL造成乱序二Flink处理乱序数据方案1Watermark和EventTime模式2提前创建保序任务3使用事务性Sink保证下游数据时序三结语       在数据处理领域,无论离线批处理领域还是实时流处理领域,数据时序性对于最终数据的......
  • 【Linux】 生产消费者模型
    线程同步同步: 在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,这就叫做同步(饥饿问题:某些线程无法得到资源而长时间无法执行,常见的就是申请不到锁)竞态条件:因为时序问题,而导致程序异常,我们称之为竞态条件。单纯的加锁会引起问题。如......
  • [Linux]文件缓冲区
    文件fd输出重定向除了用dup2()改变数组下标外,还可以用命令来完成所有的命令执行,都必须有操作系统将其运行起来变成进程,然后根据>>,<<来判断是输入重定向,还是输出重定向。缓冲区之所以有缓冲区,是为了提高效率的。就类比快递一样,如果你送一个东西给北京的朋友,那么你自己......
  • Excel/WPS超级处理器,合并单元格汇总3种方式
    在处理职场数据表格,会遇到在合并单元格中汇总求和,计算平均值或统计个数。如何快速被统计汇总呢?接下来,我们就使用超级处理器中的合并单元格汇总菜单来完成这些,鼠标点选即可。超级处理器下载与安装1)合并单元格汇总-求和2)合并单元格汇总-求平均3)合并单元格汇总-计数想......
  • 操作系统实践之路——五、初始化(2.Linux初始化)
    文章目录一、全局流程二、从BIOS到GRUB三、GRUB是如何启动的四、详解vmlinuz文件结构五、流程梳理-1六、内核初始化从_start开始七、流程梳理-2参考资料前言​本章节将讨论一下Linux如何去做初始化。一、全局流程​在机器加电后,BIOS会进行自检,然后由BIOS加载......
  • 从 Linux 内核角度探秘 JDK MappedByteBuffer
    本文涉及到的内核源码版本为:5.4,JVM源码为:OpenJDK17,RocketMQ源码版本为:5.1.1在之前的文章《一步一图带你深入剖析JDKNIOByteBuffer在不同字节序下的设计与实现》中,笔者为大家详细剖析了JDKBuffer的整个设计体系,从总体上来讲,JDKNIO为每一种Java基本类型定义了对......
  • Linux TCP/UDP CS模型
    LinuxTCP/UDPCS模型目录LinuxTCP/UDPCS模型TCPServer/TCPClientUDPServer/UDPClientTCPServer/TCPClient在C语言中实现一个TCP服务器时,使用select函数可以帮助我们同时监控多个文件描述符(包括socket)的状态,从而实现非阻塞的I/O操作。以下是一个简单的TCP服务器示例,它......