2013-09-01 22:42:34 kingdragonfly120 阅读数 3309
  • 安卓编译与开发、Linux内核及驱动

    安卓编译与开发、Linux内核及驱动视频教程,该课程内容包括一、源码编译:1、常见的ROM种类、谷歌的ROM、第三方的ROM2、区别3、RockChip的ROM、4、编译环境配置、源码下载、编译命令;二、源码开发:源码结构分析、Launcher开发、需求分析、系统级应用;三、内核讲解:内核用途、内核结构、内核职能、内核源码编译、驱动开发;四、内核开发:1、体系架构、目录结构、中断机制、内核进程控制、2、内核库文件、例子简单分析、3、内核调度流程4、内核组件工具 嘉宾介绍:仝利,英文名Burning,CSDN博客讲师,多年主要从事于流媒体,网络通讯,p2p等技术的开发与研究。目前在创业,产品是面向企业会议室和家庭客厅的多媒体通讯盒子的开发。国内还没有相关产品,预计产品会在8月份上市。

    9282 人正在学习 去看看 CSDN讲师

        当linux内核空间发生中断后怎么使用户空间的应用程序运行相应的函数呢,当芯片有数据到来时内核会产生一个中断,但是怎样通知应用程序来取数据,以前这个问题一直困扰我很长时间,后来发现linux中有异步通知机制,在用户程序中用signal注册一个响应SIGIO信号的回调函数,然后在驱动程序中向该进程发出SIGIO信号便完成该功能,下面是该功能具体实施方法:

1.在驱动中定义一个static struct fasync_struct *async;

2.在fasync系统调用中注册fasync_helper(fd, filp, mode, &async);

3.在中断服务程序(顶半部、底半部都可以)发出信号kill_fasync(&async, SIGIO, POLL_IN);

4.在用户应用程序中用signal注册一个响应SIGIO的回调函数signal(SIGIO, sig_handler);

5.通过fcntl(fd, F_SETOWN, getpid())将将进程pid传入内核

6.通过fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | FASYNC)设置异步通知


驱动部分代码:


#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <mach/regs-gpio.h>
#include <asm-generic/siginfo.h>
#include <linux/init.h>
#include <asm/signal.h>
#include <linux/timer.h>
#include <asm/uaccess.h>

#define DEVICE_NAME "mybeep"

volatile unsigned long *GPBCON;
volatile unsigned long *GPBDAT;
volatile unsigned long *GPBUP;
void beep_start(void);
void beep_stop(void);
int  beep_irq_register(void);
unsigned int flag=1;

static struct fasync_struct *async; //声明fasync_struct
struct key_irq_desc {
	unsigned int irq;
	int pin;
	int pin_setting;
	int number;
	char *name;
};

static int beep_fasync(int fd, struct file *filp, int mode)
{
	printk("application  fasync!\n");
	return fasync_helper(fd, filp, mode, &async);         //注册上层调用进程的信息,上层调用fcntl设置FASYNC会调用这个系统调用
}

static struct key_irq_desc key_irqs [] = {
	{IRQ_EINT8, S3C2410_GPG(0), S3C2410_GPG0_EINT8, 0, "KEY1"},
};

static irqreturn_t key_interrupt(int irq, void *dev_id)
{
	kill_fasync(&async, SIGIO, POLL_IN);  //向打开设备文件的进程发出SIGIO信号
	return (IRQ_HANDLED);
}

void beep_gpiob_init(void)
{
	*GPBCON&=~((1<<0)|(1<<1));
	*GPBCON|=(1<<0);
	*GPBUP&=~(1<<0);
}

void beep_start(void)
{
	*GPBDAT|=(1<<0);
}

void beep_stop(void)
{
	*GPBDAT&=~(1<<0);
}

int beep_open(struct inode *inode, struct file *filp)
{
	if(beep_irq_register() != 0)
	{
		printk("Request irq error!\n");
	}
	printk(KERN_ALERT "application  open!\n");
	return 0;
}

ssize_t beep_read(struct file *file, char __user *buff, size_t count, loff_t *offp)
{
	printk("application  read!\n");
	return 0;
}

ssize_t beep_write(struct file *file, const char __user *buff, size_t count, loff_t *offp)
{
	printk("application  write!\n");
	return 0;
}

static int beep_release(struct inode *inode, struct file *file)
{
	disable_irq(key_irqs[0].irq);
	free_irq(key_irqs[0].irq, (void *)&key_irqs[0]);
	printk("application  close!\n");
	return beep_fasync(-1, file, 0);
}

