v4l2_device

v4l2_device在v4l2框架中充当所有v4l2_subdev的父设备,管理着注册在其下的子设备。以下是v4l2_device结构体原型(去掉了无关的成员):

  1. struct v4l2_device {
  2.  
  3. structlist_head subdevs; //用链表管理注册的subdev
  4.  
  5. charname[V4L2_DEVICE_NAME_SIZE]; //device 名字
  6.  
  7. structkref ref; //引用计数
  8.  
  9. ……
  10. }

可以看出v4l2_device的主要作用是管理注册在其下的子设备,方便系统查找引用到。

V4l2_device的注册和注销:

  1. int v4l2_device_register(struct device*dev, struct v4l2_device *v4l2_dev)
  2.  
  3. static void v4l2_device_release(struct kref *ref)

V4l2_subdev

V4l2_subdev代表子设备,包含了子设备的相关属性和操作。先来看下结构体原型:

  1. struct v4l2_subdev {
  2.  
  3. struct v4l2_device *v4l2_dev; //指向父设备
  4.  
  5. //提供一些控制v4l2设备的接口
  6.  
  7. const struct v4l2_subdev_ops *ops;
  8.  
  9. //向V4L2框架提供的接口函数
  10.  
  11. const struct v4l2_subdev_internal_ops *internal_ops;
  12.  
  13. //subdev控制接口
  14.  
  15. struct v4l2_ctrl_handler *ctrl_handler;
  16.  
  17. /* name must be unique */
  18.  
  19. charname[V4L2_SUBDEV_NAME_SIZE];
  20.  
  21. /*subdev device node */
  22.  
  23. struct video_device *devnode;
  24.  
  25. };

每个子设备驱动都需要实现一个v4l2_subdev结构体,v4l2_subdev可以内嵌到其它结构体中,也可以独立使用。结构体中包含了对子设备操作的成员v4l2_subdev_ops和v4l2_subdev_internal_ops。

v4l2_subdev_ops结构体原型如下:

  1. struct v4l2_subdev_ops {
  2.  
  3. //视频设备通用的操作:初始化、加载FW、上电和RESET等
  4.  
  5. const struct v4l2_subdev_core_ops *core; //tuner特有的操作
  6.  
  7. const struct v4l2_subdev_tuner_ops *tuner; //audio特有的操作
  8.  
  9. const struct v4l2_subdev_audio_ops *audio; //视频设备的特有操作:设置帧率、裁剪图像、开关视频流等
  10.  
  11. const struct v4l2_subdev_video_ops *video;
  12. ……
  13.  
  14. };

视频设备通常需要实现core和video成员,这两个OPS中的操作都是可选的,但是对于视频流设备video->s_stream(开启或关闭流IO)必须要实现。

v4l2_subdev_internal_ops结构体原型如下:

  1. struct v4l2_subdev_internal_ops {
  2.  
  3. //当subdev注册时被调用,读取IC的ID来进行识别
  4.  
  5. int(*registered)(struct v4l2_subdev *sd);
  6.  
  7. void(*unregistered)(struct v4l2_subdev *sd);
  8.  
  9. //当设备节点被打开时调用,通常会给设备上电和设置视频捕捉FMT
  10.  
  11. int(*open)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh);
  12.  
  13. int(*close)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh);
  14.  
  15. };

v4l2_subdev_internal_ops是向V4L2框架提供的接口,只能被V4L2框架层调用。在注册或打开子设备时,进行一些辅助性操作。

Subdev的注册和注销

当我们把v4l2_subdev需要实现的成员都已经实现,就可以调用以下函数把子设备注册到V4L2核心层:

  1. int v4l2_device_register_subdev(struct v4l2_device*v4l2_dev, struct v4l2_subdev *sd)

当卸载子设备时,可以调用以下函数进行注销:

  1. void v4l2_device_unregister_subdev(struct v4l2_subdev*sd)

video_device

