//以下是学习完韦东山老师视屏教程后所做学习记录
中断方式取得按键值:
 #include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h> static struct class *thirddrv_class;
static struct class_device *thirddrv_class_dev; volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat; volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat; static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /* 中断事件标志, 中断服务程序将它置1,third_drv_read将它清0 */
static volatile int ev_press = ; struct pin_desc{
unsigned int pin;
unsigned int key_val;
}; /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
static unsigned char key_val; struct pin_desc pins_desc[] = {
{S3C2410_GPF0, 0x01},
{S3C2410_GPF2, 0x02},
{S3C2410_GPG3, 0x03},
{S3C2410_GPG11, 0x04},
}; /*
* 确定按键值
*/
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
struct pin_desc * pindesc = (struct pin_desc *)dev_id;
unsigned int pinval; pinval = s3c2410_gpio_getpin(pindesc->pin); if (pinval)
{
/* 松开 */
key_val = 0x80 | pindesc->key_val;
}
else
{
/* 按下 */
key_val = pindesc->key_val;
} ev_press = ; /* 表示中断发生了 */
wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ return IRQ_RETVAL(IRQ_HANDLED);
} static int third_drv_open(struct inode *inode, struct file *file)
{
/* 配置GPF0,2为输入引脚 */
/* 配置GPG3,11为输入引脚 */
request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[]);
request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[]);
request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[]);
request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[]); return ;
} ssize_t third_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
if (size != )
return -EINVAL; /* 如果没有按键动作, 休眠 */
wait_event_interruptible(button_waitq, ev_press); /* 如果有按键动作, 返回键值 */
copy_to_user(buf, &key_val, );
ev_press = ; return ;
} int third_drv_close(struct inode *inode, struct file *file)
{
free_irq(IRQ_EINT0, &pins_desc[]);
free_irq(IRQ_EINT2, &pins_desc[]);
free_irq(IRQ_EINT11, &pins_desc[]);
free_irq(IRQ_EINT19, &pins_desc[]);
return ;
} static struct file_operations sencod_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = third_drv_open,
.read = third_drv_read,
.release = third_drv_close,
}; int major;
static int third_drv_init(void)
{
major = register_chrdev(, "third_drv", &sencod_drv_fops); thirddrv_class = class_create(THIS_MODULE, "third_drv"); thirddrv_class_dev = class_device_create(thirddrv_class, NULL, MKDEV(major, ), NULL, "buttons"); /* /dev/buttons */ gpfcon = (volatile unsigned long *)ioremap(0x56000050, );
gpfdat = gpfcon + ; gpgcon = (volatile unsigned long *)ioremap(0x56000060, );
gpgdat = gpgcon + ; return ;
} static void third_drv_exit(void)
{
unregister_chrdev(major, "third_drv");
class_device_unregister(thirddrv_class_dev);
class_destroy(thirddrv_class);
iounmap(gpfcon);
iounmap(gpgcon);
return ;
} module_init(third_drv_init); module_exit(third_drv_exit); MODULE_LICENSE("GPL");

driver Code

测试用例:

 #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h> /* thirddrvtest
*/
int main(int argc, char **argv)
{
int fd;
unsigned char key_val; fd = open("/dev/buttons", O_RDWR);
if (fd < )
{
printf("can't open!\n");
} while ()
{
read(fd, &key_val, );
printf("key_val = 0x%x\n", key_val);
} return ;
}

drvtest Code

以上是采用中断方式的写法。

下面学习了poll机制后,做了改进:

drv Code

测试用例如下:

 #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h> /* forthdrvtest
*/
int main(int argc, char **argv)
{
int fd;
unsigned char key_val;
int ret; struct pollfd fds[]; fd = open("/dev/buttons", O_RDWR);
if (fd < )
{
printf("can't open!\n");
} fds[].fd = fd;
fds[].events = POLLIN;
while ()
{
ret = poll(fds, , );
if (ret == )
{
printf("time out\n");
}
else
{
read(fd, &key_val, );
printf("key_val = 0x%x\n", key_val);
}
} return ;
}

drvtest view

小总结:

  查询方式:消耗资源,高达90%以上

  中断:read()

  poll:指定超时时间

都是应用程序主动去读。

下面通过异步通知来改进。

先做一个简单的测试

 #include <stdio.h>
