关键词:hid、hidraw、usbhid、hidp等等。

下面首先介绍hidraw设备主要用途,然后简要分析hidraw设备驱动(但是不涉及到相关USB/Bluwtooth驱动),最后分析用户空间接口并实例。

1. hidraw介绍

在内核Documentation/hid/hidraw.txt中对hidra设备进行介绍,以及和hiddev的区别。

hidraw提供了一个通过USB/Bluetooth接口的裸数据接口,它和hiddev的区别体现在其数据不经过HID parser解析,而是直接将数据传输。

如果用户空间应用程序知道怎么恰当和硬件设备通信,和能够手动构建HID 报表,那么hidraw应该被使用。这通常是在用户控件驱动自定义HID 设备的时候。

Hidraw与不符合规范的HID 设备通信也是有利 的,这些设备以一种不符合报表描述符不一致的方式发送和接收数据。因为Hiddev解析器通过他发送和接收报表,检测设备的报表描述符,这样的通信是不可能使用hiddev。Hidraw是唯一的选择,为这些不兼容的设备编写一个定制的内核驱动程序。

Hidraw一个好处是用户空间应用程序使用独立的底层硬件类型。当前,hidraw是通过bluetooth 和 usb实现。在将来,随着硬件总线的发展,hidraw将支持更多的类型。

2. hidraw驱动

hidraw也是hid类设备,hidraw_init()被hid_init调用,在调用之前创建了虚拟的hid总线hid_bus_type,并且创建/sys/kernel/debug/hid调试接口。

static int __init hid_init(void)
{
int ret;
...
ret = bus_register(&hid_bus_type);
...
ret = hidraw_init();
if (ret)
goto err_bus; hid_debug_init();
...
} static void __exit hid_exit(void)
{
hid_debug_exit();
hidraw_exit();
bus_unregister(&hid_bus_type);
}

struct hidraw是hidraw设备实例,struct hid_device是hid框架下表示hid设备的实例。

hidraw_list是hidraw设备一次传输的实例。

struct hidraw {
unsigned int minor;----------------------从设备号。
int exist;-------------------------------表示设备是否连接。
int open;--------------------------------表示设备open计数。
wait_queue_head_t wait;
struct hid_device *hid;------------------对应hid设备实例。
struct device *dev;
spinlock_t list_lock;
struct list_head list;
}; struct hidraw_report {
__u8 *value;
int len;
}; struct hidraw_list {
structhidraw_reportbuffer[HIDRAW_BUFFER_SIZE];
int head;
int tail;
struct fasync_struct *fasync;
struct hidraw *hidraw;
struct list_head node;
struct mutex read_mutex;
};

2.1 hid总线

hid_bus_type提供了hid总线上进行match、probe、remove的接口,以及uevent上报接口。

static struct bus_type hid_bus_type = {
.name = "hid",
.dev_groups = hid_dev_groups,
.match = hid_bus_match,
.probe = hid_device_probe,
.remove = hid_device_remove,
.uevent =hid_uevent,
}; static int hid_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct hid_device *hdev = to_hid_device(dev); if (add_uevent_var(env, "HID_ID=%04X:%08X:%08X",
hdev->bus, hdev->vendor, hdev->product))
return -ENOMEM; if (add_uevent_var(env, "HID_NAME=%s", hdev->name))
return -ENOMEM; if (add_uevent_var(env, "HID_PHYS=%s", hdev->phys))
return -ENOMEM; if (add_uevent_var(env, "HID_UNIQ=%s", hdev->uniq))
return -ENOMEM; if (add_uevent_var(env, "MODALIAS=hid:b%04Xg%04Xv%08Xp%08X",
hdev->bus, hdev->group, hdev->vendor, hdev->product))
return -ENOMEM; return ;
}

2.1 hidraw初始化

hidraw首先创建了字符设备hidraw_class,并和字符设备hidraw_cdev绑定,对应的文件操作函数集为hidraw_ops

其中字符设备hidraw_cdev从设备好范围为0~63,后面创建设备通过hidraw_class即可。