static int beep_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
	switch(cmd)
	{
	case 0:
		beep_start();
		break;
	case 1:
		beep_stop();
		break;
	default:
		break;
	}
	return 0;
}

static struct file_operations beep_ops = {
	.owner = THIS_MODULE,
	.open = beep_open,
	.release = beep_release,
	.ioctl = beep_ioctl,
	.read = beep_read,
	.write = beep_write,
	.fasync = beep_fasync,
};

static struct miscdevice beep_misc = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEVICE_NAME,
	.fops = &beep_ops,
};

int beep_irq_register(void)
{
	int err;
	err = request_irq(key_irqs[0].irq, key_interrupt, 0, key_irqs[0].name, (void *)&key_irqs[0]);
	set_irq_type(key_irqs[0].irq, IRQ_TYPE_EDGE_RISING);
	if(err)
	{
		disable_irq(key_irqs[0].irq);
		free_irq(key_irqs[0].irq, (void *)&key_irqs[0]);
		return -EBUSY;
	}
	return 0;
}

static int __init beep_init(void)
{
	int ret;
	ret=misc_register(&beep_misc);
	if(ret <0)
	{
		printk("register miscdevice error code:%d\n",ret);
		return ret;
	}
	printk("beep device create!\n");
	GPBCON=(volatile unsigned long *)ioremap(0x56000010,12);
	GPBDAT=GPBCON+1;
	GPBUP=GPBCON+2;
	beep_gpiob_init();
	return 0;
}

static void __exit beep_exit(void)
{
	iounmap(GPBCON);
	misc_deregister(&beep_misc);
	printk("beep device delete!\n");
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("kingdragonfly");
module_init(beep_init);
module_exit(beep_exit);


用户应用程序代码:

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

void sig_handler(int sig)
{
	if(sig == SIGIO)
	{
		printf("Receive io signal from kernel!\n");
	}
}

int main(void)
{
	int fd;
	signal(SIGIO, sig_handler);
	fd = open("/dev/mybeep",O_RDWR);
	fcntl(fd, F_SETOWN, getpid());
	fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | FASYNC);
	printf("waiting key interrupt:\n");
	while(1)
	{
	}
}

当内核里发生中断时在中断服务程序中发出SIGIO信号从而自动调用相应的回调函数,在回调函数中可以进行相应处理。

上面程序在mini2440开发板实现了按K1键,用户程序自动调用void sig_handler(int sig)功能

2019-11-19 19:18:13 a568713197 阅读数 72
  • 安卓编译与开发、Linux内核及驱动

    安卓编译与开发、Linux内核及驱动视频教程,该课程内容包括一、源码编译:1、常见的ROM种类、谷歌的ROM、第三方的ROM2、区别3、RockChip的ROM、4、编译环境配置、源码下载、编译命令;二、源码开发:源码结构分析、Launcher开发、需求分析、系统级应用;三、内核讲解:内核用途、内核结构、内核职能、内核源码编译、驱动开发;四、内核开发:1、体系架构、目录结构、中断机制、内核进程控制、2、内核库文件、例子简单分析、3、内核调度流程4、内核组件工具 嘉宾介绍:仝利,英文名Burning,CSDN博客讲师,多年主要从事于流媒体,网络通讯,p2p等技术的开发与研究。目前在创业,产品是面向企业会议室和家庭客厅的多媒体通讯盒子的开发。国内还没有相关产品,预计产品会在8月份上市。

    9282 人正在学习 去看看 CSDN讲师

简介

前面使用阻塞和非阻塞的方式来读取驱动中的按键值都是应用程序主动读取的,对于非阻塞的方式还需要应用程序通过poll函数不断的轮询
Linux内核提供了异步通知这个机制来实现驱动程序主动向应用程序发出通知,报告自己可以访问,然后应用程序再从驱动程序中读取或写入数据,软件中断的方式
”信号“类似于硬件上的中断,是软件层面上对中断的模拟,设备可以被读写时发出信号
阻塞、非阻塞、异步通知是针对不同的场合提出来的不同的解决方法,没有优劣之分,选择合适的
除了SIGKILL(9)和SIGSTOP(19)这两个信号不能被忽略外,其他的信号都可以被忽略
如果要在应用程序中使用信号,必须设置信号所使用的信号处理函数,使用如下的函数来注册信号处理函数

sighandler_t signal(int signum, sighandler_t handler)

signum信号标号,handler处理函数指针
处理函数的原型:typedef void (*sighandler_t)(int)

驱动中的信号处理

