网站开发工程师需要什么证书,wordpress专业,做外贸一般在什么网站,南京学做网站1、IO
“应用程序”对“驱动设备“进行输入/输出操作#xff0c;简称IO操作#xff0c;它是Input和Output的缩写。 2、阻塞IO
阻塞IO是“应用程序”对“驱动设备”进行操作#xff0c;若不能获取到设备资源#xff0c;则阻塞IO应用程序的线程会被“挂起”#xff0c;直到…1、IO
“应用程序”对“驱动设备“进行输入/输出操作简称IO操作它是Input和Output的缩写。 2、阻塞IO
阻塞IO是“应用程序”对“驱动设备”进行操作若不能获取到设备资源则阻塞IO应用程序的线程会被“挂起”直到获取到设备资源为止。
“挂起”就是让线程进入休眠将CPU的资源让出来。线程进入休眠后当设备文件可以操作时就必须唤醒这个休眠的线程。通常是在中断函数里完成唤醒工作Linux内核是采用“等待队列wait queue”来完成阻塞线程的唤醒工作。 阻塞IO应用举例
int fd;
int data 0;
fd open(/dev/xxx_dev, O_RDWR); /* 以阻塞方式打开 */
ret read(fd, data, sizeof(data)); /* 读取数据 */ 3、非阻塞IO
非阻塞IO是“应用程序”对“驱动设备”进行操作若不能获取到设备资源则非阻塞IO应用程序的线程不会被“挂起”即线程不进入休眠而是一直“轮询”直到获取到设备资源为止或者直接放弃。 非阻塞IO应用举例
int fd;
int data 0;
fd open(/dev/xxx_dev, O_RDWR | O_NONBLOCK);
/* 非阻塞方式打开 */
/*O_NONBLOCK表示如果路径名指向FIFO/块文字/字符文件则把文件打开和后继I/O设置为非阻塞*/ ret read( fd, data, sizeof(data) ); /* 读取数据 */ 4、等待队列
wait_queue_head结构体需要包含头文件“#include linux/wait.h”
struct wait_queue_head { spinlock_t lock; struct list_head head;
};
typedef struct wait_queue_head wait_queue_head_t;
//给“wait_queue_head”起个别名叫“wait_queue_head_t”
//目的是兼容老版本的代码 初始化“等待队列头”
void init_waitqueue_head(struct wait_queue_head *wq_head)
//wq_head是要初始化的“等待队列头”
//使用宏 DECLARE WAIT OUEUE HEAD一次性完成等待队列头的定义和初始化 “等待队列头”是等待队列的头部每个访问设备的进程都是一个队列项当不能获取到设备资源的时候就要将“进程对应的队列项”添加到“等待队列”里面。
wait_queue_entry结构体如下
struct wait_queue_entry { unsigned int flags; void *private; wait_queue_func_t func; struct list_head entry;
}; DECLARE_WAITQUEUE(name, tsk)
//给“当前正在运行的进程tsk”创建并初始化一个“等待队列项name”
//name就是等待队列项的名字
//tsk表示这个等待队列项属于哪个任务(进程)一般设置为current
注意
在Linux内核中current相当于一个全局变量表示当前进程 void add_wait_queue( struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry)
//将“等待队列项wq_entry”添加到“等待队列头wq_head”允许进程睡眠
//wq_head“等待队列项”要加入的“等待队列头”
//wq_entry要加入的“等待队列项” void remove_wait_queue( struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry)
//将“等待队列项wq_entry”从“等待队列头wq_head”中删除允许访问设备
//wq head要删除的“等待队列项”所处的“等待队列头”
//wq_entry要删除的“等待队列项” 等待唤醒1
void wake_up(struct wait_queue_head *wq_head)
//由“驱动程序”去唤醒进入休眠的进程属于主动唤醒
//wq_head要唤醒的“等待队列头” 等待唤醒2
void wake_up_interruptible(struct wait_queue_head *wq_head)
//由“驱动程序”去唤醒进入休眠的进程属于主动唤醒
//wq_head要唤醒的“等待队列头” 设置“等待队列”等待某个事件当这个事件满足以后就可以自动唤醒“等待队列的进程”。 等待事件1
wait_event(wq_head, condition)
//由“wq_head为等待队列头的等待队列”去唤醒进程
//当condition条件满足(为真)时会执行唤醒否则会一直阻塞。
//唤醒后会将进程设置为 TASK_UNINTERRUPTIBLE 状态即进程不能被信号打断。 等待事件2
wait_event_interruptible(wq_head, condition)
//等待以“wq_head为等待队列头的等待队列”被唤醒属于等待队列唤醒
//当condition条件满足(为真)时会执行唤醒否则会一直阻塞。
//唤醒后会将进程设置为 TASK_INTERRUPTIBLE 状态即进程可以被信号打断。 等待事件3
wait_event_timeout(wq_head, condition, timeout)
//等待以“wq_head为等待队列头的等待队列”被唤醒属于等待队列唤醒
//如果返回值为0表示超时且condition为假阻塞直到超时再执行唤醒
//如果返回值为1表示condition为真阻塞直到条件满足再执行唤醒 等待事件4
wait_event_interruptible_timeout(wq_head, condition, timeout)
//等待以“wq head为等待队列头的等待队列”被唤醒属于等待队列唤醒
//如果返回值为0表示超时且condition为假阻塞直到超时再执行唤醒
//如果返回值为1表示condition为真阻塞直到条件满足再执行唤醒
//唤醒后会将进程设置为TASK_INTERRUPTIBLE状态即进程可以被信号打断。 5、轮询
在非阻塞处理方式中poll()、epoll()和select()用于处理轮询。若不能获取到设备资源就从设备读取或者向设备写入数据。当应用程序调用poll()、epoll()或select()函数时设备驱动程序中的poll()函数就会被执行因此需要在设备驱动程序中编写poll()函数。
应用程序需要包含“#include poll.h”
驱动程序需要包含“#include linux/poll.h” 1)、select()函数
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
//nfds所要监视的这“三类文件描述集合”中“最大文件描述符”加1
/*readfds用于监视指定描述符集的“读变化”监视这些文件是否可以读取
若可以读取则返回大于0的值若没有文件可以读取则根据timeout判断是否超时*/
/*writefds用于监视指定描述符集的“写变化”监视这些文件是否可以写入
若文件可以执行写操作则返回大于0的值若没有文件可以写入则根据timeout判断是否超时*/
//exceptfds用于监视文件的异常
//timeout超时时间
struct timeval { long tv_sec; /* 秒 */ long tv_usec; /* 微妙 */
}
当timeout为NULL时表示无限期等待
缺点
在单个线程中select()函数能够监视的“文件描述符数量”有最大的限制一般为 1024我们可以修改内核将监视的“文件描述符数量”改大,但是这样做会降低效率 void FD_ZERO(fd_set *set)
//将fd_set型变量的所有位都清零即将所有的文件描述符从fd_set中删除 void FD_SET(int fd, fd_set *set)
//将fd_set型变量的某个位置1即向fd_set添加一个文件描述符
//fd是要加入的文件描述符 void FD_CLR(int fd, fd_set *set)
//将fd_set型变量的某个位置0即将一个文件描述符从fd_set中删除
//fd是要删除的文件描述符 int FD_ISSET(int fd, fd_set *set)
//测试一个文件是否属于某个集合
//参数fd就是要判断的文件描述符 select()函数非阻塞读访问举例
void main(void)
{ int ret, fd; /* 要监视的文件描述符 */ fd_set readfds; /* 读操作文件描述符集 */ struct timeval timeout; /* 超时结构体 */ fd open(dev_xxx, O_RDWR | O_NONBLOCK); //打开文件成功则fd 为“文件描述符” /* 非阻塞式访问 */ /*O_NONBLOCK表示如果路径名指向FIFO/块文字/字符文件则把文件打开和后继I/O设置为非阻塞*/ FD_ZERO(readfds); /* 清除readfds */
//将readfds变量的所有位都清零即将所有的文件描述符从readfds中删除 FD_SET(fd, readfds); /* 将fd添加到readfds里面 */ /* 构造超时时间 */ timeout.tv_sec 0; timeout.tv_usec 500000; /*设置超时时间为500ms */ ret select(fd 1, readfds, NULL, NULL, timeout);
//“fd 1”表示“最大文件描述符”加1
/*readfds用于监视指定描述符集的“读变化”监视这些文件是否可以读取
若可以读取则返回大于0的值若没有文件可以读取则根据timeout判断是否超时*/
//NULL不关注写
//NULL不关注文件的异常
//timeout设置超时时间为500ms; switch (ret) { case 0: /* ret0超时 */ printf(timeout!\r\n); break; case -1: /* 错误 */ printf(error!\r\n); break; default: /* 可以读取数据 */ if( FD_ISSET(fd, readfds) ) /* 判断是否为fd文件描述符 */ { /* 使用read函数读取数据 */ } break; }
} 2)、poll()函数
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
//fds是一个pollfd型结构数组 //nfds是要监视的文件描述符数量
//timeout超时时间单位为ms;
//函数返回值为0超时;
//函数返回值为-1发生错误并且设置errno为错误类型
//成功:函数返回值为revents域中不为0的pollfd结构体个数也就是发生事件或错误的文件描述符数量;
缺点poll()函数都会随着所监听的fd数量的增加出现效率低下且每次必须遍历“所有的描述符”来检查就绪的描述符这个过程很浪费时间; struct pollfd { int fd; /* 要监视的文件描述符*/ short events; /* 要监视的事件若文件描述符无效则监视事件无效*/ short revents; /* 返回的事件*/
};
events事件类型
POLLIN 有数据可以读取。
POLLPRI 有紧急的数据需要读取。
POLLOUT 可以写数据。
POLLERR 指定的文件描述符发生错误。
POLLHUP 指定的文件描述符挂起。
POLLNVAL 无效的请求。
POLLRDNORM 有数据可以读取等同于POLLIN poll()函数读非阻塞访问应用举例:
void main(void)
{ int ret; int fd; /* 要监视的文件描述符 */ struct pollfd fds; fd open(filename, O_RDWR | O_NONBLOCK); //打开文件成功则fd 为“文件描述符” /* 非阻塞式访问 */ /*O_NONBLOCK表示如果路径名指向FIFO/块文字/字符文件则把文件打开和后继I/O设置为非阻塞*/ /* 构造结构体 */ fds.fd fd; /* 要监视的文件描述符*/ fds.events POLLIN; /* 监视数据是否可以读取 */ ret poll(fds, 1, 500); /* 轮询文件是否可操作超时500ms */
//fds是一个pollfd型结构数组 //nfds1是要监视的文件描述符数量
//timeout500超时时间单位为ms; if (ret) /* 数据有效 */ { ...... /* 读取数据 */ ...... } else if (ret 0) /* 超时 */ { ...... } else if (ret 0) /* 错误 */ { ...... }
} 3)、epoll()函数
int epoll_create(int size)
//size从Limux2.6.8开始该参数已经没有意义了设置size0就可以了
//返回值成功返回的是epoll句柄如果为-1的话表示创建失败 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
//epfd要操作的epoll句柄也就是使用 epoll_create()函数创建的epoll句柄。
//op表示要对epfd(epoll句柄)进行操作
EPOLL CTL ADD 向epfd添加文件参数fd表示的描述符
EPOLL CTL MOD 修改参数fd的 event 事件
EPOLL CTL DEL 从epfd中删除fd描述符
//fd要监视的文件描述符
//event为epoll_event型结构指针表示要监视的事件类型
struct epoll_event { uint32_t events; /* epoll事件 */ epoll_data_t data; /* 用户数据*/
};
events事件类型
POLLIN 有数据可以读取。
POLLOUT 可以写数据。
POLLPRI 有紧急数据需要读取。
POLLERR 指定的文件描述符发生错误。
POLLHUP 指定的文件描述符挂起。
EPOLLET 设置epo1l为边沿触发默认触发模式为水平触发
EPOLLONESHOT 一次性的监视当监视完成以后还需要再次监视某个fd 那么就需要将fd重新添加到epoll里面;
注意
上面这些事件可以进行“或”操作也就是说可以设置监视多个事件。
//函数返回值为0成功;
//函数返回值为-1失败并且设置 errno 的值为相应的错误码; int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
epfd要等待的 epoll
events指向epoll_event 结构体的数组当有事件发生的时候Linux内核会填写events调用者可以根据 events 判断发生了哪些事件
maxeventsevents数组大小必须大于0
timeout超时时间单位为 ms
函数返回值0超时-1错误其他值准备就绪的文件描述符数量
注意
epoll()更多的是用在大规模的“并发服务器”上因为在这种场合下select()和 poll()并不适合。当设计到的文件描述符(fd)比较少的时候就适合用selcet()和poll() 4)、Linux驱动下的poll()函数
需要包含“#include linux/delay.h”
unsigned int (*poll) (struct file *filp, struct poll_table_struct *wait)
filp要打开的设备文件(文件描述符)
waitpoll_table_struct类型指针由应用程序传递进来的。一般将此参数传递给poll_wait()函数;
返回值向应用程序返回设备或者资源状态可以返回的资源状态如下
POLLIN 有数据可以读取。
POLLPRI 有紧急的数据需要读取。
POLLOUT 可以写数据。
POLLERR 指定的文件描述符发生错误。
POLLHUP 指定的文件描述符挂起。
POLLNVAL 无效的请求。
POLLRDNORM 有普通数据可以读取等同于POLLIN 在驱动程序的poll()函数中调用poll_wait()函数poll_wait()函数不会引起阻塞只是将“应用程序”添加到poll_table中
void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
//wait_address是要添加到poll_table中的等待队列头;
//p是 poll_table型指针就是file_operations中poll()函数的wait参数 6、绑定信息文档
设备树是用来描述板子上的设备信息不同的设备其信息不同反映到设备树中就是属性不同。
在设备树中添加一个硬件对应的节点我们从哪里查阅相关的说明呢?
在Linux内核源码中有详细的TXT文档描述了如何添加节点这些TXT文档叫做绑定文档路径为
Linux 源码目录/Documentation/devicetree/bindings
绑定文档Documentation/devicetree/bindings/gpio/gpio.txt详细描述了 gpio 控制器节点各个属性信息
led0 { compatible zgq,led; status okay; led-gpio gpioi 0 GPIO_ACTIVE_LOW; }; key0 { compatible zgq,key; status okay; key-gpio gpiog 3 GPIO_ACTIVE_LOW; interrupt-parent gpiog;/*指定父中断器为gpiog*/ interrupts 3 IRQ_TYPE_EDGE_FALLING; /*指定中断号为3中断类型和触发方式为下降沿触发*/ }; EXTI控制器的设备树绑定信息参考文档
Documentation/devicetree/bindings/interrupt-controller/st,stmm32-exti.txt