int __init hidraw_init(void)
{
int result;
dev_t dev_id; result = alloc_chrdev_region(&dev_id, HIDRAW_FIRST_MINOR,
HIDRAW_MAX_DEVICES, "hidraw");---------------------------------从设备号0~63。
if (result < ) {
pr_warn("can't get major number\n");
goto out;
} hidraw_major = MAJOR(dev_id); hidraw_class = class_create(THIS_MODULE, "hidraw");------------------创建hidraw设备类。
if (IS_ERR(hidraw_class)) {
result = PTR_ERR(hidraw_class);
goto error_cdev;
} cdev_init(&hidraw_cdev, &hidraw_ops);------------------------------初始化一个字符设备hidraw_dev,操作函数集为hidraw_ops。
result = cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES);
if (result < )
goto error_class; printk(KERN_INFO "hidraw: raw HID events driver (C) Jiri Kosina\n");
out:
return result; error_class:
class_destroy(hidraw_class);
error_cdev:
unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
goto out;
} void hidraw_exit(void)
{
dev_t dev_id = MKDEV(hidraw_major, ); cdev_del(&hidraw_cdev);
class_destroy(hidraw_class);
unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES); }

对于hidraw设备的操作,都在hidraw_ops中体现,包括open/close/read/write/ioctl,以及poll、fasync。

