ubuntu下一个头文件引发的mmap段错误(Segfault)

最近要开发一个项目,用到了内存映射mmap机制,于是自己在Ubuntu下先写了一个测试的小程序,由于测试代码是从网上摘抄更改的,出现了一个头文件的问题,这两天搞的自己心情相当的郁闷,都已经开始严重怀疑这个世界了,当得知是头文件出错时,心里更是五味杂瓶,感觉生活欺骗了自己一样,代码如下:

驱动代码:

/*
 *  Maxim (Dallas) MAX3107/8/9, MAX14830 serial driver
 *
 *  Copyright (C) 2012-2014 Alexander Shiyan <shc_work@mail.ru>
 *
 *  Based on max3100.c, by Christian Pellegrin <chripell@evolware.org>
 *  Based on max3110.c, by Feng Tang <feng.tang@intel.com>
 *  Based on max3107.c, by Aavamobile
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 */
#include <linux/kernel.h>
#include <linux/notifier.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/spinlock.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <asm/pgtable_types.h>
#include <linux/mm.h>
#include "pic.h"

#define SD16A_NAME			"sd16a"
#define SD16A_MAJOR			204
#define SD16A_MINOR			0
//#define DATA_LEN                 259200
#define DATA_LEN            259200

uint8_t data[DATA_LEN];
static unsigned long buf_size;
static int sd16a_major = SD16A_MAJOR;
static int sd16a_minor = SD16A_MINOR;
struct sd16a_devtype {
	char	name[9];
	int	(*detect)(struct device *);
	void (*power)(int);
};

uint8_t * sd16a_pic_data_buf;

struct sd16a_dev{
    struct spi_device * spi;
    int irq_gpio;
    int sda_gpio;
    spinlock_t buf_lock;
    u8 * data_buf;
    struct spi_master *master;
    u8 irq_cnt;
};

static const struct sd16a_devtype sd16a_devtype = {
    .name = "SD16A",
};
static const struct spi_device_id sd16a_id_table[] = {
    {"dm,sd16a",(kernel_ulong_t)&sd16a_devtype,},
    {},
};
static const struct of_device_id __maybe_unused sd16a_dt_ids[] = {
    {.compatible = "dm,sd16a",    .data = &sd16a_devtype, },
    {},
};

static int sd16a_open(struct inode *inode, struct file *filp){
    printk("open sd16a start\n");
	sd16a_pic_data_buf = (uint8_t *)kmalloc(DATA_LEN, GFP_KERNEL); //内核申请内存只能按页申请,申请该内存以便后面把它当作虚拟设备  
    printk("sd16a_pic_data_buf is %p\n",sd16a_pic_data_buf);
    if(sd16a_pic_data_buf){
        printk("sd16a mmap addr is %p\n",sd16a_pic_data_buf);
        memset(sd16a_pic_data_buf,0,buf_size);
    }
    printk("open sd16a end\n");
    return 0;
}

static size_t sd16a_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos){
    //u8   ret;
    uint32_t i = 0;
    uint32_t len = count;
    printk("sd16a_read start\n");
    memset(data,10,DATA_LEN - 1);
    data[DATA_LEN - 1] = 0;
    //pic_buf[i][0] = i;
    memcpy(sd16a_pic_data_buf,data,DATA_LEN);
    printk("sd16a_read success\n");
    printk("sd16a_pic_data_buf is");
    for(i = 0;i < len;i ++){
        printk("%d",sd16a_pic_data_buf[i]);
    }
    return 0;
}

static long sd16a_ioctl(struct file * file, unsigned int cmd, unsigned long arg){

    return 0;
}

static int sd16a_release(struct inode *inode, struct file *filp){
    unsigned long size = buf_size;
    //unsigned long addr = (unsigned long)sd16a_pic_data_buf;
	printk("sd16a close\n");
    kfree(sd16a_pic_data_buf);
	//free_pages(addr,get_order(size));
    return 0;
}

#if 0
static int sd16a_llseek(struct file *file, loff_t offset, int whence){

}
#endif

