ubuntu下一個頭文件引發的mmap段錯誤(Segfault)

最近要開發一個項目,用到了內存映射mmap機制,於是自己在Ubuntu下先寫了一個測試的小程序,由於測試代碼是從網上摘抄更改的,出現了一個頭文件的問題,這兩天搞的自己心情相當的鬱悶,都已經開始嚴重懷疑這個世界了,當得知是頭文件出錯時,心裏更是五味雜瓶,感覺生活欺騙了自己一樣,代碼如下:

驅動代碼:

/*
 *  Maxim (Dallas) MAX3107/8/9, MAX14830 serial driver
 *
 *  Copyright (C) 2012-2014 Alexander Shiyan <[email protected]>
 *
 *  Based on max3100.c, by Christian Pellegrin <[email protected]>
 *  Based on max3110.c, by Feng Tang <[email protected]>
 *  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函數的聲明,也是服了。看來以後關於使用的函數頭文件還是需要謹慎啊。

正確的執行結果如下:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章