#include <signal.h>
void my_signal_fun(int signum)
{
static int cnt = ;
printf("signal = %d,%d times",signum,++cnt);
} int main(int argc,char *argv)
{
signal(SIGUSR1,my_signal_fun);
while()
{
sleep();
}
return ;
}

signal Code

要点:  1.注册一个信号处理函数

     2.谁发?发给谁?怎么发?

目标:按下按键是,驱动程序通知应用程序,

    1、应用程序要注册信号处理函数。

    2、驱动程序发信号给应用程序  (应用程序要告诉驱动PID号)

    3、怎么发?

用到signal、fcntl

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/poll.h> static struct class *fifthdrv_class;
static struct class_device *fifthdrv_class_dev; volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat; volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat; static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /* 中断事件标志, 中断服务程序将它置1,fifth_drv_read将它清0 */
static volatile int ev_press = ; static struct fasync_struct *button_async; struct pin_desc{
unsigned int pin;
unsigned int key_val;
}; /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
static unsigned char key_val; struct pin_desc pins_desc[] = {
{S3C2410_GPF0, 0x01},
{S3C2410_GPF2, 0x02},
{S3C2410_GPG3, 0x03},
{S3C2410_GPG11, 0x04},
}; /*
* 确定按键值
*/
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
struct pin_desc * pindesc = (struct pin_desc *)dev_id;
unsigned int pinval; pinval = s3c2410_gpio_getpin(pindesc->pin); if (pinval)
{
/* 松开 */
key_val = 0x80 | pindesc->key_val;
}
else
{
/* 按下 */
key_val = pindesc->key_val;
} ev_press = ; /* 表示中断发生了 */
wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ kill_fasync (&button_async, SIGIO, POLL_IN); return IRQ_RETVAL(IRQ_HANDLED);
} static int fifth_drv_open(struct inode *inode, struct file *file)
{
/* 配置GPF0,2为输入引脚 */
/* 配置GPG3,11为输入引脚 */
request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[]);
request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[]);
request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[]);
request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[]); return ;
} ssize_t fifth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
if (size != )
return -EINVAL; /* 如果没有按键动作, 休眠 */
wait_event_interruptible(button_waitq, ev_press); /* 如果有按键动作, 返回键值 */
copy_to_user(buf, &key_val, );
ev_press = ; return ;
} int fifth_drv_close(struct inode *inode, struct file *file)
{
free_irq(IRQ_EINT0, &pins_desc[]);
free_irq(IRQ_EINT2, &pins_desc[]);
free_irq(IRQ_EINT11, &pins_desc[]);
free_irq(IRQ_EINT19, &pins_desc[]);
return ;
} static unsigned fifth_drv_poll(struct file *file, poll_table *wait)
{
unsigned int mask = ;
poll_wait(file, &button_waitq, wait); // 不会立即休眠 if (ev_press)
mask |= POLLIN | POLLRDNORM; return mask;
} static int fifth_drv_fasync (int fd, struct file *filp, int on)
{
printk("driver: fifth_drv_fasync\n");
return fasync_helper (fd, filp, on, &button_async);
} static struct file_operations sencod_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = fifth_drv_open,
.read = fifth_drv_read,
.release = fifth_drv_close,
.poll = fifth_drv_poll,
.fasync = fifth_drv_fasync,
}; int major;
static int fifth_drv_init(void)
{
major = register_chrdev(, "fifth_drv", &sencod_drv_fops); fifthdrv_class = class_create(THIS_MODULE, "fifth_drv"); fifthdrv_class_dev = class_device_create(fifthdrv_class, NULL, MKDEV(major, ), NULL, "buttons"); /* /dev/buttons */ gpfcon = (volatile unsigned long *)ioremap(0x56000050, );
gpfdat = gpfcon + ; gpgcon = (volatile unsigned long *)ioremap(0x56000060, );
gpgdat = gpgcon + ; return ;
} static void fifth_drv_exit(void)
{
unregister_chrdev(major, "fifth_drv");
class_device_unregister(fifthdrv_class_dev);
class_destroy(fifthdrv_class);
iounmap(gpfcon);
iounmap(gpgcon);
return ;
} module_init(fifth_drv_init); module_exit(fifth_drv_exit); MODULE_LICENSE("GPL");

driver Code

测试用例:

 #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h> /* fifthdrvtest
