Linux系统编程---用户空间与内核空间

一、基本概念

        Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数据可能不在内存中。用户空间的内存映射采用段页式,而内核空间有自己的规则。

(1)Linux内核地址空间划分

        通常32位Linux内核虚拟地址空间划分0~3G为用户空间,3~4G为内核空间(注意,内核可以使用的线性地址只有1G)。注意这里是32位内核地址空间划分,64位内核地址空间划分是不同的。

(2)为什么这么划分?

  • 处理器模式不同,权限不同:

        对于X86体系的cpu,用户空间代码运行在Ring3横式,内核空间代码运行Ring0模式,对于arm体系的cpu,用户空间代码运行在usr横式,内核空间代码运行在svc模式;

  • 安全考虑:

        整个系统中有各种资源,比如计箅资源、内存资源和外设资源,而linux是多用户、多进程系统,所以,这些资源必须在受限的、被管理的状态下使用,要不然就陷入了混乱,空间隔离可以保证即便是单个应用程序出现错误也不会影响到操作系统的稳定性。

  • 从软件设计思想来看,解除了核心代码和业务逻辑代码的耦合:

        内核代码偏重于系统管理;而用户空间代码(也即应用程序)偏重于业务逻辑代码的实现,两者分工不同,隔离也是解耦合。

二、用户空间和内核空间数据传递 

用户空间的应用程序,在业务实现时:
(1)比如你只实现个printf("hello world");,并没有和内核进行交互,那么就不会存在两个空间的数据拷贝;
(2)但是实际上,很多的业务实现都要与内核进行交互,各种上网,操作一些字符、块设备,调用open()、write()、read()、ioctl()等等函数,就要用到系统调用。这些都会触发一个中断,并通过中断的方式陷入到内核去执行,但用到的还是用户态进程,然后将进程需要的数据返回到用户空间。

 

        为了解决用户空间和内核空间数据传递的问题,在Linux内核中提供了 2个接口用来实现:

/* 从用户空间拷贝到内核空间 */
static inline long copy_from_user(void *to,
		const void __user * from, unsigned long n)
{
	might_sleep();
	if (access_ok(VERIFY_READ, from, n))
		return __copy_from_user(to, from, n);
	else
		return n;
}

/* 从内核空间拷贝到用户空间 */
static inline long copy_to_user(void __user *to,
		const void *from, unsigned long n)
{
	might_sleep();
	if (access_ok(VERIFY_WRITE, to, n))
		return __copy_to_user(to, from, n);
	else
		return n;
}

        下面我们编译一个misc_driver.c驱动程序和misc_test.c应用程序,来验证用户空间和内核空间的数据传递。

        (1)misc_driver驱动程序:

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

char kernel_buf[1024] = {0};

static ssize_t misc_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff_t)
{
    if (copy_to_user(ubuf, kernel_buf, sizeof(kernel_buf)) != 0) {
        printk("misc read error!\n");
        return -1;
    }

    printk("misc read success!\n");

    return 0;
}

static ssize_t misc_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{
    if (copy_from_user(kernel_buf, ubuf, size) != 0) {
        printk("misc write error!\n");
        return -1;
    }

    printk("misc write success!\n");

    return 0;
}

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

struct file_operations misc_fops = {
    .owner = THIS_MODULE,
    .read = misc_read,
    .write = misc_write,
    .release = misc_release
};

struct miscdevice misc_dev = {
	.minor = MISC_DYNAMIC_MINOR,        // 动态分配子设备号
	.name = "misc_driver",
	.fops = &misc_fops
};


static int misc_init(void)
{
    int ret;

    ret = misc_register(&misc_dev);
    if (ret < 0) {
        printk("misc register is error\n");
        return -1;
    } else {
        printk("misc register success\n");
    }

    return 0;
}

static void misc_exit(void)
{
    int ret;

    ret = misc_deregister(&misc_dev);
    if (ret < 0) {
        printk("misc deregister is error\n");
    } else {
        printk("misc deregister success\n");
    }
}

module_init(misc_init);
module_exit(misc_exit);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("zhixiaoxing");

        Makefile文件:

#!/bin/bash

obj-m += misc_driver.o

#Kernel源码目录
KDIR := /home/kernel/iTop4412_Kernel_3.0

PWD ?= $(shell pwd)

#make -C 指定调用执行的路径
all:
	make -C $(KDIR) M=$(PWD) modules

clean:
	rm -rf *.o *order *.mod.c *symvers
	rm -rf ./.tmp_versions

        (2)misc_test应用程序:

        应用程序需要在开发板上运行,所以需要使用开发板的编译工具链编译,这里我们使用静态编译。

        编译指令:arm-none-linux-gnueabi-gcc -o misc_test misc_test.c -static

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

#define DRIVER_PATH "/dev/misc_driver"

int main(int argc, char **argv)
{
    int fd;
    ssize_t size;
    char r_buf[1024] = {0};
    char w_buf[1024] = {0};

    fd = open(DRIVER_PATH, O_RDWR);
    if (fd < 0) {
        printf("file open fail\n");
        exit(EXIT_FAILURE);
    }

    strncpy(w_buf, "hello world!", strlen("hello world!"));
    size = write(fd, w_buf, strlen(w_buf));
    if (size < 0) {
        printf("file write fail\n");
        exit(EXIT_FAILURE);
    }

    size = read(fd, r_buf, sizeof(r_buf));
    if (size < 0) {
        printf("file read fail\n");
        exit(EXIT_FAILURE);
    } else {
        printf("read data: %s\n", r_buf);
    }

    exit(EXIT_SUCCESS);
}

        (3) 运行结果

        上面的驱动程序需要在开发板上安装,可参考我另外一篇博客Linux驱动开发---杂项设备_智小星的博客-CSDN博客,应用程序也是需要使用开发板的编译工具链来编译,并在开发上运行。

root@android:/data/tianyu # ./misc_test                                        
[67370.559988] misc write success!
[67370.561784] misc read success!
read data: hello world!

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
PWM-GPIO驱动程序是Linux内核中的一个驱动模块,用于控制嵌入式系统中的GPIO引脚产生PWM信号。该驱动程序允许开发人员通过编程的方式来控制GPIO引脚的电平变化,从而产生不同占空比的PWM信号。 在Linux内核中,PWM-GPIO驱动程序通过向用户空间提供了相应的接口来实现PWM信号的控制。开发人员可以通过打开相应的设备节点,并使用相应的系统调用函数来设置PWM的频率、占空比等参数,从而实现对GPIO引脚的PWM信号的控制。 驱动程序的核心部分是一个PWM子系统,它与GPIO子系统紧密集成。PWM子系统负责管理PWM信号的生成和控制,而GPIO子系统负责管理GPIO引脚的配置和操作。PWM-GPIO驱动程序在这两个子系统之间起着桥梁的作用。 PWM-GPIO驱动程序的实现方式与硬件平台相关,每个平台可能有不同的具体实现。在驱动程序的初始化过程中,必须先配置GPIO引脚的功能为PWM模式,并将相应的寄存器映射到内核中,以便能够通过对寄存器的操作来控制GPIO引脚。驱动程序还需要初始化PWM子系统,为每个GPIO引脚分配相应的PWM通道,并根据需求设置PWM的频率、占空比等参数。 通过PWM-GPIO驱动程序,开发人员可以方便地利用Linux内核的功能来实现对嵌入式系统中GPIO引脚产生PWM信号的控制。这为开发PWM驱动、控制舵机、LED等应用提供了便捷的方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

智小星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值