static const struct file_operations hidraw_ops = {
.owner = THIS_MODULE,
.read =hidraw_read,
.write =hidraw_write,
.poll =hidraw_poll,
.open =hidraw_open,
.release =hidraw_release,
.unlocked_ioctl =hidraw_ioctl,
.fasync =hidraw_fasync,
#ifdef CONFIG_COMPAT
.compat_ioctl =hidraw_ioctl,
#endif
.llseek = noop_llseek,
}; static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
struct hidraw_list *list = file->private_data;
int ret = , len;
DECLARE_WAITQUEUE(wait, current); mutex_lock(&list->read_mutex); while (ret == ) {
if (list->head == list->tail) {
add_wait_queue(&list->hidraw->wait, &wait);
set_current_state(TASK_INTERRUPTIBLE); while (list->head == list->tail) {
if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
}
if (!list->hidraw->exist) {
ret = -EIO;
break;
}
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
break;
} /* allow O_NONBLOCK to work well from other threads */
mutex_unlock(&list->read_mutex);
schedule();
mutex_lock(&list->read_mutex);
set_current_state(TASK_INTERRUPTIBLE);
} set_current_state(TASK_RUNNING);
remove_wait_queue(&list->hidraw->wait, &wait);
} if (ret)
goto out; len = list->buffer[list->tail].len > count ?
count : list->buffer[list->tail].len; if (list->buffer[list->tail].value) {
if (copy_to_user(buffer, list->buffer[list->tail].value, len)) {
ret = -EFAULT;
goto out;
}
ret = len;
} kfree(list->buffer[list->tail].value);
list->buffer[list->tail].value = NULL;
list->tail = (list->tail + ) & (HIDRAW_BUFFER_SIZE - );
}
out:
mutex_unlock(&list->read_mutex);
return ret;
} static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, size_t count, unsigned char report_type)
{
unsigned int minor = iminor(file_inode(file));
struct hid_device *dev;
__u8 *buf;
int ret = ; if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
ret = -ENODEV;
goto out;
} dev = hidraw_table[minor]->hid;-----------------------------------------------根据minor号找到对应hidraw设备的hid_device。
...
buf = memdup_user(buffer, count);
if (IS_ERR(buf)) {
ret = PTR_ERR(buf);
goto out;
} if ((report_type == HID_OUTPUT_REPORT) &&
!(dev->quirks & HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP)) {
ret = hid_hw_output_report(dev, buf, count);
if (ret != -ENOSYS)
goto out_free;
} ret = hid_hw_raw_request(dev, buf[], buf, count, report_type,
HID_REQ_SET_REPORT);----------------------------------------------调用实际硬件接口发送report,比如usb、bluetooth等。 out_free:
kfree(buf);
out:
return ret;
} static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
ssize_t ret;
mutex_lock(&minors_lock);
ret =hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT);
mutex_unlock(&minors_lock);
return ret;
} static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type)
{
unsigned int minor = iminor(file_inode(file));
struct hid_device *dev;
__u8 *buf;
int ret = , len;
unsigned char report_number; dev = hidraw_table[minor]->hid;
...
buf = kmalloc(count * sizeof(__u8), GFP_KERNEL);
if (!buf) {
ret = -ENOMEM;
goto out;
} if (copy_from_user(&report_number, buffer, )) {
ret = -EFAULT;
goto out_free;
} ret = hid_hw_raw_request(dev, report_number, buf, count, report_type,
HID_REQ_GET_REPORT);---------------------------------------------从硬件接口接收数据。
...
if (copy_to_user(buffer, buf, len)) {
ret = -EFAULT;
goto out_free;
} ret = len; out_free:
kfree(buf);
out:
return ret;
} static unsigned int hidraw_poll(struct file *file, poll_table *wait)
{
struct hidraw_list *list = file->private_data; poll_wait(file, &list->hidraw->wait, wait);
if (list->head != list->tail)
return POLLIN | POLLRDNORM;
if (!list->hidraw->exist)
return POLLERR | POLLHUP;
return ;
} static int hidraw_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct hidraw *dev;
struct hidraw_list *list;
unsigned long flags;
int err = ; if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) {
err = -ENOMEM;
goto out;
} mutex_lock(&minors_lock);
if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
err = -ENODEV;
goto out_unlock;
} dev = hidraw_table[minor];
if (!dev->open++) {
err = hid_hw_power(dev->hid, PM_HINT_FULLON);--------------调用底层设备即具体接口的power()函数,hdev->ll_driver->power()。
if (err < ) {
dev->open--;
goto out_unlock;
} err = hid_hw_open(dev->hid);-------------------------------调用底层设备的open()函数,hdev->ll_driver->open()。
if (err < ) {
hid_hw_power(dev->hid, PM_HINT_NORMAL);
dev->open--;
goto out_unlock;
}
} list->hidraw = hidraw_table[minor];
mutex_init(&list->read_mutex);
spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags);
list_add_tail(&list->node, &hidraw_table[minor]->list);
spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags);
file->private_data = list;
...
} static int hidraw_fasync(int fd, struct file *file, int on)
{
struct hidraw_list *list = file->private_data; return fasync_helper(fd, file, on, &list->fasync);--------------发送异步通知信号。
} static void drop_ref(struct hidraw *hidraw, int exists_bit)
{
if (exists_bit) {
hidraw->exist = ;
if (hidraw->open) {
hid_hw_close(hidraw->hid);
wake_up_interruptible(&hidraw->wait);
}
device_destroy(hidraw_class,
MKDEV(hidraw_major, hidraw->minor));
} else {
--hidraw->open;---------------------------------------------打开计算减1。
}
if (!hidraw->open) {--------------------------------------------当计数为0后,开始释放资源。
if (!hidraw->exist) {
hidraw_table[hidraw->minor] = NULL;
kfree(hidraw);
} else {
/* close device for last reader */
hid_hw_power(hidraw->hid, PM_HINT_NORMAL);
hid_hw_close(hidraw->hid);
}
}
} static int hidraw_release(struct inode * inode, struct file * file)
{
unsigned int minor = iminor(inode);
struct hidraw_list *list = file->private_data;
unsigned long flags; mutex_lock(&minors_lock); spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags);
list_del(&list->node);
spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags);
kfree(list); drop_ref(hidraw_table[minor], ); mutex_unlock(&minors_lock);
return ;
} static long hidraw_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct inode *inode = file_inode(file);
unsigned int minor = iminor(inode);
long ret = ;
struct hidraw *dev;
void __user *user_arg = (void __user*) arg; mutex_lock(&minors_lock);
dev = hidraw_table[minor];
if (!dev) {
ret = -ENODEV;
goto out;
} switch (cmd) {
case HIDIOCGRDESCSIZE:----------------------------------------------Get report descriptor size。
if (put_user(dev->hid->rsize, (int __user *)arg))
ret = -EFAULT;
break; case HIDIOCGRDESC:--------------------------------------------------Get report descriptor。
{
__u32 len; if (get_user(len, (int __user *)arg))
ret = -EFAULT;
else if (len > HID_MAX_DESCRIPTOR_SIZE - )
ret = -EINVAL;
else if (copy_to_user(user_arg + offsetof(
struct hidraw_report_descriptor,
value[]),
dev->hid->rdesc,
min(dev->hid->rsize, len)))
ret = -EFAULT;
break;
}
case HIDIOCGRAWINFO:------------------------------------------------Get raw info,包括bus类型,vid和pid。
{
struct hidraw_devinfo dinfo; dinfo.bustype = dev->hid->bus;
dinfo.vendor = dev->hid->vendor;
dinfo.product = dev->hid->product;
if (copy_to_user(user_arg, &dinfo, sizeof(dinfo)))
ret = -EFAULT;
break;
}
default:
{
struct hid_device *hid = dev->hid;
if (_IOC_TYPE(cmd) != 'H') {
ret = -EINVAL;
break;
} if (_IOC_NR(cmd) == _IOC_NR(HIDIOCSFEATURE())) {-------------Send a feature report。
int len = _IOC_SIZE(cmd);
ret = hidraw_send_report(file, user_arg, len, HID_FEATURE_REPORT);
break;
}
if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGFEATURE())) {-------------Get a feature report。
int len = _IOC_SIZE(cmd);
ret = hidraw_get_report(file, user_arg, len, HID_FEATURE_REPORT);
break;
} /* Begin Read-only ioctls. */
if (_IOC_DIR(cmd) != _IOC_READ) {
ret = -EINVAL;
break;
} if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWNAME())) {--------------Get raw name。
int len = strlen(hid->name) + ;
if (len > _IOC_SIZE(cmd))
len = _IOC_SIZE(cmd);
ret = copy_to_user(user_arg, hid->name, len) ?
-EFAULT : len;
break;
} if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWPHYS())) {---------------Get physical address。
int len = strlen(hid->phys) + ;
if (len > _IOC_SIZE(cmd))
len = _IOC_SIZE(cmd);
ret = copy_to_user(user_arg, hid->phys, len) ?
-EFAULT : len;
break;
}
} ret = -ENOTTY;
}
out:
mutex_unlock(&minors_lock);
return ret;
}