*/
int fd; void my_signal_fun(int signum)
{
unsigned char key_val;
read(fd, &key_val, );
printf("key_val: 0x%x\n", key_val);
} int main(int argc, char **argv)
{
unsigned char key_val;
int ret;
int Oflags; signal(SIGIO, my_signal_fun); fd = open("/dev/buttons", O_RDWR);
if (fd < )
{
printf("can't open!\n");
} fcntl(fd, F_SETOWN, getpid()); Oflags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, Oflags | FASYNC); while ()
{
sleep();
} return ;
}

drvtest code

fcntl(fd, F_SETOWN, getpid());   //告诉内核,发给谁

Oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, Oflags | FASYNC); //改变fasync标记,最终会调用驱动的faync>faync_helper : 初始化/释放fasync_struct

目前,我们这个按键驱动程序已经较完善。

现在我们想让这个驱动同一时间只有一个app可以打开。

如果用一个变量来控制的话,会存在漏洞,分析:

linux是一个多任务的系统,修改一个值的时候有三个过程:1、读出;2、修改;3、写回

修改代码不是一个原子操作,引入原子变量。

笔记如下:

1. 原子操作
原子操作指的是在执行过程中不会被别的代码路径所中断的操作。
常用原子操作函数举例:
atomic_t v = ATOMIC_INIT(0); //定义原子变量v并初始化为0
atomic_read(atomic_t *v); //返回原子变量的值
void atomic_inc(atomic_t *v); //原子变量增加1
void atomic_dec(atomic_t *v); //原子变量减少1
int atomic_dec_and_test(atomic_t *v); //自减操作后测试其是否为0,为0则返回true,否则返回false。

2. 信号量
信号量(semaphore)是用于保护临界区的一种常用方法,只有得到信号量的进程才能执行临界区代码。
当获取不到信号量时,进程进入休眠等待状态。

定义信号量
struct semaphore sem;
初始化信号量
void sema_init (struct semaphore *sem, int val);
void init_MUTEX(struct semaphore *sem);//初始化为0

static DECLARE_MUTEX(button_lock); //定义互斥锁

获得信号量
void down(struct semaphore * sem);
int down_interruptible(struct semaphore * sem);
int down_trylock(struct semaphore * sem);
释放信号量
void up(struct semaphore * sem);

3. 阻塞
阻塞操作
是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。
被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。

非阻塞操作
进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止。

fd = open("...", O_RDWR | O_NONBLOCK);

加上按键防抖动就完美:

 #include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/poll.h> static struct class *sixthdrv_class;
