阻塞后的进程会休眠被移出调度器调度队列,如果休眠的进程不唤醒,将会一直休眠。
非阻塞的进程会一直轮询设备资源,直到可用就使用相应的资源,不可用就执行其他,用于可以让程序自己处理不满足的情况。
应用层:
fd = open("/dev/ttyS1",O_RDWR); // 阻塞
fd = open("/dev/ttyS1",O_RDWR| O_NONBLOCK);// 非阻塞
note:
可使用ioctl(), fcntl() 改变读写方式
如:fcntl(fd, F_SETFL, O_NONBLOCK)将fd的i/o设置为非阻塞。
----------------------------------------------------------------------------------------------------------------
阻塞实例:
Note: 应用和设备都需要相应的实现,在设备驱动端把读取他的应用的进程休眠掉,
并在适当的条件成立时唤醒这个进程。
app:
char buf;
fd = open("/dev/ttyS1",O_RDWR); // 阻塞
.....
res = read(fd, &buf, 1); // 如果IO设备不可获得则开始休眠(在设备驱动中实现)
if(res == 1)
printf("%c\n", buf);
driver: (参考:net\bluetooth\Af_bluetooth.c)
static ssize_t xxx_write (....)
{
.....
DECLARE_WAITQUEUE(wait, current); // 定义等待队列元素
add_wait_queue(xxx_wait, &wait); // 添加元素到等待队列
do{
avail = device_writable(....);
if(avail <0){
if(file ->f_flags &O_NONBLOCK){ // 非阻塞
ret = -EAGAIN;
goto out;
}
__set_current_state(TASK_INTERRUPTIBLE); // 改变进程状态
schedule(); // 调度其他进程执行,执行到这里开始休眠 *************
if(signal_pending(current)){ // 唤醒后第一时间判断是否是信号唤醒****************
ret = -ERESTARTSYS;
goto out;
}
}
}while(avail<0);
device_write(...)
out :
remove_wait_queue(&xxx_wait, &wait); // 将元素移出xxx_wait指向的队列
set_current_state(TASK_RUNNING); // 设置进程状态为 TASK_RUNNING
return ret;
}
其他进程:
wake_up_interruptible(xxx_wait); // 唤醒xxx_wait进程
设备轮询:
应用层的select() 或 poll() 会监视设备fd的状态,首先会对所有的fd进行状态获得若
任一资源可用则直接返回,若都不可用,则select() 或 poll()会将进程休眠,直到资源可读。
select() 或 poll() 会调用驱动中的poll(); poll()要实现 poll_wait(); 使select() 或 poll()被休眠的
进程能被唤醒。
应用层:
void main(void)
{
int fd, num;
char rd_ch[BUFFER_LEN];
fd_set rfds, wfds; /* 读/写文件描述符集 */
/* 以非阻塞方式打开/dev/globalfifo设备文件 */
fd = open("/dev/globalfifo", O_RDONLY | O_NONBLOCK);
if (fd != -1) {
/* FIFO清0 */
if (ioctl(fd, FIFO_CLEAR, 0) < 0)
printf("ioctl command failed\n");
while (1) {
FD_ZERO(&rfds); // 清除文件描述符集合
FD_ZERO(&wfds);
FD_SET(fd, &rfds); // 将描述符加入文件描述符集合
FD_SET(fd, &wfds);
select(fd + 1, &rfds, &wfds, NULL, NULL);
/* 数据可获得 */
if (FD_ISSET(fd, &rfds)) // 判断描述符是否被置位
printf("Poll monitor:can be read\n");
/* 数据可写入 */
if (FD_ISSET(fd, &wfds))
printf("Poll monitor:can be written\n");
}
} else {
printf("Device open failure\n");
}
}
驱动层:poll() (参考:net\bluetooth\Af_bluetooth.c)
static unsigned int xxx_poll(struct file *filp, poll_table * wait)
{
unsigned int mask = 0;
struct globalfifo_dev *dev = filp->private_data;
mutex_lock(&dev->mutex);
poll_wait(filp, &dev->r_wait, wait);
poll_wait(filp, &dev->w_wait, wait); // 作用是让等待队列能唤醒进程
if (dev->current_len != 0) {
mask |= POLLIN | POLLRDNORM;
}
if (dev->current_len != GLOBALFIFO_SIZE) {
mask |= POLLOUT | POLLWRNORM;
}
mutex_unlock(&dev->mutex);
return mask;
}
------------------------------------------------------------------------------------------------------------
poll 分析
驱动poll:
内核打印信息如下:
4. return mask = 260
应用select()到可写,在while(1) 条件下继续select
5. 调用驱动层poll -->
6. 执行驱动xxx_poll 函数unsigned int mask = 0;
打印:
8. return mask = 0
应用层select 休眠进程
参考:linux设备驱动开发详解&互联网