调试分析之 imx257中proc下mymsg及myprintk的实现

发布时间:2024-08-13  

一.实现在/proc下面创建文件条目


1.定义proc_dir_entry结构体,已经file_operatioons结构体


1 //定义proc的entry结构体

2 static struct proc_dir_entry *myentry;

4 static struct file_operations proc_mymsg_operations = {

5 };


2.在入口函数中创建proc条目,并且关联file_operations结构体


 1 static int mymsg_init(void)

 2 {

 3     //创建proc的目录

 4     myentry = create_proc_entry('mymsg',S_IRUSR,NULL);  //S_IRUSR:400 只读

 5 

 6     if(myentry)

 7         myentry->proc_fops = &proc_mymsg_operations;

 8     

 9     return 0;

10 }


3.在出口函数中自然就是删除条目咯


1 static void mymsg_exit(void)

2 {

3     remove_proc_entry('mymsg', NULL);

4 }

 

4.编译测试代码,再/proc目录下创建了一个文件mymsg

7e480d720fd506e199d8838e8cb188e0_Et5PLyBNqQj5E8dGcGtZVoQ5WCvUwmn+adTHys6qppXRczV6tuk5OU3T9GWksaC5RXJhf2nzfuxe5wYo4r5axIq3YDXwW7bA4iLncBTTa0x9+v+BJ8FWtcyUCAAAAABJRU5ErkJggg==.png

附上驱动程序mymsg_1.c


 1 #include

 2 #include

 3 #include

 4 #include

 5 #include

 6 #include

 7 #include

 8 #include

 9 

10 //定义proc的entry结构体

11 static struct proc_dir_entry *myentry;

12 

13 static struct file_operations proc_mymsg_operations = {

14 };

15 

16 static int mymsg_init(void)

17 {

18     //创建proc的目录

19     myentry = create_proc_entry('mymsg',S_IRUSR,NULL);    //S_IRUSR:400 只读

20     

21     if(myentry)

22         myentry->proc_fops = &proc_mymsg_operations;

23     

24     return 0;

25 }

26 

27 static void mymsg_exit(void)

28 {

29     remove_proc_entry('mymsg', NULL);

30 }

31 

32 module_init(mymsg_init);

33 module_exit(mymsg_exit);

34 

35 MODULE_LICENSE('GPL');

36 MODULE_AUTHOR('Lover雪儿');


二.实现读写函数


由于上面我们的file_operations结构体为空,所以我们自然就无法对/proc/mymsg进行读取,此处我们增加一个读函数.


1 static ssize_t mymsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos)

2 {

3     printk('mymsg_read n');

4     return 0;

5 }

7 static struct file_operations proc_mymsg_operations = {

8     .read = mymsg_read,

9 };


编译加载完成后,我们使用cat命令对齐进行读取,结果如下所示:说明已经成功的进入了mymsg_read函数中.

4df7f62580e947d22b336a773780a698_h90WZmOMWo6LAAAAABJRU5ErkJggg==.png

附上驱动程序mymsg_2.c


 1 #include

 2 #include

 3 #include

 4 #include

 5 #include

 6 #include

 7 #include

 8 #include

 9 

10 //定义proc的entry结构体

11 static struct proc_dir_entry *myentry;

12 

13 

14 static ssize_t mymsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos)

15 {

16     printk('mymsg_read n');

17     return 0;

18 }

19 

20 

21 static struct file_operations proc_mymsg_operations = {

22     .read = mymsg_read,

23 };

24 

25 static int mymsg_init(void)

26 {

27     //创建proc的目录

28     myentry = create_proc_entry('mymsg',S_IRUSR,NULL);    //S_IRUSR:400 只读

29     

30     if(myentry)

31         myentry->proc_fops = &proc_mymsg_operations;

32     

33     

34     return 0;

35 }

36 

37 static void mymsg_exit(void)

38 {

39     remove_proc_entry('mymsg', NULL);

40 }

41 

42 module_init(mymsg_init);

43 module_exit(mymsg_exit);

44 

45 MODULE_LICENSE('GPL');

46 MODULE_AUTHOR('Lover雪儿');


三.模拟内存数据读取


既然要进行读取,自然就少不了数据的拷贝打印,此处我们利用数组来模拟数据的buff,然后再init函数中对其进行格式化数据,模拟写数据,


接着我们在mymsg_read函数中对其进行读取,看是否能成功读出数据.


1.定义一个内存buff数组


1 //定义proc的entry结构体

2 static struct proc_dir_entry *myentry;

3 static char mylog_buf[1024];        //数据缓冲区

 