由于一个系统下可能存在多个hidraw设备,常通过HIDIOCGRAWINFO获取信息,判断对应的hidraw设备。

2.3 创建hidraw设备

int hidraw_connect(struct hid_device *hid)
{
int minor, result;
struct hidraw *dev; /* we accept any HID device, all applications */ dev = kzalloc(sizeof(struct hidraw), GFP_KERNEL);
if (!dev)
return -ENOMEM; result = -EINVAL; mutex_lock(&minors_lock); for (minor = ; minor < HIDRAW_MAX_DEVICES; minor++) {--------------------分配hidraw设备的minor号,并将dev赋给hidraw_table[]。
if (hidraw_table[minor])
continue;
hidraw_table[minor] = dev;
result = ;
break;
} if (result) {
mutex_unlock(&minors_lock);
kfree(dev);
goto out;
} dev->dev = device_create(hidraw_class, &hid->dev, MKDEV(hidraw_major, minor),
NULL, "%s%d", "hidraw", minor);------------------------------创建/dev/hidrawX设备节点。 if (IS_ERR(dev->dev)) {
hidraw_table[minor] = NULL;
mutex_unlock(&minors_lock);
result = PTR_ERR(dev->dev);
kfree(dev);
goto out;
} init_waitqueue_head(&dev->wait);
spin_lock_init(&dev->list_lock);
INIT_LIST_HEAD(&dev->list); dev->hid = hid;
dev->minor = minor; dev->exist = ;
hid->hidraw = dev; mutex_unlock(&minors_lock);
out:
return result; }
EXPORT_SYMBOL_GPL(hidraw_connect); void hidraw_disconnect(struct hid_device *hid)
{
struct hidraw *hidraw = hid->hidraw; mutex_lock(&minors_lock); drop_ref(hidraw, ); mutex_unlock(&minors_lock);
}
EXPORT_SYMBOL_GPL(hidraw_disconnect);