static int sd16a_mmap(struct file *file, struct vm_area_struct *vma) {
	unsigned int size = 0;
    uint8_t * kmalloc_addr = NULL;
    uint32_t i = 0;
    unsigned int alloc_size = 0;
    unsigned int page_res_size = 0;

	size =  vma->vm_end - vma->vm_start;
    alloc_size = size + 2 * PAGE_SIZE;
    page_res_size = (size + PAGE_SIZE - 1) & PAGE_MASK;
	kmalloc_addr = (uint8_t *)kmalloc(alloc_size, GFP_KERNEL);
	sd16a_pic_data_buf = (uint8_t *)(((unsigned long)kmalloc_addr + PAGE_SIZE - 1) & PAGE_MASK);
    for(i = 0;i < (page_res_size / PAGE_SIZE);i += PAGE_SIZE){
        SetPageReserved(virt_to_page(((unsigned long)sd16a_pic_data_buf) + i));
    }
	if(sd16a_pic_data_buf == NULL){
		pr_err("kmalloc failed\n");
		return -1;
	}
	 if(remap_pfn_range(vma,
                 vma->vm_start,
                 virt_to_phys((void *)sd16a_pic_data_buf)>>PAGE_SHIFT,
                 size,
                 vma->vm_page_prot))
    {  
		pr_err("remap_pfn_range failed\n");
		return -EAGAIN;  
    } 
    return 0;  
}
static struct file_operations sd16a_ops = {
    .owner      = THIS_MODULE,
    .read    = sd16a_read,
    //.write   = sd16a_write,
    .mmap  = sd16a_mmap,
    .open    = sd16a_open,
    .unlocked_ioctl = sd16a_ioctl,
    .release    = sd16a_release,
    .llseek     = noop_llseek,
};

static struct cdev sd16a_cdev;
static struct class * sd16a_class; 
static int sd16a_spi_probe(struct spi_device *spi){
    //struct sd16a_devtype * devtype;
    int ret;
    struct device * dev;
    dev_t devid = MKDEV(sd16a_major,sd16a_minor);

    dev = &spi->dev;
    if(!dev){
        pr_err("in file :%s function: %s, dev is null\n",__FILE__,__func__);
        return -EINVAL;
    }
    printk("enter %s \n",__func__);

    if (sd16a_major) {
        devid = MKDEV(sd16a_major, sd16a_minor);
        ret = register_chrdev_region(devid, 1, "dm_sd16a");
    }else{
        ret = alloc_chrdev_region(&devid, 0, 1, "dm_sd16a");
        sd16a_major = MAJOR(devid);
    }
    if(ret < 0){
        dev_err(dev, "dm_sd16a chrdev_region err: %d\n", ret);
        goto out;
    }
    cdev_init(&sd16a_cdev, &sd16a_ops);
    cdev_add(&sd16a_cdev, devid, 1);
    sd16a_class = class_create(THIS_MODULE, "sd16a-dev");
    if (IS_ERR(sd16a_class)) {
        ret = PTR_ERR(sd16a_class);
        goto out_unreg_chrdev;
    }
    device_create(sd16a_class,dev,devid,NULL,"%s",SD16A_NAME);
    printk("sda16a probe success\n");
    return 0;


out_unreg_chrdev:

//error:

//err_register_chrdev:

out:
    return ret;

}
static int sd16a_spi_remove(struct spi_device *spi)
{
    ;
    /*
     *should remove irq and clk , all the alooced sources
     */
    return 0;
}
/*
static struct spi_driver sd16a_uart_driver = {
	.driver = {
		.name		= SD16A_NAME,
		.owner		= THIS_MODULE,
		.of_match_table	= of_match_ptr(sd16a_dt_ids),
	},
	.probe		= sd16a_spi_probe,
	.remove		= sd16a_spi_remove,
	.id_table	= sd16a_id_table,
};*/
static int __init sd16a_uart_driver_init(void) 
{ 
    int ret;
    dev_t devid = MKDEV(sd16a_major,sd16a_minor);

    if (sd16a_major) {
        devid = MKDEV(sd16a_major, sd16a_minor);
        ret = register_chrdev_region(devid, 1, "dm_sd16a");
    }else{
        ret = alloc_chrdev_region(&devid, 0, 1, "dm_sd16a");
        sd16a_major = MAJOR(devid);
    }
    cdev_init(&sd16a_cdev, &sd16a_ops);
    cdev_add(&sd16a_cdev, devid, 1);
    sd16a_class = class_create(THIS_MODULE, "sd16a-dev");
    if (IS_ERR(sd16a_class)) {
        ret = PTR_ERR(sd16a_class);
    }
    device_create(sd16a_class,NULL,devid,NULL,"%s",SD16A_NAME);
    printk("sda16a probe success\n");
    return 0;
} 
module_init(sd16a_uart_driver_init); 
static void __exit sd16a_uart_driver_exit(void) 
{ 
    device_destroy(sd16a_class,MKDEV(SD16A_MAJOR,SD16A_MINOR));
    class_destroy(sd16a_class);
    cdev_del(&sd16a_cdev);
    unregister_chrdev_region(MKDEV(SD16A_MAJOR,SD16A_MINOR),1);
} 
module_exit(sd16a_uart_driver_exit);
//module_spi_driver(sd16a_uart_driver);
//#endif

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Const jxh");
MODULE_DESCRIPTION("sd16a serial driver");