2.在init函数中对其进行格式化字符串,模拟写数据


 1 static int mymsg_init(void)

 2 {

 3     sprintf(mylog_buf, '%s', 'abcdefghijklmnn');    //模拟伪造buf的数据

 4     //创建proc的目录

 5     myentry = create_proc_entry('mymsg',S_IRUSR,NULL);    //S_IRUSR:400 只读

 6     

 7     if(myentry)

 8         myentry->proc_fops = &proc_mymsg_operations;

 9     

10     return 0;

11 }


3.再mymsg_read函数中对其进行读取


1 //实现读函数

2 static ssize_t mymsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos)

3 {

4     if(copy_to_user(buf, mylog_buf, 10));

5     return 10;

6 }

 

4.编译测试:发现成功的读出了数据.

b0f56e35d7dae278067df7739bcd876b_ACP2VBHeQAvGAAAAAElFTkSuQmCC.png

附上驱动程序mymsg_3.c

 1 #include

 2 #include

 3 #include

 4 #include

 5 #include

 6 #include

 7 #include

 8 #include

 9 

10 //定义proc的entry结构体

11 static struct proc_dir_entry *myentry;

12 static char mylog_buf[1024];        //数据缓冲区

13 

14 //实现读函数

15 static ssize_t mymsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos)

16 {

17     //int cnt;

18     //printk('mymsg_read n');

19     // 把mylog_buf的数据copy_to_user, return

20     //cnt = min(1024,count);

21     if(copy_to_user(buf, mylog_buf, 10));

22     

23     return 10;

24 }

25 

26 //定义file_operation结构体

27 static struct file_operations proc_mymsg_operations = {

28     .read = mymsg_read,

29 };

30 

31 static int mymsg_init(void)

32 {

33     sprintf(mylog_buf, '%s', 'abcdefghijklmnn');    //模拟伪造buf的数据

34     //创建proc的目录

35     myentry = create_proc_entry('mymsg',S_IRUSR,NULL);    //S_IRUSR:400 只读

36     

37     if(myentry)

38         myentry->proc_fops = &proc_mymsg_operations;

39     

40     return 0;

41 }

42 

43 static void mymsg_exit(void)

44 {

45     remove_proc_entry('mymsg', NULL);

46 }

47 

48 module_init(mymsg_init);

49 module_exit(mymsg_exit);

50 

51 MODULE_LICENSE('GPL');

52 MODULE_AUTHOR('Lover雪儿');

53 

54 

55 /*

56 1.环形缓冲区

57     空: R == W

58     写: buf[W] = val;

59         W = (W+1) % 10;

60     读: val = buf[R]

61         R = (R+1) % 10    

62 2.        

63         

64         

65         

66 

67 

68 

69 

70 

71 

72 

73 */


四.参考printk的函数,可以用于对消息的进行打印保存


现在我们来编写一个类似printk的myprintk函数,从而实现其他驱动程序调用myprintk将打印信息全部输出到/proc/mymsg中,


便于统一对驱动的打印信息进行调试,不会收到其他的打印信息的干扰.


测试驱动程序我们选用以前imx257的led驱动程序:


博客文章地址:http://www.cnblogs.com/lihaiyan/p/4297923.html


当然,选择其他的驱动程序也一样,只要外部声明一下myprintk函数,然后将全部的printk替换为myprintk即可.


1.定义两个数据buff,以及读写指针,和一个等待队列


1 //定义proc的entry结构体

2 static struct proc_dir_entry *myentry;

3 static char mylog_buf[MYLOG_BUF_LEN];        //数据缓冲区

4 static char tmp_buf[MYLOG_BUF_LEN];            //数据缓冲区

5 static int mylog_r = 0;

6 static int mylog_w = 0;

8 static DECLARE_WAIT_QUEUE_HEAD(mylog_wait);


2.实现 判断buff空的函数


1 //判断是否为空

2 static int is_mylog_empty(void)

3 {

4     return (mylog_r == mylog_w);

5 }

 

3.实现 判断buff满的函数


1 //判断是否已满

2 static int is_mylog_full(void)

3 {

4     return (((mylog_w + 1) % MYLOG_BUF_LEN) == mylog_r);

5 }

 

4.实现 向buff写入字符的函数


 1 //写入字符

 2 static void mylog_putc(char c)

 3 {

 4     if(is_mylog_full)

 5     {

 6         //丢弃一个数据

 7         mylog_r = (mylog_r + 1) % MYLOG_BUF_LEN;

 8     }

 9     mylog_buf[mylog_w] = c;

10     mylog_w = (mylog_w + 1) % MYLOG_BUF_LEN;

11     

12     /* 唤醒等待数据的进程 */

13     wake_up_interruptible(&mylog_wait);

14 }