所以hidraw的驱动分为两部分,一是hidraw_init()发起的整个hidraw设备驱动的初始化,二是底层驱动检测到hidraw设备后通过hidraw_connect()/hidraw_disconnect()创建或者销毁设备。

当USB/Bluetooth检查到设备是hidraw类型之后,就会调用hidraw提供的接口创建设备,表现为创建节点/dev/hidrawx。

后续用户空间程序对/dev/hidrawx进行read/write/ioctl等操作。

3. hidraw测试程序

内核提供了一个示例程序hid-example.c,下面结合内核代码简单分析一下。遍历/dev下所有的hidraw设备,然后读取信息。

/* Linux */
#include <linux/types.h>
#include <linux/input.h>
#include <linux/hidraw.h>
#include <dirent.h> #ifndef HIDIOCSFEATURE
#warning Please have your distro update the userspace kernel headers
#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len)
#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len)
#endif /* Unix */
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h> /* C */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h> const char *bus_str(int bus); int main(int argc, char **argv)
{
int fd;
int i, res, desc_size = ;
char buf[];
struct hidraw_report_descriptor rpt_desc;
struct hidraw_devinfo info;
char device[] = "/dev/hidraw0";
DIR *dir = NULL;
struct dirent *ptr; /* Open dir /dev. */
dir = opendir("/dev");-------------------------------------------------打开/dev目录。
if(!dir) {
perror("Popen dir failed...");
return -ENOENT;
} /* */
while(ptr = readdir(dir)) {--------------------------------------------遍历/dev目录下所有的文件。
if(ptr->d_type != DT_CHR)------------------------------------------判断设备是否为字符设备,其他设备包括DT_UNKNOWN/DT_FIFO/DT_CHR DT_DIR/DT_BLK/DT_REG/DT_LNK/DT_SOCK/DT_WHT。
continue;
if(!strncmp(ptr->d_name, "hidraw", )) {

            snprintf(device, sizeof(device), "%s/%s", "/dev", ptr->d_name);

            /* Open the Device with non-blocking reads. In real life,
don't use a hard coded path; use libudev instead. */
fd = open(device, O_RDWR|O_NONBLOCK);--------------------------打开hidraw设备,对应内核的hidraw_open()
if (fd < ) {
printf("Unable to open device %s.\n", device);
return ;
} memset(&rpt_desc, 0x0, sizeof(rpt_desc));
memset(&info, 0x0, sizeof(info));
memset(buf, 0x0, sizeof(buf)); printf("\n\n=================================Device %s info=================================\n", device);
/* Get Raw Name */
res = ioctl(fd, HIDIOCGRAWNAME(), buf);---------------------对应hidraw_ioctl()的HIDIOCGRAWNAME
if (res < )
perror("HIDIOCGRAWNAME");
else
printf("Raw Name: %s\n", buf); /* Get Physical Location */
res = ioctl(fd, HIDIOCGRAWPHYS(), buf);---------------------对应HIDIOCGRAWPHYS
if (res < )
perror("HIDIOCGRAWPHYS");
else
printf("Raw Phys: %s\n", buf); /* Get Raw Info */
res = ioctl(fd, HIDIOCGRAWINFO, &info);-------------------------对应HIDIOCGRAWINFO
if (res < ) {
perror("HIDIOCGRAWINFO");
} else {
printf("Raw Info:\n");
printf("\tbustype: %d (%s)\n",
info.bustype, bus_str(info.bustype));
printf("\tvendor: 0x%04hx\n", info.vendor);
printf("\tproduct: 0x%04hx\n", info.product);
} /* Get Report Descriptor Size */
res = ioctl(fd, HIDIOCGRDESCSIZE, &desc_size);------------------对应HIDIOCGRDESCSIZE if (res < )
perror("HIDIOCGRDESCSIZE");
else
printf("Report Descriptor Size: %d\n", desc_size); /* Get Report Descriptor */
rpt_desc.size = desc_size;
res = ioctl(fd, HIDIOCGRDESC, &rpt_desc);------------------------对应HIDIOCGRDESC
if (res < ) {
perror("HIDIOCGRDESC");
} else {
printf("Report Descriptor:\n");
for (i = ; i < rpt_desc.size; i++)
printf("%hhx ", rpt_desc.value[i]);
puts("\n");
} /* Set Feature */
buf[] = 0x9; /* Report Number */
buf[] = 0xff;
buf[] = 0xff;
buf[] = 0xff;
res = ioctl(fd, HIDIOCSFEATURE(), buf);-------------------------对应HIDIOCSFEATURE
if (res < )
perror("HIDIOCSFEATURE");
else
printf("ioctl HIDIOCGFEATURE returned: %d\n", res); /* Get Feature */
buf[] = 0x9; /* Report Number */
res = ioctl(fd, HIDIOCGFEATURE(), buf);-----------------------对应HIDIOCGFEATURE
if (res < ) {
perror("HIDIOCGFEATURE");
} else {
printf("ioctl HIDIOCGFEATURE returned: %d\n", res);
printf("Report data (not containing the report number):\n\t");
for (i = ; i < res; i++)
printf("%hhx ", buf[i]);
puts("\n");
} /* Send a Report to the Device */
buf[] = 0x1; /* Report Number */
buf[] = 0x77;
res = write(fd, buf, );
if (res < ) {
printf("Error: %d\n", errno);
perror("write");
} else {
printf("write() wrote %d bytes\n", res);
} /* Get a report from the device */
res = read(fd, buf, );
if (res < ) {
perror("read");
} else {
printf("read() read %d bytes:\n\t", res);
for (i = ; i < res; i++)
printf("%hhx ", buf[i]);
puts("\n");
} close(fd);
}
}
return ;
} const char *
bus_str(int bus)
{
switch (bus) {
case BUS_USB:
return "USB";
break;
case BUS_HIL:
return "HIL";
break;
case BUS_BLUETOOTH:
return "Bluetooth";
break;
case BUS_VIRTUAL:
return "Virtual";
break;
default:
return "Other";
break;
}

综合来看hidraw设备是hid的一种,传输裸数据,对应的底层硬件可能是USB/Bluetooth等。

通过对hidraw设备的操作,ioctl可以配置hidraw设备,read/write可以对hidraw设备进行读写。

参考文档:

HIDRAW - Raw Access to USB and Bluetooth Human Interface Devices》:包括简要介绍以及hidraw相关API介绍,尤其是ioctl命令。

Linux之访问/dev/hidraw》是对上文的翻译,附加了两个示例。

hidraw设备简要分析的更多相关文章

  1. Android Hal层简要分析

    Android Hal层简要分析 Android Hal层(即 Hardware Abstraction Layer)是Google开发的Android系统里上层应用对底层硬件操作屏蔽的一个软件层次, ...

  2. Activity源码简要分析总结

    Activity源码简要分析总结 摘自参考书籍,只列一下结论: 1. Activity的顶层View是DecorView,而我们在onCreate()方法中通过setContentView()设置的V ...

  3. Google发布SSLv3漏洞简要分析报告

    今天上午,Google发布了一份关于SSLv3漏洞的简要分析报告.根据Google的说法,该漏洞贯穿于所有的SSLv3版本中,利用该漏洞,黑客可以通过中间人攻击等类似的方式(只要劫持到的数据加密两端均 ...

  4. 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(34)-文章发布系统①-简要分析

    原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(34)-文章发布系统①-简要分析 系列目录 最新比较闲,为了学习下Android的开发构建ASP.NET ...

  5. CVE-2015-5122 简要分析(2016.4)

    CVE-2015-5122 简要分析 背景 最近在学习Flash漏洞的分析,其与IE漏洞的分析还是有诸多的不同(不便)之处,折腾了一阵子终于克服了没有符号表.Flash的超时定时器等问题.所以找到了去 ...

  6. Java7中的ForkJoin并发框架初探(中)——JDK中实现简要分析

    原文发表于 2013 年 8 月 28 日 由 三石 根据前文描述的Doug Lea的理论基础,在JDK1.7中已经给出了Fork Join的实现.在Java SE 7的API中,多了ForkJoin ...

  7. [转]Java7中的ForkJoin并发框架初探(中)——JDK中实现简要分析

    详见: http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp85   根据前文描述的Doug Lea的理论基础,在JDK1.7中已经给 ...

  8. ITS简要分析流程(using Qiime)

    Qiime安装 参考资料:http://blog.sina.com.cn/s/blog_83f77c940101h2rp.html Qiime script官方说明http://qiime.org/s ...

  9. Android初级教程通过简要分析“土司”源码,来自实现定义土司理论探讨

    由于系统自带的土司瞬间即逝,而且非常难看.因此我们就希望自定义自己的土司风格.有些实例就是基于自定义土司完成的,例如金山卫士的火箭发射,基本原理就是个土司.但是在做出自己的土司风格之前,还是要简要分析 ...

随机推荐

  1. Vue---记一次通过{{}}获取json数据-页面渲染不出来的坑

    前两天干活儿的时候碰到一个Vue的问题,让我这个菜鸡完全摸不到头脑,需求如下:前端页面点击表格中的某一行的详情按钮,会弹出一个Dialog,然后Dialog中有选项卡,选项卡中再有具体的table来展 ...

  2. javascript数组拆分为三个一组

    首先构建原始数据. var arr = [ {name:'yanggb1',age:'15'}, {name:'yanggb2',age:'16'}, {name:'yanggb3',age:'17' ...

  3. Nacos Cluster Building

    原文链接:https://www.javaspring.net/nacos/nacos-cluster-building Continue to talk about the Nacos build ...

  4. C# -- 模拟扑克牌发牌

    C# -- 模拟扑克牌发牌 1.  User 类: 玩家 public class User { private List<PaperCard> listCard = new List&l ...

  5. Linux逻辑卷的创建

    什么是LVM? LVM(逻辑卷管理器):是Linux环境下对磁盘分区进行管理的一种机制.主要作用是动态分配磁盘分区及调整磁盘分区大小,并且可以让多个分区或者物理硬盘作为一个逻辑卷(相当于一个逻辑硬盘) ...

  6. WebSocket断开原因、心跳机制防止自动断开连接

    1.断开原因 WebSocket断开的原因有很多,最好在WebSocket断开时,将错误打印出来. ws.onclose = function (e) { console.log('websocket ...

  7. Redis 到底是怎么实现“附近的人”这个功能的呢?

    作者简介 万汨,饿了么资深开发工程师.iOS,Go,Java均有涉猎.目前主攻大数据开发.喜欢骑行.爬山. 前言:针对“附近的人”这一位置服务领域的应用场景,常见的可使用PG.MySQL和MongoD ...

  8. RMAN命令详解和常用汇总

    RMAN命令详解和常用汇总转摘汇集,日后使用本文链接:https://blog.csdn.net/EVISWANG/article/details/50448370http://blog.itpub. ...

  9. sql server数据库查询取出重复数据记录

    问题:博主在2011年6月,广东技术师范大学大四的时候,从学校计算机科学学院网站看到招聘信息并到广东中原地产IT部面试,很清楚记得当时的面试题目:怎么从数据库里面查询重复记录. 解决方案:在sql s ...

  10. asp.net实现SQL2005的通知数据缓存

    首先第一步是确保您的 Service Broker 已经激活,激活 Service Broker (Transact-SQL)如下: USE master ; GO ALTER DATABASE Yo ...