static struct class_device *sixthdrv_class_dev; volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat; volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat; static struct timer_list buttons_timer; static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /* 中断事件标志, 中断服务程序将它置1,sixth_drv_read将它清0 */
static volatile int ev_press = ; static struct fasync_struct *button_async; struct pin_desc{
unsigned int pin;
unsigned int key_val;
}; /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
static unsigned char key_val; struct pin_desc pins_desc[] = {
{S3C2410_GPF0, 0x01},
{S3C2410_GPF2, 0x02},
{S3C2410_GPG3, 0x03},
{S3C2410_GPG11, 0x04},
}; static struct pin_desc *irq_pd; //static atomic_t canopen = ATOMIC_INIT(1); //定义原子变量并初始化为1 static DECLARE_MUTEX(button_lock); //定义互斥锁 /*
* 确定按键值
*/
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
/* 10ms后启动定时器 */
irq_pd = (struct pin_desc *)dev_id;
mod_timer(&buttons_timer, jiffies+HZ/);
return IRQ_RETVAL(IRQ_HANDLED);
} static int sixth_drv_open(struct inode *inode, struct file *file)
{
#if 0
if (!atomic_dec_and_test(&canopen))
{
atomic_inc(&canopen);
return -EBUSY;
}
#endif if (file->f_flags & O_NONBLOCK)
{
if (down_trylock(&button_lock))
return -EBUSY;
}
else
{
/* 获取信号量 */
down(&button_lock);
} /* 配置GPF0,2为输入引脚 */
/* 配置GPG3,11为输入引脚 */
request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[]);
request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[]);
request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[]);
request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[]); return ;
} ssize_t sixth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
if (size != )
return -EINVAL; if (file->f_flags & O_NONBLOCK)
{
if (!ev_press)
return -EAGAIN;
}
else
{
/* 如果没有按键动作, 休眠 */
wait_event_interruptible(button_waitq, ev_press);
} /* 如果有按键动作, 返回键值 */
copy_to_user(buf, &key_val, );
ev_press = ; return ;
} int sixth_drv_close(struct inode *inode, struct file *file)
{
//atomic_inc(&canopen);
free_irq(IRQ_EINT0, &pins_desc[]);
free_irq(IRQ_EINT2, &pins_desc[]);
free_irq(IRQ_EINT11, &pins_desc[]);
free_irq(IRQ_EINT19, &pins_desc[]);
up(&button_lock);
return ;
} static unsigned sixth_drv_poll(struct file *file, poll_table *wait)
{
unsigned int mask = ;
poll_wait(file, &button_waitq, wait); // 不会立即休眠 if (ev_press)
mask |= POLLIN | POLLRDNORM; return mask;
} static int sixth_drv_fasync (int fd, struct file *filp, int on)
{
printk("driver: sixth_drv_fasync\n");
return fasync_helper (fd, filp, on, &button_async);
} static struct file_operations sencod_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = sixth_drv_open,
.read = sixth_drv_read,
.release = sixth_drv_close,
.poll = sixth_drv_poll,
.fasync = sixth_drv_fasync,
}; int major; static void buttons_timer_function(unsigned long data)
{
struct pin_desc * pindesc = irq_pd;
unsigned int pinval; if (!pindesc)
return; pinval = s3c2410_gpio_getpin(pindesc->pin); if (pinval)
{
/* 松开 */
key_val = 0x80 | pindesc->key_val;
}
else
{
/* 按下 */
key_val = pindesc->key_val;
} ev_press = ; /* 表示中断发生了 */
wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ kill_fasync (&button_async, SIGIO, POLL_IN);
} static int sixth_drv_init(void)
{
init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function;
//buttons_timer.expires = 0;
add_timer(&buttons_timer); major = register_chrdev(, "sixth_drv", &sencod_drv_fops); sixthdrv_class = class_create(THIS_MODULE, "sixth_drv"); /* 为了让mdev根据这些信息来创建设备节点 */
sixthdrv_class_dev = class_device_create(sixthdrv_class, NULL, MKDEV(major, ), NULL, "buttons"); /* /dev/buttons */ gpfcon = (volatile unsigned long *)ioremap(0x56000050, );
gpfdat = gpfcon + ; gpgcon = (volatile unsigned long *)ioremap(0x56000060, );
gpgdat = gpgcon + ; return ;
} static void sixth_drv_exit(void)
{
unregister_chrdev(major, "sixth_drv");
class_device_unregister(sixthdrv_class_dev);
class_destroy(sixthdrv_class);
iounmap(gpfcon);
iounmap(gpgcon);
return ;
} module_init(sixth_drv_init); module_exit(sixth_drv_exit); MODULE_LICENSE("GPL");

driver Code

测试程序:

 #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h> /* sixthdrvtest
*/
int fd; void my_signal_fun(int signum)
{
unsigned char key_val;
read(fd, &key_val, );
printf("key_val: 0x%x\n", key_val);
} int main(int argc, char **argv)
{
unsigned char key_val;
int ret;
int Oflags; //signal(SIGIO, my_signal_fun); fd = open("/dev/buttons", O_RDWR);
if (fd < )
{
printf("can't open!\n");
return -;
} //fcntl(fd, F_SETOWN, getpid()); //Oflags = fcntl(fd, F_GETFL); //fcntl(fd, F_SETFL, Oflags | FASYNC); while ()
{
ret = read(fd, &key_val, );
printf("key_val: 0x%x, ret = %d\n", key_val, ret);
//sleep(5);
} return ;
}

驱动:

linux-2.6内核驱动学习——jz2440之按键的更多相关文章

  1. linux-2.6内核驱动学习——jz2440之输入子系统

    如果按照上一篇记录的那样,只有本公司的人或者自己才能使用驱动.想写出一个通用的驱动程序,让其他应用程序来无缝移植,需要使用现成的驱动——输入子系统. /drivers/input/input.c #d ...

  2. Linux内核驱动学习(八)GPIO驱动模拟输出PWM

    文章目录 前言 原理图 IO模拟输出PWM 设备树 驱动端 调试信息 实验结果 附录 前言 上一篇的学习中介绍了如何在用户空间直接操作GPIO,并写了一个脚本可以产生PWM.本篇的学习会将写一个驱动操 ...

  3. linux内核驱动学习指南

    1. 参考链接 小白的博客 ONE_Tech 你为什么看不懂Linux内核驱动源码? 求教怎么学习linux内核驱动

  4. Linux内核驱动学习(六)GPIO之概览

    文章目录 前言 功能 如何使用 设备树 API 总结 前言 GPIO(General Purpose Input/Output)通用输入/输出接口,是十分灵活软件可编程的接口,功能强大,十分常用,SO ...

  5. Linux内核驱动学习(三)字符型设备驱动之初体验

    Linux字符型设备驱动之初体验 文章目录 Linux字符型设备驱动之初体验 前言 框架 字符型设备 程序实现 cdev kobj owner file_operations dev_t 设备注册过程 ...

  6. Linux内核驱动学习(十)Input子系统详解

    文章目录 前言 框架 如何实现`input device` 设备驱动? 头文件 注册input_dev设备 上报按键值 dev->open()和dev->close() 其他事件类型,处理 ...

  7. Linux内核驱动学习(九)GPIO外部输入的处理

    文章目录 前言 设备树 两个结构体 gpio_platform_data gpio_demo_device 两种方式 轮询 外部中断 总结 附录 前言 前面是如何操作GPIO进行输出,这里我重新实现了 ...

  8. Linux内核驱动学习(一)编写最简单Linux内核模块HelloWorld

    文章目录 准备工作 什么是内核模块 编写 hello.c 模块编译 相关指令 测试结果 模块加载 模块卸载 准备工作 在进行以下操作前,首先我准备了一台电脑,并且安装了虚拟机,系统是Ubuntu16. ...

  9. Linux内核驱动学习(七)应用层直接操作GPIO

    文章目录 简介 原理图 节点 设置为输出 设置为输入 映射关系 debugfs pwm demo 简介 前面通过libgpio的方式介绍了内核空间对GPIO进行操作的接口,其做了较好的封装,同时Lin ...

随机推荐

  1. Vue 框架-07-循环指令 v-for,和模板的使用

    Vue 框架-07-循环指令 v-for,和模板的使用 本章主要是写一些小实例,记录代码,想要更详细的话,请查看 官方文档:https://cn.vuejs.org/v2/guide/#%E6%9D% ...

  2. idea插件Lombok

    在spring boot 中,我们可以使用@Data标签,这样就不需要手动添加getter/setter方法了,但Idea会报错. 此时,我们需要安装Lombok插件,安装好插件后便可以解决这个问题. ...

  3. 3The superclass “javax.servlet.http.HttpServlet" was not found on the Java Build Path 之一

    另外一篇短文里还有第三种解决方案,查看请点击这里 1.异常信息 创建maven web项目时,出现 The superclass “javax.servlet.http.HttpServlet&quo ...

  4. Storm并行度

    1.Storm并行度相关的概念 Storm集群有很多节点,按照类型分为nimbus(主节点).supervisor(从节点),在conf/storm.yaml中配置了一个supervisor有多个槽( ...

  5. 带你从零学ReactNative开发跨平台App开发[expo 打包发布](八)

    ReactNative跨平台开发系列教程: 带你从零学ReactNative开发跨平台App开发(一) 带你从零学ReactNative开发跨平台App开发(二) 带你从零学ReactNative开发 ...

  6. 结对编程的感想&收获

    关于结对编程的感想.感受,见我的另一篇随笔——<构建之法>结对编程   感想 下面我来谈谈本次结对编程的收获以及发现的问题 收获 ①这是我人生中第一次做UI界面设计,刚拿到这个题目还是比较 ...

  7. 测试、集成等领域最好的Java工具

    无论你是刚入门,还是进行了一段时间的开发,使用合适的工具编程都会让你事半功倍,它能够让你更快的编写代码,能够快速及时的为你识别出Bug,能够让你的代码质量更上一层楼. 如果你选择的编程语言是Java, ...

  8. Oracle EBS 数据访问权限集

    SELECT frv.responsibility_name, fpo.profile_option_name, fpo.user_profile_option_name, fpv.profile_o ...

  9. Asp.net单点登录解决方案

    原文出处:http://www.cnblogs.com/wu-jian 主站:Passport集中验证服务器,DEMO中为:http://www.passport.com/ 分站:http://www ...

  10. 用tableView实现的一种加载数据的布局

    用tableView实现的一种加载数据的布局 此博文是应朋友之邀解决他的业务逻辑问题 效果: 素材: 源码: ImageCell.h 与 ImageCell.m // // ImageCell.h / ...