函数除了向buff中写的功能外,还有一个重要的任务就是唤醒进程,从而再read函数中将数据打印出来


5.实现 读取buff字符的函数


1 //读取字符

2 static int mylog_getc(char *p)

3 {

4     if(is_mylog_empty())

5         return 0;

6     *p = mylog_buf[mylog_r];

7     mylog_r = (mylog_r + 1) %  MYLOG_BUF_LEN;

8     return 1;

9 }


6.参考内核代码中的vsprintf.c的sprinf函数,实现myprintk函数,并且导出myprintk函数,供其他的程序使用


//打印输出  参考vsprintf.c  的 sprintf

int myprintk(const char *fmt, ...)

{

    va_list args;

    int i,j;

    

    va_start(args, fmt);

    i = vsnprintf(tmp_buf, INT_MAX, fmt, args);

    va_end(args);

    for(j = 0; j    {

        mylog_putc(tmp_buf[j]);

    }

    return 1;

}

EXPORT_SYMBOL(myprintk);


7.完善读函数


//实现读函数     参考kmsg.c

static ssize_t mymsg_read(struct file *file, char __user *buf,size_t count, loff_t *ppos)

{

    int error;

    int i = 0;        //所读的数据的个数

    char c;

    

    //以非阻塞打开,并且数据队列为空

    if ((file->f_flags & O_NONBLOCK) && is_mylog_empty())

        return -EAGAIN;

    

    //等待直到队列非空

    error = wait_event_interruptible(mylog_wait,!is_mylog_empty());

    

    /* copy_to_user 若是没错,获取字符串成功 */

    while( !error && (mylog_getc(&c)) && i < count ){

        error = __put_user(c, buf);            //等同copy_to_user

        buf++;

        i++;

    }

    if(!error)

        error = i;

    return error;

}


当应用程序使用cat来读取/proc/mymsg时,如果程序是以非阻塞的方式开始,并且buff为空的话,则直接返回,否则让程序进入可中断的睡眠,

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

相关文章

    ) ;设置I1为反转限位2开关输入端, Pr4.01=65793 ( 00010101H) ;设置SI2为正转限位2开关输入端。 Pr5.04=0;正反转限位开关2有效, Pr5.05=2;正反转限位顺序设......
    使系统能稳定可靠的工作。具体驱动电路如图2所示。 3 、步进电机控制程序设计 3.1 下位机程序设计 单片机接口程序采用C51语言编写。程序中,定义了数组table1和table2,用来......
    热继电器的保护触点采用动断触点输入,梯形图中的程序X3(FR动断)采用了动合触点。 PLC控制电动机正反转程序设计方法 PLC控制电动机正反转电路的程序设计方法比较多。 图3-3所示为采用置位与复位指令控制电动机正反转运行的程序......
    输入模块采集输入信号,例如传感器信号、按钮信号、开关信号等等。   信号处理:PLC对输入信号进行处理,例如滤波、放大、反转等操作,以确保信号的准确性和稳定性。   扫描程序:PLC在内部设定的扫描周期内,按照程序的顺序执行各个程序......
    )总体电路图 3.软件设计 系统软件设计主要有几部分组成,主要分为主程序和各个模块子程序。 (1)主程序设计 主程序构成无限循环,主要完成单片机初始化,关中断,菜单显示内容初始化,按键扫描,电机......
    的指示灯会根据电梯上行和下行高亮,到达指定楼层后状态灯会直接亮起。 3.当按下上下楼按键后电机模拟电梯的运转,上楼电机正转,下楼电机反转。 4.在电梯内外使用按键进行报警。 三.程序设计 使用Keil 51进行程序设......
    PID控制算法,编写出相应的流量控制子程序,实现对流量的控制,达到预期的控制要求; 3.各子程序:各个子程序完成具体的实现方法,主要包括:设定值输入、数码管显示、步进电机控制、AD转换中断、T0定时......
    单片机烧录程序怎么分类;说起给单片机烧录程序,大家应该都不陌生,我最早接触单片机是从51单片机开始的,型号是STC89C52RC,当时烧录程序就是用的下面这种烧录软件——STC-ISP。 这种......
    程序烧录方式你了解多少?;说起给单片机烧录程序,大家应该都不陌生,我最早接触单片机是从51单片机开始的,型号是STC89C52RC,当时烧录程序就是用的下面这种烧录软件——STC-ISP。 这种......
    度高于阈值则通过蜂鸣器报警,并利用风扇进行降温。   三.程序设计 使用Keil 51进行程序设计,打开Proteus时程序是默认烧录的状态,如果没有烧录点击AT89C51单片机并将程序导入就能运行系统(程序......

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>