video_device结构体用于在/dev目录下生成设备节点文件,把操作设备的接口暴露给用户空间。

  1. struct video_device
  2.  
  3. {
  4.  
  5. const struct v4l2_file_operations *fops; //V4L2设备操作集合
  6.  
  7. /*sysfs */
  8.  
  9. struct device dev; /* v4l device */
  10.  
  11. struct cdev *cdev; //字符设备
  12.  
  13. /* Seteither parent or v4l2_dev if your driver uses v4l2_device */
  14.  
  15. struct device *parent; /* deviceparent */
  16.  
  17. struct v4l2_device *v4l2_dev; /*v4l2_device parent */
  18.  
  19. /*Control handler associated with this device node. May be NULL. */
  20.  
  21. struct v4l2_ctrl_handler *ctrl_handler;
  22.  
  23. /* 指向video buffer队列*/
  24.  
  25. struct vb2_queue *queue;
  26.  
  27. int vfl_type; /* device type */
  28.  
  29. int minor; //次设备号
  30.  
  31. /* V4L2file handles */
  32.  
  33. spin lock_t fh_lock; /* Lock for allv4l2_fhs */
  34.  
  35. struct list_head fh_list; /* List ofstruct v4l2_fh */
  36.  
  37. /*ioctl回调函数集,提供file_operations中的ioctl调用 */
  38.  
  39. const struct v4l2_ioctl_ops *ioctl_ops;
  40.  
  41. ……
  42.  
  43. };

Video_device分配和释放,用于分配和释放video_device结构体:

  1. struct video_device *video_device_alloc(void)
  2.  
  3. void video_device_release(struct video_device *vdev)

video_device注册和注销,实现video_device结构体的相关成员后,就可以调用下面的接口进行注册:

  1. static inline int __must_checkvideo_register_device(struct video_device *vdev, inttype, int nr)
  2.  
  3. void video_unregister_device(struct video_device*vdev);

vdev:需要注册和注销的video_device;

type:设备类型,包括VFL_TYPE_GRABBER、VFL_TYPE_VBI、VFL_TYPE_RADIO和VFL_TYPE_SUBDEV。

nr:设备节点名编号,如/dev/video[nr]。

v4l2_fh

v4l2_fh是用来保存子设备的特有操作方法,也就是下面要分析到的v4l2_ctrl_handler,内核提供一组v4l2_fh的操作方法,通常在打开设备节点时进行v4l2_fh注册。

初始化v4l2_fh,添加v4l2_ctrl_handler到v4l2_fh:

  1. void v4l2_fh_init(struct v4l2_fh *fh, structvideo_device *vdev)

添加v4l2_fh到video_device,方便核心层调用到:

  1. void v4l2_fh_add(struct v4l2_fh *fh)

v4l2_ctrl_handler

v4l2_ctrl_handler是用于保存子设备控制方法集的结构体,对于视频设备这些ctrls包括设置亮度、饱和度、对比度和清晰度等,用链表的方式来保存ctrls,可以通过v4l2_ctrl_new_std函数向链表添加ctrls。

  1. struct v4l2_ctrl *v4l2_ctrl_new_std(structv4l2_ctrl_handler *hdl,
  2.  
  3. conststruct v4l2_ctrl_ops *ops,
  4.  
  5. u32id, s32 min, s32 max, u32 step, s32 def)

hdl是初始化好的v4l2_ctrl_handler结构体;

ops是v4l2_ctrl_ops结构体,包含ctrls的具体实现;

id是通过IOCTL的arg参数传过来的指令,定义在v4l2-controls.h文件;

min、max用来定义某操作对象的范围。如:

  1. v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS,-, , , );

用户空间可以通过ioctl的VIDIOC_S_CTRL指令调用到v4l2_ctrl_handler,id透过arg参数传递。

3、ioctl框架

你可能观察到用户空间对V4L2设备的操作基本都是ioctl来实现的,V4L2设备都有大量可操作的功能(配置寄存器),所以V4L2的ioctl也是十分庞大的。它是一个怎样的框架,是怎么实现的呢?

