回调函数:把需要调用的方法的指针pCallBackFuncX作为参数传递给一个函数UsrFunction,以便该UsrFunction函数在处理相似事件的时候可以灵活的使用不同的方法。
以在flash中存储有序的交易记录为例:
交易记录列表内容如下所示,我们要对其排序然后存储到flash当中去。这个排序函数写作: int SortReclist(RECLIST_ST * reclist);
typedef struct { char cityCode[2] ; //城市代码 + 应用序列号 char usrName[10]; unsigned char tradeTime[7]; //交易时间【bcd】 年 月 日 时 分 秒 unsigned char TerminalCode[6]; //终端硬件序列号 unsigned char reserve[1]; } RECS_ST ;//交易记录 typedef struct { unsigned char count[2] ; unsigned char max[2] ; unsigned char data[N_MAX_RECORD][SIZE_OF_RECS];// sizeof(RECS_ST); //要对这里的数据进行排序、去重。 unsigned char check ; } RECLIST_ST;//交易记录列表
我们使用冒泡排序的方法对其中的数据进行排序,我们得到一组无序数据,我们不知道这组数据有多少个数据项、一个数据项多大、怎么比较他们的大小、如何交换数据项的位置。
我们可以设计如下,我们需要传入的参数不仅是数据p_data_in、n_elements、element_size,还有方法cmp_func、mem_swap:
bubble_sort(void* p_data_in , //pointer to data to sort 待排列的数据 int n_elements , //number of elements 待排列的数据个数 int element_size, //size of each element == sizeof(RECS_ST) 数据宽度 (CALLBACK_AFC_DATA_CMP) cmp_func , //cmp_func: pointer to comparison function 判定其排列次序的方法,这个是根据需要变换的。 (CALLBACK_MEM_SWAP) mem_swap) ; //mem_swap: pointer to swap function or NULL 内存交换的方法
typedef void (*CALLBACK_MEM_SWAP)(void *, void *, int n_bytes) ; typedef int (*CALLBACK_AFC_DATA_CMP)(const void *, const void *) ;
bubble_sort的实现如下:
//ret==1: error
//ret==0: ok int bubble_sort(void *base , //pointer to data to sort int n_elements , //number of elements int size_element , //size of each element CALLBACK_AFC_DATA_CMP cmp_func ,//cmp_func: pointer to comparison function CALLBACK_MEM_SWAP mem_swap )//mem_swap: pointer to swap function or NULL { int i, j, flag , cmp ; if( base==0 || cmp_func==0 || mem_swap==0 ){ return 1 ; } for (i=0; i<n_elements-1; i++) { flag = 0; for (j=0; j<n_elements-1-i; j++) { cmp = cmp_func( ((char *)base+size_element*j ) , ((char *)base+size_element*(j+1)) ) ; if ( cmp > 0 ) { mem_swap( ((char *)base+j*size_element) , ((char *)base+(j+1)*size_element) , size_element ); flag = 1; } } if ( !flag ) { break ; } } return 0; }
int SortReclist(RECLIST_ST * reclist) { int ret; unsigned char count[2]; do{ memcpy(count , reclist->count , sizeof( count )); //这里是解决mdk对外部全局变量不能正确强转的解决办法,无视 bubble_sort(reclist->data , //pointer to data to sort *(signed short*)count , //number of elements sizeof( RECS_ST ) , //size of each element (CALLBACK_AFC_DATA_CMP) reclistcmp , //cmp_func: pointer to comparison function (CALLBACK_MEM_SWAP)mem_swap) ; //mem_swap: pointer to swap function or NULL //暂且无视排序中去重的需求 }while(ret!=0) ; return 0; }
到底采用何种方式来判断数据的大小,就根据实际需要了。下面是一个实现,只比较城市代码:
int reclistcmp(char *before ,char *next ) { int ret ; ret = memcmp(before ,next , 2); //citycode[2] 简单判定 return ret ; }
好处是,以回调函数的方式,在写主体框架时,可以不用知道某种功能的具体实现,而只是提出这个需求。
【写框架的人并不是完全不关心具体实现,不合理的框架设计导致具体功能无法按照给定的接口来实现。】
这样在处理具体业务时有更高的灵活性,这个功能的具体实现甚至可以交给其他人去完成。
//-----------------------------------------------------------------------------------------------------------------------------------------------//
另外,linux设备驱动函数注册也是回调的思想。linux内核并不知道我们具体设备的操作方式,它是这样对设备进行操作的,先规定操作函数的形式:
struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);//指定函数接口形式
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); int (*dir_notify)(struct file *filp, unsigned long arg); int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); };
然后驱动设计者按照规定的形式实现ioctl。
/* 应用程序对设备文件/dev/leds执行ioctl(...)时, * 就会调用s3c24xx_leds_ioctl函数 */ static int s3c24xx_leds_ioctl( struct inode *inode, struct file *file, unsigned int cmd,//ON / OFF unsigned long arg)//n_led { if (arg > 4) { return -EINVAL; } switch(cmd) { case IOCTL_LED_ON: // 设置指定引脚的输出电平为0 s3c2410_gpio_setpin(led_table[arg], 0); return 0; case IOCTL_LED_OFF: // 设置指定引脚的输出电平为1 s3c2410_gpio_setpin(led_table[arg], 1); return 0; default: return -EINVAL; } }
驱动设计者再注册操作集,包括了ioctl。
static struct file_operations s3c24xx_leds_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ .open = s3c24xx_leds_open, .ioctl = s3c24xx_leds_ioctl, };
//in moudle_init:
ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s3c24xx_leds_fops);
这样,在打开操作设备时,系统使用到了驱动设计者的设备操作方法。
扩展阅读:
1.回调机制
http://blog.sina.cn/dpool/blog/s/blog_77c632410101cjty.html
回调实现分层设计。
http://blog.chinaunix.net/uid-21222282-id-1829257.html