linux下怎样进行摄像头编程?
在linux下所有设备都是文件。所以对摄像头的操作其实就是对文件的操作。USB摄像头的设备文件就是在/dev目录下的video0(假如只有一个摄像头)。在linux下操作摄像头就是使用v4l2对摄像头进行的操作,操作步骤如下
打开设备文件。
int fd=open(”/dev/video0″,O_RDWR);
2. 取得设备的capability,看看设备具有什么功能,比如是否具有输入,或者音频输入输出等。VIDIOC_QUERYCAP,struct v4l2_capability
v4l2_std_id std;
do {
ret= ioctl(fd, VIDIOC_QUERYSTD, std);
} while (ret == -1 errno == EAGAIN);
switch (std) {
case V4L2_STD_NTSC:
//……
case V4L2_STD_PAL:
//……
}
3. 选择输入,一个设备可以有多个输入。VIDIOC_S_INPUT,struct v4l2_input(可不要)
4. 设置的制式和帧格式,制式包括PAL,NTSC,帧的格式个包括宽度和高度等。
VIDIOC_S_STD,VIDIOC_S_FMT,struct v4l2_std_id,struct v4l2_format
struct v4l2_format fmt;
memset ( fmt, 0, sizeof(fmt) );
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 320;
fmt.fmt.pix.height = 240;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;
if (ioctl(fd, VIDIOC_S_FMT, fmt) < 0)
{
printf(“set format failedn”);
//return 0;
}
5. 向驱动申请帧缓冲,一般不超过5个。struct v4l2_requestbuffers
struct v4l2_requestbuffers req;
memset(req, 0, sizeof (req));
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (ioctl(fd,VIDIOC_REQBUFS,req) == -1)
{
perror(“VIDIOC_REQBUFS error n”);
//return -1;
}
6.申请物理内存
将申请到的帧缓冲映射到用户空间,这样就可以直接操作采集到的帧了,而不必去复制。将申请到的帧缓冲全部入队列,以便存放采集到的数据.VIDIOC_QBUF,struct v4l2_buffer
VideoBuffer* buffers = calloc( req.count, sizeof(VideoBuffer) );
printf(“sizeof(VideoBuffer) is %dn”,sizeof(VideoBuffer));
struct v4l2_buffer buf;
for (numBufs = 0; numBufs < req.count; numBufs++)
{
memset( buf, 0, sizeof(buf) );
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = numBufs;
if (ioctl(fd, VIDIOC_QUERYBUF, buf) < 0)
{
printf(“VIDIOC_QUERYBUF errorn”);
//return -1;
}
printf(“buf len is %dn”,sizeof(buf));
//内存映射
buffers.length = buf.length;
buffers.offset = (size_t) buf.m.offset;
buffers.start = mmap (NULL, buf.length,PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
printf(“buffers.length = %d,buffers.offset = %d ,buffers.start = %dn”,buffers.length,buffers.offset,buffers.start);
printf(“buf2 len is %dn”,sizeof(buffers.start));
if (buffers.start == MAP_FAILED)
{
perror(“buffers errorn”);
//return -1;
}
if (ioctl (fd, VIDIOC_QBUF, buf) < 0)
{
printf(“VIDIOC_QBUF errorn”);
//return -1;
}
}
7. 开始的采集。
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl (fd, VIDIOC_STREAMON, type) < 0)
{
printf(“VIDIOC_STREAMON errorn”);
// return -1;
}
8. 出队列以取得已采集数据的帧缓冲,取得原始采集数据。VIDIOC_DQBUF, 将缓冲重新入队列尾,这样可以循环采集。VIDIOC_QBUF
if (ioctl(fd, VIDIOC_DQBUF, buf) < 0)
{
perror(“VIDIOC_DQBUF failed.n”);
//return -1;
}
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
unsigned char *ptcur = buffers.start;
DEBUG(“buf.bytesused = %d n”,buf.bytesused);
int i1;
for(i1=0; i1<buf.bytesused; i1++)
{
if((buffers.start == 0xFF) (buffers.start == 0xC4))
{
DEBUG(“huffman table finded! nbuf.bytesused = %dnFFC4 = %d n”,buf.bytesused,i1);
break;
}
}
if(i1 == buf.bytesused)printf(“huffman table don’t exist! n”);
int i;
for(i=0; i<buf.bytesused; i++)
{
if((buffers.start == 0xFF) (buffers.start == 0xD8)) break;
ptcur++;
}
DEBUG(“i=%d,FF=%02x,D8=%02xn”,i,buffers.start,buffers.start);
int imagesize =buf.bytesused – i;
DEBUG(“buf.bytesused = %d n”,buf.bytesused);
DEBUG (“imagesize = %d n”,imagesize);
9. 停止的采集。VIDIOC_STREAMOFF
10. 关闭设备。close(fd);
每天进步一点点――Linux下的磁盘缓存?
前段时间在开发一个使用SSD做缓存的系统,在高速写入数据时会出现大量的磁盘缓存。太多的磁盘缓存如果没有及时的写入磁盘中,在机器出现问题时是非常危险的,这样会导致很多的数据丢失,但是如果实时的将数据刷入磁盘中,这样写入效率有太低了。为了弄明白linux系统的这种磁盘写入特性,最近深入的学习了一下。 VFS(Virtual File System)的存在使得linux可以兼容不同的文件系统,例如ext3、ext4、xfs、ntfs等等,其不仅具有为所有的文件系统实现一个通用的外接口的作用,还具有另一个与系统性能相关的重要作用——缓存。VFS中引入了高速磁盘缓存的机制,这属于一种软件机制,允许内核将原本存在磁盘上的某些信息保存在RAM中,以便对这些数据的进一步访问能快速进行,而不必慢速访问磁盘本身。高速磁盘缓存可大致分为以下三种:
目录项高速缓存——主要存放的是描述文件系统路径名的目录项对象
索引节点高速缓存——主要存放的是描述磁盘索引节点的索引节点对象
页高速缓存——主要存放的是完整的数据页对象,每个页所包含的数据一定属于某个文件,同时,所有的文件读写操作都依赖于页高速缓存。其是linux内核所使用的主要磁盘高速缓存。 正是由于缓存的引入,所以VFS文件系统采用了文件数据延迟写的技术,因此,如果在调用系统接口写入数据时没有使用同步写模式,那么大多数据将会先保存在缓存中,待等到满足某些条件时才将数据刷入磁盘里。
内核是如何将数据刷入磁盘的呢?在看完以下两点后就能得到答案。
1. 把脏页写入磁盘 正如我们所了解的,内核不断用包含块设备数据的页填充页高速缓存。只要进程修改了数据,相应的页就被标记为脏页,即把它的PG_dirty标志位置。 Unix系统允许把脏缓冲区写入块设备的操作延迟执行,因为这种策略可以显著地提高系统的性能。对高速缓存中的页的几次写操作可能只需对相应的磁盘块进行一次缓慢的物理更新就可以满足。此外,写操作没有读操作那么紧迫,因为进程通常是不会因为延迟写而挂起,而大部分情况都因为延迟读而挂起。正是由于延迟写,使得任一物理块设备平均为读请求提供服务将多于写请求。一个脏页可能直到最后一刻(即直到系统关闭时)都一直逗留在主存中。然而,从延迟写策略的局限性来看,它有两个主要的缺点: 一、如果发生了硬件错误或者电源掉电的情况,那么就无法再获得RAM的内容,因此,从系统启动以来对文件进行的很多修改就丢失了。 二、页高速缓存的大小(由此存放它所需的RAM的大小)就可要很大——至少要与所访问块设备的大小不同。因此,在下列条件下把脏页刷新(写入)到磁盘:
页高速缓存变得太满,但还需要更多的页,或者脏页的数量已经太多。
自从页变成脏页以来已过去太长时间。
进程请求对块设备或者特定文件任何待定的变化都进行刷新。通过调用sync()、fsync()或者fdatasync()系统调用来实现。 缓冲区页的引入是问题更加复杂。与每个缓冲区页相关的缓冲区首部使内核能够了解每个独立块缓冲区的状态。如果至少有一个缓冲区首部的PG_Dirty标志被置位,就应该设置相应缓冲区页的PG_dirty标志。当内核选择要刷新的缓冲区时,它扫描相应的缓冲区首部,并只把脏块的内容有效的写到磁盘。一旦内核把缓冲区的所有脏页刷新到磁盘,就把页的PG_dirty标志清0。
2. pdflush内核线程 早期版本的linux使用bdfllush内核线程系统地扫描页高速缓存以搜索要刷新的脏页,并且使用另一个内核线程kupdate来保证所有的页不会脏太长时间。linux 2.6用一组通用内核线程pdflush替代上述两个线程。这些内核线程结构灵活,它们作用于两个参数:一个指向线程要执行的函数的指针和一个函数要用的参数。系统中pdflush内核线程的数量是要动态调整的:pdflush线程太少时就创建,太多时就杀死。因为这些内核线程所执行的函数可以阻塞,所以创建多个而不是一个pdflush内核线程可以改善系统性能。根据下面的原则控制pdflush线程的产生和消亡:
必须有至少两个,最多八个pdflush内核线程
如果最近一次pdflush变为空闲的时间超过了1s,就应该删除一个pdflush线程 所有的pdflush内核线程都有pdflush_work描述符,其数据结构如下:
类型 字段 说明 struct task_struct who 指向内核线程描述符的指针 void (*) (unsigned long) fn 内核线程所执行的回调函数 unsigned long arg0 给回调函数的参数 struct list head list pdflush_list链表的链接 unsigned long when_i_went_to_sleep 当内核线程可用时的时间(以jiffies表示)
当系统没有要刷新的脏页时,pdflush线程会自动处于睡眠状态,最后由pdflush_operation()函数来唤醒。那么在这个过程中pdflush内核线程主要完成了哪些工作呢?其中一些工作与脏数据的刷新有关。尤其是pdflush通常执行下面的回调函数之一: 1. background_writeout(): 系统地扫描页高速缓存以搜索要刷新的脏页。
为了得到需要刷新的脏页,就要彻底的搜索与在磁盘上有映像的索引节点相应的所有address_space对象(是一棵搜索树)。由于页高速缓存可能有大量的页,如果用一个单独的执行流来扫描整个高速缓存,会令CPU和磁盘长时间繁忙,因此,linux使用一种复杂的机制把对页高速缓存的扫描划分为几个执行流。当内存不足或者用户显式的(用户态进程发出sync()系统调用等)调用请求刷新操作时会执行wakeup_bdflush()函数。wakeup_bdflush()函数会调用pdflush_operation()唤醒pdflush内核线程,并委托它执行回调函数background_writeout()。background_writeout()函数有效的从页高速缓存中获得指定数量的脏页,并把它写回磁盘。此外,执行background_writeout()函数的pdflush内核线程只有在满足以下两个条件下才能被唤醒:一是对页高速缓存中的页内容进行了修改,二是引起脏页部分增加到超过某个脏背景阈值。背景阈值通常设置为系统中所有页的10%,不过可以通过修改文件/proc/sys/vm/dirty_background_ratio来调整该值。
linux内核编程书籍有那些比较经典?
学习内核不是件容易事,前面有很多前续课程要读。
1.C语言,推荐Theprogrammginclanguage被称为圣经的书,不过没有中文版,不过C语言不太难,而且读内核也不需要你对C有多深了解。学习到指针和结构体就够你用了。
2.数据结构,不懂这个想学是不可能的,推荐《数据结构》C语言版本。
3.操作系统,任何一个大学的教科书都可以。一定要看,虽然是纯理论。但不看这个,想看懂内核,那是不可能的。
4.微机原理,内核和底层相关,不知道微机原理是不行的。我学的时候书上是以INTEL8086为蓝本,非常简单。后来看内核时候,补充了80386以后出现的保护模式,非常复杂。但还是先看下老版的微机原理,先从简单的下手。
5.linux系统管理,推荐《linux系统管理手册》,被linux的作者推荐的系统管理书籍。 6,终于到内核了,我看的是《Linux内核代码情景分析》虽然有点老。但结合新的源代码,在了解机制后,代码实现上差距不大。
linux怎么管理空闲内存?
内存组织层次:页式管理—>(numa)—>node的zonelist—>32位DMA/NORMAL/HIGHMEM三个区,64位没有高端内存—>伙伴分配系统—>slab/slub/slob2.创建进程时内存分配:实际上只分配task_struct和thread_info的内存,而且很可能是从slab缓存中分配的,当进程运行时由于缺页中断,才由内核层具体分配物理内存并与vm挂接3.malloc是c runtime中的实现,是上层库的内存分配层,至于内核层的,可以看看__alloc_pages/alloc_pages/kmalloc(小内存直接slab,大内存还是alloc_pages)/vmalloc(alloc_page分配不连续的物理页,映射到连续的vm_struct中的pages指针数组)/vmap/map_vm_area等几个函数