Ioctl框架是由v4l2_ioctl.c文件实现,文件中定义结构体数组v4l2_ioctls,可以看做是ioctl指令和回调函数的关系表。用户空间调用系统调用ioctl,传递下来ioctl指令,然后通过查找此关系表找到对应回调函数。

以下是截取数组的两项:

  1. IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf,v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)),
  2.  
  3. IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf,v4l_print_framebuffer, ),

内核提供两个宏(IOCTL_INFO_FNC和IOCTL_INFO_STD)来初始化结构体,参数依次是ioctl指令、回调函数或者v4l2_ioctl_ops结构体成员、debug函数、flag。如果回调函数是v4l2_ioctl_ops结构体成员,则使用IOCTL_INFO_STD;如果回调函数是v4l2_ioctl.c自己实现的,则使用IOCTL_INFO_FNC。

IOCTL调用的流程图如下:

用户空间通过打开/dev/目录下的设备节点,获取到文件的file结构体,通过系统调用ioctl把cmd和arg传入到内核。通过一系列的调用后最终会调用到__video_do_ioctl函数,然后通过cmd检索v4l2_ioctls[],判断是INFO_FL_STD还是INFO_FL_FUNC。如果是INFO_FL_STD会直接调用到视频设备驱动中video_device->v4l2_ioctl_ops函数集。如果是INFO_FL_FUNC会先调用到v4l2自己实现的标准回调函数,然后根据arg再调用到video_device->v4l2_ioctl_ops或v4l2_fh->v4l2_ctrl_handler函数集。

4、IO访问

V4L2支持三种不同IO访问方式(内核中还支持了其它的访问方式,暂不讨论):

read和write,是基本帧IO访问方式,通过read读取每一帧数据,数据需要在内核和用户之间拷贝,这种方式访问速度可能会非常慢;

内存映射缓冲区(V4L2_MEMORY_MMAP),是在内核空间开辟缓冲区,应用通过mmap()系统调用映射到用户地址空间。这些缓冲区可以是大而连续DMA缓冲区、通过vmalloc()创建的虚拟缓冲区,或者直接在设备的IO内存中开辟的缓冲区(如果硬件支持);

用户空间缓冲区(V4L2_MEMORY_USERPTR),是用户空间的应用中开辟缓冲区,用户与内核空间之间交换缓冲区指针。很明显,在这种情况下是不需要mmap()调用的,但驱动为有效的支持用户空间缓冲区,其工作将也会更困难。

Read和write方式属于帧IO访问方式,每一帧都要通过IO操作,需要用户和内核之间数据拷贝,而后两种是流IO访问方式,不需要内存拷贝,访问速度比较快。内存映射缓冲区访问方式是比较常用的方式。

内存映射缓存区方式

硬件层的数据流传输

Camerasensor捕捉到图像数据通过并口或MIPI传输到CAMIF(camera interface),CAMIF可以对图像数据进行调整(翻转、裁剪和格式转换等)。然后DMA控制器设置DMA通道请求AHB将图像数据传到分配好的DMA缓冲区。

待图像数据传输到DMA缓冲区之后,mmap操作把缓冲区映射到用户空间,应用就可以直接访问缓冲区的数据。

vb2_queue

为了使设备支持流IO这种方式,驱动需要实现struct vb2_queue,来看下这个结构体:

  1. struct vb2_queue {
  2.  
  3. enum v4l2_buf_type type; //buffer类型
  4.  
  5. unsigned int io_modes; //访问IO的方式:mmap、userptr etc
  6.  
  7. const struct vb2_ops *ops; //buffer队列操作函数集合
  8.  
  9. const struct vb2_mem_ops *mem_ops; //buffer memory操作集合
  10.  
  11. struct vb2_buffer *bufs[VIDEO_MAX_FRAME]; //代表每个buffer
  12.  
  13. unsigned int num_buffers; //分配的buffer个数
  14.  
  15. ……
  16.  
  17. };

Vb2_queue代表一个videobuffer队列,vb2_buffer是这个队列中的成员,vb2_mem_ops是缓冲内存的操作函数集,vb2_ops用来管理队列。