重点关注一个数据结构两个函数

fasync_struct结构体

struct fasync_struct { 
	spinlock_t fa_lock; 
	int magic; 
	int fa_fd; 
	struct fasync_struct *fa_next; 
	struct file *fa_file; 
	struct rcu_head fa_rcu; 
};

fasync函数

如果要使用异步通知,需要在设备驱动中实现fops中的fasync函数

int (*fasync) (int fd, struct file *filp, int on)

fasync函数中一般通过fasync_helper函数来初始化fasync_struct结构体

int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)

前三个参数就是fasync的三个参数
第四个参数就是要初始化fasync_struct结构体指针变量
当应用程序通过fcntl(fd,F_SETEL,flags | FASYNC)改变fasync标记时,驱动程序fops操作集中的fasync函数就会执行
关闭驱动文化的时候需要在file_operations操作集中的release函数中释放fasync_struct,释放时同样调用fasync_helper

kill_fasync函数

当设备可以访问的时候,驱动程序需要向应用程序发出信号,相当于产生”中断“。kill_fastync函数负责发送指定的信号,kill_fastync函数原型如下

void kill_fasync(struct fasync_struct **fp, int sig, int band)
  • fp:要操作的 fasync_struct
  • sig:要发送的信号
  • band:可读时设置为POLL_IN,可写时设置为POLL_OUT

应用程序的处理

1、注册信号处理函数
2、将本应用程序的进程号告诉内核
3、开启异步通知

	flags = fcntl(fd,F_GETEL);//获取当前的进程状态
	fcntl(fd,F_SETEL,flags | FASYNC);//开启当前进程异步通知功能

重点就是通过fcntl函数设置进程状态为FASYNC,经过这一步,驱动程序中的fasync函数就会执行

实验代码及分析

实验代码

驱动代码

struct irqkey_dev
{
    dev_t devid;
	...
    struct fasync_struct *async_queue;
};

void timer_function(unsigned long arg)
{
    unsigned char value;
	...
    //一次完成的按键过程
    if(atomic_read(&dev->releasekey))
    {
        if(dev->async_queue)
            kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
    }
}
static int key_fasync(int fd, struct file *filp, int on)
{
    printk(KERN_EMERG "key_fasync enter!\n");
    struct irqkey_dev *dev = (struct irqkey_dev *)filp->private_data;

    return fasync_helper(fd, filp, on, &dev->async_queue);
}

static int key_release(struct inode *inode,struct file *filp)
{
    printk(KERN_EMERG "key_release enter!\n");
    return key_fasync(-1, filp, 0);
}

应用程序代码

static void sigio_signal_func(int signum)
{
    int err = 0;
    unsigned int keyvalue = 0;

    err = read(fd, &keyvalue, sizeof(keyvalue));
    if(err < 0)
    {

    }
    else
    {
        printf("SIGIO signal! key value = %d\r\n",keyvalue);
    }
    
}
int main(int argc, char *argv[])
{
...
 /*open device*/
 fd = open(filename, O_RDWR);
 if(fd < 0)
 {
     printf("Can't open file %s\r\n", filename);
     return -1;
 }
 printf("Open file %s OK!\r\n", filename);
 
 signal(SIGIO, sigio_signal_func);

 fcntl(fd, F_SETOWN, getpid());
 flags = fcntl(fd, F_GETFD);
 fcntl(fd, F_SETFL, flags | FASYNC);

 while(1)
 {
     sleep(2);
 }
 }

代码分析

驱动部分

在timer_function中,当经过按键防抖确实按键被按下后使用kill_fasync()释放SIGIO信号,第三个参数为POLL_IN表示资源可读
在驱动的fasync函数中使用fasync_helper函数初始化fasync,包括分配内存和设置属性
在驱动的release函数中调用设备驱动的fasync函数(key_fasync())将文件从异步通知的列表中删除
使用如下的语句

return key_fasync(-1, filp, 0);

应用程序部分

在应用程序中使用signal来为SIGIO安装sigio_signal_func()作为信号处理函数

signal(SIGIO, sigio_signal_func);

使用fcntl来设置本进程为文件的拥有者,没有这一步内核不会知道应该将信号发给哪个进程

fcntl(fd, F_SETOWN, getpid());

因为启用了异步通知机制还需对设备设置FASYNC机制

flags = fcntl(fd, F_GETFD);
fcntl(fd, F_SETFL, flags | FASYNC);

总结