测试代码:

#include<stdio.h>
#include<string.h>
#include<linux/mman.h>
#include<fcntl.h>
#include<unistd.h>
#define LEN 262144 //259400
//#define LEN 20000  //262144
#define MS(n) (n*1000)

#define CHR_DEV_NAME "/dev/sd16a"

int main()
{
    int ret;
    int i = 0;
    int j = 0;
    char buf[LEN];
    unsigned char * mmp_addr;
    char * buf_p;
    int fd = open(CHR_DEV_NAME,O_RDWR | O_NONBLOCK);
    if(fd < 0)
    {
        printf("open file %s failed!\n",CHR_DEV_NAME);
        return -1;
    }
    printf("fd is %d\n",fd);
    mmp_addr = (unsigned char*)mmap(NULL,LEN,PROT_READ|PROT_WRITE,MAP_LOCKED|MAP_SHARED,fd,0);
    if(mmp_addr == NULL){
        printf("mmap failed\n");
        return -1;
    }
    printf("mmp_addr is %p\n",mmp_addr);
    for(i = 0;i < 10;i ++){
        read(fd,buf,32);
//        memcpy(mmp_addr,"hello world",sizeof("hello world"));
        for(j = 0;j < 32;j ++){
            printf("%d",mmp_addr[j]);
        }
        printf("\n");
        usleep(MS(10));
    }

    close(fd);

    munmap(mmp_addr,LEN);
    return 0;
}



原本是一个很简单的程序,但是搞了两天才确定下来问题,真是头大。其实引起错误的原因很简单,就是头文件引用不对,虽然编译的时候没有报错,只是弹出了警告,但是执行的时候却会出现段错误。

编译时弹出的警告:

运行时的段错误提示:

看到段错误的原因是4,根据段错误码如下图

段错误码:

bit2为1,代表的是用户访问越界造成的,可是应用程序那么简单,也没看到数组访问错误的地方,于是怀疑是自己驱动mmap函数哪里设置不对,就各种改驱动代码,结果这样调了一天也没改观,于是只能从其它地方着手找问题了,用man mmap查看了一下,发现其头文件是在sys/mman.h,而不是linux/mman.h,由于这个测试代码是自己从别的地方拷贝更改的,也不知道为何别人用的linux/mman.h头文件。于是抱着试试看的心态把头文件目录改成sys/mman.h,奇迹发生了,代码终于运行正常了,就这样一个头文件问题,搞了两天,真是服了。顺便吐槽一下,既然头文件包含错误为何编译的时候只是警告,而不是错误呢?我也跟进去看了一下,发现linux/mman.h根本也没有关于mmap函数的声明,也是服了。看来以后关于使用的函数头文件还是需要谨慎啊。

正确的执行结果如下:

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值