vb2_mem_ops

vb2_mem_ops包含了内存映射缓冲区、用户空间缓冲区的内存操作方法:

  1. struct vb2_mem_ops {
  2.  
  3. void *(*alloc)(void *alloc_ctx, unsignedlong size); //分配视频缓存
  4.  
  5. void (*put)(void *buf_priv); //释放视频缓存
  6.  
  7. //获取用户空间视频缓冲区指针
  8.  
  9. void *(*get_userptr)(void *alloc_ctx,unsigned long vaddr,
  10.  
  11. unsigned long size, int write);
  12.  
  13. void (*put_userptr)(void *buf_priv); //释放用户空间视频缓冲区指针
  14.  
  15. //用于缓存同步
  16.  
  17. void (*prepare)(void *buf_priv);
  18.  
  19. void (*finish)(void *buf_priv);
  20.  
  21. void *(*vaddr)(void *buf_priv);
  22.  
  23. void *(*cookie)(void *buf_priv);
  24.  
  25. unsigned int (*num_users)(void *buf_priv); //返回当期在用户空间的buffer数
  26.  
  27. int (*mmap)(void *buf_priv, structvm_area_struct *vma); //把缓冲区映射到用户空间
  28.  
  29. };

这是一个相当庞大的结构体,这么多的结构体需要实现还不得累死,幸运的是内核都已经帮我们实现了。提供了三种类型的视频缓存区操作方法:连续的DMA缓冲区、集散的DMA缓冲区以及vmalloc创建的缓冲区,分别由videobuf2-dma-contig.c、videobuf2-dma-sg.c和videobuf-vmalloc.c文件实现,可以根据实际情况来使用。

vb2_ops

vb2_ops是用来管理buffer队列的函数集合,包括队列和缓冲区初始化

  1. struct vb2_ops {
  2.  
  3. //队列初始化
  4.  
  5. int(*queue_setup)(struct vb2_queue *q, const struct v4l2_format *fmt,
  6.  
  7. unsigned int *num_buffers, unsigned int*num_planes,
  8.  
  9. unsigned int sizes[], void *alloc_ctxs[]);
  10.  
  11. //释放和获取设备操作锁
  12.  
  13. void(*wait_prepare)(struct vb2_queue *q);
  14.  
  15. void(*wait_finish)(struct vb2_queue *q);
  16.  
  17. //对buffer的操作
  18.  
  19. int(*buf_init)(struct vb2_buffer *vb);
  20.  
  21. int(*buf_prepare)(struct vb2_buffer *vb);
  22.  
  23. int(*buf_finish)(struct vb2_buffer *vb);
  24.  
  25. void(*buf_cleanup)(struct vb2_buffer *vb);
  26.  
  27. //开始视频流
  28.  
  29. int(*start_streaming)(struct vb2_queue *q, unsigned int count);
  30.  
  31. //停止视频流
  32.  
  33. int(*stop_streaming)(struct vb2_queue *q);
  34.  
  35. //把VB传递给驱动
  36.  
  37. void(*buf_queue)(struct vb2_buffer *vb);
  38.  
  39. };

vb2_buffer是缓存队列的基本单位,内嵌在其中v4l2_buffer是核心成员。当开始流IO时,帧以v4l2_buffer的格式在应用和驱动之间传输。一个缓冲区可以有三种状态:

在驱动的传入队列中,驱动程序将会对此队列中的缓冲区进行处理,用户空间通过IOCTL:VIDIOC_QBUF把缓冲区放入到队列。对于一个视频捕获设备,传入队列中的缓冲区是空的,驱动会往其中填充数据;

在驱动的传出队列中,这些缓冲区已由驱动处理过,对于一个视频捕获设备,缓存区已经填充了视频数据,正等用户空间来认领;

用户空间状态的队列,已经通过IOCTL:VIDIOC_DQBUF传出到用户空间的缓冲区,此时缓冲区由用户空间拥有,驱动无法访问。

这三种状态的切换如下图所示:

v4l2_buffer结构如下:

  1. struct v4l2_buffer {
  2.  
  3. __u32 index; //buffer 序号
  4.  
  5. __u32 type; //buffer类型
  6.  
  7. __u32 bytesused; 缓冲区已使用byte
  8.  
  9. __u32 flags;
  10.  
  11. __u32 field;
  12.  
  13. struct timeval timestamp; //时间戳,代表帧捕获的时间
  14.  
  15. struct v4l2_timecode timecode;
  16.  
  17. __u32 sequence;
  18.  
  19. /*memory location */
  20.  
  21. __u32 memory; //表示缓冲区是内存映射缓冲区还是用户空间缓冲区
  22.  
  23. union {
  24.  
  25. __u32 offset; //内核缓冲区的位置
  26.  
  27. unsigned long userptr; //缓冲区的用户空间地址
  28.  
  29. struct v4l2_plane *planes;
  30.  
  31. __s32 fd;
  32.  
  33. } m;
  34.  
  35. __u32 length; //缓冲区大小,单位byte
  36.  
  37. };

当用户空间拿到v4l2_buffer,可以获取到缓冲区的相关信息。Byteused是图像数据所占的字节数,如果是V4L2_MEMORY_MMAP方式,m.offset是内核空间图像数据存放的开始地址,会传递给mmap函数作为一个偏移,通过mmap映射返回一个缓冲区指针p,p+byteused是图像数据在进程的虚拟地址空间所占区域;如果是用户指针缓冲区的方式,可以获取的图像数据开始地址的指针m.userptr,userptr是一个用户空间的指针,userptr+byteused便是所占的虚拟地址空间,应用可以直接访问。

5、用户空间访问设备

下面通过内核映射缓冲区方式访问视频设备(capturedevice)的流程。

1>    打开设备文件

  1. fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, );
  2. dev_name[/dev/videoX]

2>    查询设备支持的能力

  1. struct v4l2_capability cap;
  2.  
  3. ioctl(fd, VIDIOC_QUERYCAP, &cap);

3>    设置视频捕获格式

  1. fmt.type= V4L2_BUF_TYPE_VIDEO_CAPTURE;
  2.  
  3. fmt.fmt.pix.width = ;
  4.  
  5. fmt.fmt.pix.height = ;
  6.  
  7. fmt.fmt.pix.pixelformat= V4L2_PIX_FMT_YUYV; //像素格式
  8.  
  9. fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
  10.  
  11. ioctl(fd,VIDIOC_S_FMT, & fmt);

4>    向驱动申请缓冲区

  1. struct v4l2_requestbuffers req;
  2.  
  3. req.count= ; //缓冲个数
  4.  
  5. req.type= V4L2_BUF_TYPE_VIDEO_CAPTURE;
  6.  
  7. req.memory= V4L2_MEMORY_MMAP;
  8.  
  9. if(- == xioctl(fd, VIDIOC_REQBUFS, &req))
  10. {
  11. ...
  12. }

5>    获取每个缓冲区的信息,映射到用户空间

  1. struct buffer {
  2.  
  3. void *start;
  4.  
  5. size_t length;
  6.  
  7. } *buffers;
  8.  
  9. buffers = calloc(req.count, sizeof(*buffers));
  10.  
  11. for (n_buffers= ; n_buffers < req.count; ++n_buffers) {
  12.  
  13. struct v4l2_buffer buf;
  14.  
  15. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  16.  
  17. buf.memory = V4L2_MEMORY_MMAP;
  18.  
  19. buf.index = n_buffers;
  20.  
  21. if (- ==xioctl(fd, VIDIOC_QUERYBUF, & buf))
  22.  
  23. errno_exit("VIDIOC_QUERYBUF");
  24.  
  25. buffers[n_buffers].length= buf.length;
  26.  
  27. buffers[n_buffers].start=
  28.  
  29. mmap(NULL /* start anywhere */,
  30.  
  31. buf.length,
  32.  
  33. PROT_READ | PROT_WRITE /* required */,
  34.  
  35. MAP_SHARED /* recommended */,
  36.  
  37. fd, buf.m.offset);
  38.  
  39. }