为了能在用户空间中处理一个设备释放的信号,它必须完成3项工作

  • 通过F_SETOWN IO控制命令设置设备文件的拥有者为本进程,这样从设备驱动发出的信号才能被本进程接收到
  • 通过F_SETOWN IO控制命令设置设备文件以支持FASYNC,即异步通知模式
  • 通过signal()函数连接信号和信号处理函数

为了使设备支持异步通知机制,驱动程序中涉及3项工作

  • 支持F_SETOWN 命令,能在这个控制命令处理中设置filp->f_owner为对应进程ID。不过此项工作已由内核完成,设备驱动无需处理
  • 支持F_SETFL命令,没放FASYNC标志改变时,驱动程序中的fasync()函数将得以执行。因此,驱动中应该事先fasync()函数
  • 在设备资源可获得时,调用kill_fasync()函数激发相应的信号

整个机制的框图如下
在这里插入图片描述

2019-08-20 10:01:23 qq_41786131 阅读数 109
  • 安卓编译与开发、Linux内核及驱动

    安卓编译与开发、Linux内核及驱动视频教程,该课程内容包括一、源码编译:1、常见的ROM种类、谷歌的ROM、第三方的ROM2、区别3、RockChip的ROM、4、编译环境配置、源码下载、编译命令;二、源码开发:源码结构分析、Launcher开发、需求分析、系统级应用;三、内核讲解:内核用途、内核结构、内核职能、内核源码编译、驱动开发;四、内核开发:1、体系架构、目录结构、中断机制、内核进程控制、2、内核库文件、例子简单分析、3、内核调度流程4、内核组件工具 嘉宾介绍:仝利,英文名Burning,CSDN博客讲师,多年主要从事于流媒体,网络通讯,p2p等技术的开发与研究。目前在创业,产品是面向企业会议室和家庭客厅的多媒体通讯盒子的开发。国内还没有相关产品,预计产品会在8月份上市。

    9282 人正在学习 去看看 CSDN讲师

信号机制是 Linux 进程间通信的一种重要方式,Linux 信号一方面用于正常的进程间通信和同步,如任务控制(SIGINT, SIGTSTP,SIGKILL, SIGCONT,……);另一方面,它还负责监控系统异常及中断。 当应用程序运行异常时, Linux 内核将产生错误信号并通知当前进程。 当前进程在接收到该错误信号后,可以有三种不同的处理方式。

(1)忽略该信号。

(2)捕捉该信号并执行对应的信号处理函数(signal handler)。

(3)执行该信号的缺省操作(如 SIGSEGV, 其缺省操作是终止进程)。

当 Linux 应用程序在执行时发生严重错误,一般会导致程序 crash。其中,Linux 专门提供了一类 crash 信号,在程序接收到此类信号时,缺省操作是将 crash 的现场信息记录到 core 文件,然后终止进程。

常见的crash信号列表如下(定义在prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8/sysroot/usr/include/bits/signum.h):

           信号类型                      官方描述                                             可能原因
     
     
     
     
     
     
     
     
     
     
     
     

 

2012-11-17 17:27:02 ssjmhyvi 阅读数 35
  • 安卓编译与开发、Linux内核及驱动

    安卓编译与开发、Linux内核及驱动视频教程,该课程内容包括一、源码编译:1、常见的ROM种类、谷歌的ROM、第三方的ROM2、区别3、RockChip的ROM、4、编译环境配置、源码下载、编译命令;二、源码开发:源码结构分析、Launcher开发、需求分析、系统级应用;三、内核讲解:内核用途、内核结构、内核职能、内核源码编译、驱动开发;四、内核开发:1、体系架构、目录结构、中断机制、内核进程控制、2、内核库文件、例子简单分析、3、内核调度流程4、内核组件工具 嘉宾介绍:仝利,英文名Burning,CSDN博客讲师,多年主要从事于流媒体,网络通讯,p2p等技术的开发与研究。目前在创业,产品是面向企业会议室和家庭客厅的多媒体通讯盒子的开发。国内还没有相关产品,预计产品会在8月份上市。

    9282 人正在学习 去看看 CSDN讲师

 

 