6>    把缓冲区放入到传入队列上,打开流IO,开始视频采集

  1. for (i =; i < n_buffers; ++i) {
  2.  
  3. struct v4l2_buffer buf;
  4.  
  5. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  6.  
  7. buf.memory = V4L2_MEMORY_MMAP;
  8.  
  9. buf.index = i;
  10.  
  11. if (- == xioctl(fd, VIDIOC_QBUF, &buf))
  12.  
  13. errno_exit("VIDIOC_QBUF");
  14.  
  15. }
  16.  
  17. type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  18.  
  19. if (- == xioctl(fd, VIDIOC_STREAMON, & type))
  20. {
  21. ...
  22. }

7>  调用select监测文件描述符,缓冲区的数据是否填充好,然后对视频数据

  1. for (;;)
  2. {
  3.  
  4. fd_set fds;
  5. struct timeval tv;
  6. int r;
  7. FD_ZERO(&amp;fds);
  8. FD_SET(fd,&amp;fds);
  9. /* Timeout. */
  10. tv.tv_sec = ;
  11. tv.tv_usec = ;
  12. //监测文件描述是否变化
  13. r = select(fd + ,& fds, NULL, NULL, & tv);
  14. if (- == r)
  15. {
  16. if (EINTR ==errno)
  17. continue;
  18. errno_exit("select");
  19. }
  20.  
  21. if ( == r)
  22. {
  23. fprintf(stderr,"select timeout\n");
  24. exit(EXIT_FAILURE);
  25. }
  26. //对视频数据进行处理
  27.  
  28. if (read_frame())
  29. break;
  30. /* EAGAIN - continueselect loop. */
  31. }

8>    取出已经填充好的缓冲,获取到视频数据的大小,然后对数据进行处理。这里取出的缓冲只包含缓冲区的信息,并没有进行视频数据拷贝。

  1. buf.type= V4L2_BUF_TYPE_VIDEO_CAPTURE;
  2.  
  3. buf.memory= V4L2_MEMORY_MMAP;
  4.  
  5. if (- ==ioctl(fd, VIDIOC_DQBUF, & buf)) //取出缓冲
  6.  
  7. errno_exit("VIDIOC_QBUF");
  8.  
  9. process_image(buffers[buf.index].start,buf.bytesused); //视频数据处理
  10.  
  11. if (- ==xioctl(fd, VIDIOC_QBUF, & buf)) //然后又放入到传入队列
  12.  
  13. errno_exit("VIDIOC_QBUF");

9>    停止视频采集

  1. type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
  2.  
  3. ioctl(fd,VIDIOC_STREAMOff, & type);

10> 关闭设备

  1. Close(fd);

V4L2学习(二)结构介绍的更多相关文章

  1. python web开发——django学习(二)orm介绍与model检查

    原始是这样连接数据库的: 现在改用django orm (1)先在setting里配置app (2)在modle.py里建model class UserMessage(models.Model): ...

  2. Magento学习第一课——目录结构介绍

    Magento学习第一课--目录结构介绍 一.Magento为何强大 Magento是在Zend框架基础上建立起来的,这点保证了代码的安全性及稳定性.选择Zend的原因有很多,但是最基本的是因为zen ...

  3. salesforce lightning零基础学习(二) lightning 知识简单介绍----lightning事件驱动模型

    看此篇博客前或者后,看一下trailhead可以加深印象以及理解的更好:https://trailhead.salesforce.com/modules/lex_dev_lc_basics 做过cla ...

  4. 二十四、V4L2框架主要结构体分析和虚拟摄像头驱动编写

    一.V4L2框架主要结构体分析 V4L2(video for linux version 2),是内核中视频设备的驱动框架,为上层访问视频设备提供统一接口. V4L2整体框架如下图: 图中主要包括两层 ...

  5. .NetCore微服务Surging新手傻瓜式 入门教程 学习日志---结构简介(二)

    原文:.NetCore微服务Surging新手傻瓜式 入门教程 学习日志---结构简介(二) 先上项目解决方案图: 以上可以看出项目结构可以划分为4大块,1是surging的核心底层,2,3,4都可以 ...

  6. 8086汇编语言学习(二) 8086汇编开发环境搭建和Debug模式介绍

    1. 8086汇编开发环境搭建 在上篇博客中简单的介绍了8086汇编语言.工欲善其事,必先利其器,在8086汇编语言正式开始学习之前,先介绍一下如何搭建8086汇编的开发环境. 汇编语言设计之初是用于 ...

  7. vue学习笔记(三)——目录结构介绍

    1.初始目录结构如下: 2.目录结构介绍 目录/文件 说明 build 最终发布的代码存放位置. config 配置目录,包括端口号等.我们初学可以使用默认的. node_modules npm 加载 ...

  8. pytorch学习笔记(九):PyTorch结构介绍

    PyTorch结构介绍对PyTorch架构的粗浅理解,不能保证完全正确,但是希望可以从更高层次上对PyTorch上有个整体把握.水平有限,如有错误,欢迎指错,谢谢! 几个重要的类型和数值相关的Tens ...

  9. V4L2学习记录【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637600.html V4L2学习记录 这个还没有分析完,先在这放着,防止电脑坏掉丢了,以后再完善 V4L ...

  10. ReactJS入门学习二

    ReactJS入门学习二 阅读目录 React的背景和基本原理 理解React.render() 什么是JSX? 为什么要使用JSX? JSX的语法 如何在JSX中如何使用事件 如何在JSX中如何使用 ...

随机推荐

  1. linux下的idea的界面问题,错位以及各种...

    问题 ’ 方法 主题设置为GTK,多余的点会消失,而且字体也会好很多

  2. 前端专业术语: shim 和 Polyfill,了解下

    在学习和使用 JavaScript 的时候,我们会经常碰到两个术语:shim 和 polyfill.它们有许多定义和解释,意思相近又有差异. Shim Shim 指的是在一个旧的环境中模拟出一个新 A ...

  3. ElasticSearch 处理自然语言流程

    ES处理人类语言 ElasticSearch提供了很多的语言分析器,这些分析器承担以下四种角色: 文本拆分为单词 The quick brown foxes → [ The, quick, brown ...

  4. vue使用element-ui实现按需引入

    基于Vue的Ui框架 饿了么公司基于vue开的的vue的Ui组件库 Element Ui 基于vue pc端的UI框架 MintUi 基于vue 移动端的ui框架 http://element.ele ...

  5. vue-resource的使用

    之前使用axios post请求不能向后台发送数据,然后使用了vue-resource这个插件 import  Vue from 'vue' import VueResource from 'vue- ...

  6. mockjs模拟数据请求

    一般项目的方法 <html> <head> <script> <script src="http://requirejs.org/docs/rele ...

  7. 获取hudson持续构建编译结果的一种方法

    作者:朱金灿 来源:http://blog.csdn.net/clever101 很多时候使用hudson结合VisualStudio进行持续构建后需要获取持续构建的编译结果,通过编译结果来知道哪些项 ...

  8. 【Oracle】曾经的Oracle学习笔记(8-15)ER图,三大范式,数据库字典,视图,索引,序列

    一.数据库建模 二.建表 三.数据库字典 四.DML语句 五.视图 六.索引 七.序列 八.DDL语句 Lesson 8 Overview of Data Modeling and Database ...

  9. fifo - 先进先出的特殊文件, 又名管道

    描述 (DESCRIPTION) 一个 FIFO 特殊 文件 (又名 管道) 同 管道线 相似, 但是 它是 作为 文件 系统 的一部分 访问的. 可以 有 多个 进程 打开它 以供 读写. 当 进程 ...

  10. python_41_with语句

    #为了避免打开文件后忘记关闭,可以通过管理上下文,即:with open('log','r') as f: # 如此方式,当with代码块执行完毕时,内部会自动关闭并释放文件资源. with open ...