2017-01-02 16:15:38 l289123557 阅读数 608
  • 安卓编译与开发、Linux内核及驱动

    安卓编译与开发、Linux内核及驱动视频教程,该课程内容包括一、源码编译:1、常见的ROM种类、谷歌的ROM、第三方的ROM2、区别3、RockChip的ROM、4、编译环境配置、源码下载、编译命令;二、源码开发:源码结构分析、Launcher开发、需求分析、系统级应用;三、内核讲解:内核用途、内核结构、内核职能、内核源码编译、驱动开发;四、内核开发:1、体系架构、目录结构、中断机制、内核进程控制、2、内核库文件、例子简单分析、3、内核调度流程4、内核组件工具 嘉宾介绍:仝利,英文名Burning,CSDN博客讲师,多年主要从事于流媒体,网络通讯,p2p等技术的开发与研究。目前在创业,产品是面向企业会议室和家庭客厅的多媒体通讯盒子的开发。国内还没有相关产品,预计产品会在8月份上市。

    9282 人正在学习 去看看 CSDN讲师

1. 定时器类型


经典定时器(低精度)

内核最初版本就是提供的此类定时器,实现于kernel/timer.c中,提供的典型分辨率为4ms,从现在看来分辨率很低,此类定时器只产生周期性的信号

高精度定时器

随着应用需求的提升,后来引入了高分辨率定时器,分辨率可以达到ns级别,这样就可以不用只产生周期行的信号,而是设置一些需要的事件信号就可以,这样就可以随机发出信号,而原先的周期信号就可以通过模拟来产生

2. 源代码


时间子系统的源代码在目录kernel/time下,代码总量近2W行:

~/kernel/time$ wc -l ./*
  1089 ./alarmtimer.c
   763 ./clockevents.c--------------------clockevent
  1019 ./clocksource.c--------------------clocksource
  1830 ./hrtimer.c------------------------hrtimer
   301 ./itimer.c
   136 ./jiffies.c
   197 ./Kconfig
    14 ./Makefile
  1017 ./ntp.c
    13 ./ntp_internal.h
   446 ./posix-clock.c
  1523 ./posix-cpu-timers.c
  1131 ./posix-timers.c
   303 ./sched_clock.c
   168 ./test_udelay.c
  1005 ./tick-broadcast.c
   110 ./tick-broadcast-hrtimer.c
   535 ./tick-common.c
   166 ./tick-internal.h
   132 ./tick-oneshot.c
  1183 ./tick-sched.c
    84 ./tick-sched.h
   764 ./time.c
   109 ./timeconst.bc
   127 ./timeconv.c
   112 ./timecounter.c
  2054 ./timekeeping.c
    74 ./timekeeping_debug.c
    26 ./timekeeping.h
    29 ./timekeeping_internal.h
  1718 ./timer.c
   401 ./timer_list.c
   425 ./timer_stats.c
 19004 total

3. 子系统软件框架


时间子系统的软件框架如图所示,从底层硬件到上层软件的整个关系概略如下:

这里写图片描述

3.1 clockevent和clocksource(timekeeper)

一个硬件timer在内核中可以被表示成两种形式:clockevent和clocksource

clocksource层主要是来表示硬件源,提供操作硬件设备的各种功能:读取时间;timer设备的休眠与唤醒;使能和关闭设备等。clocksource最终会提供给timekeeper模块使用,每注册进一个timer设备,系统都会进行比较,让timekeeper选取一个最合适的clocksource

clockevent层表示时钟事件,当某个事件到时间时,就产生一个中断报告事件到来,clockevent被上层的tick_device层使用。

3.2 tick_device

tick_device表示能产生tick的设备,其实就是timer设备,或许是出于区分,所以用tick_device来表示。

tickdevice分两种:oneshot和periodic。oneshot是单触发的,在内核中又称为是动态的,它产生的时钟信号都是动态信号,没有固定的周期,不过也可以模拟固定周期,只要以一个固定时间产生时钟信号即可。periodic是周期的,它以一个固定的时间来产生时钟信号,而且支持这种模式的底层硬件timer一般是低精度的。

对于低精度timer,最合适的是表示成periodic tick_device,这样就能产生周期性的事件,但是如果要支持动态(即高精度)事件,可以通过配置CONFIG_NO_HZ_COMMON开启,这样就能实现在低精度timer设备的支持下实现动态事件,但是动态事件的产生不能是高精度的。

对于高精度timer,最合适的就是表示成oneshot tick_device,这样就能实现动态事件,如果要模拟周期性的事件,还需要开启CONFIG_HIGH_RES_TIMERS,这样就能实现。

3.3 hrtimer

时间子系统中动态事件的运转需要hrtimer模块支持,每一个事件用hrtimer表示,然后加入到红黑树中,然后在每个hrtimer的时间到期时,timer就会产生时钟信号,然后调用此时的事件任务就可以了。

change log


date content linux
2017.1.2 初稿 linux4.6.3
没有更多推荐了,返回首页