linux 阻塞?linux线程阻塞的方法
Linux内核阻塞IO(wait_queue)和非阻塞IO(轮询poll)
Linux内核提供了两种IO访问模式:阻塞和非阻塞。阻塞IO在设备不可用时会使进程挂起,而非阻塞IO则会不断查询直到设备可用。主要通过wait_queue(等待队列)实现阻塞,它包括等待队列头、添加和移除队列项,以及唤醒机制。例如,DECLARE_WAIT_QUEUE宏为当前进程创建等待队列,wake_up和wake_up_interruptible函数用于唤醒队列中的进程。
轮询方式,如select、poll和epoll,是通过file_operations的poll函数来实现非阻塞IO。poll函数接收一个poll_table_struct指针,通常通过poll_wait函数添加应用程序到该表中,但并不会阻塞进程。当设备状态改变时,驱动程序会更新poll_table,应用程序将根据返回的资源状态进行相应操作。
理解这两种IO模型的关键在于理解如何在设备操作中实现进程的挂起和唤醒,以及如何在非阻塞模式下有效地轮询。深入学习Linux内核源码,例如xxetb.xet.tech/s/3jDmTD,可以更好地掌握这些概念。此外,还有丰富的学习资源如书籍、视频等,可以加入学习交流群739729163获取更多资料。
Linux内核进程命令——阻塞与唤醒
在操作系统中,进程的阻塞与唤醒是确保多任务处理同步和高效的关键机制。当一个进程需要等待特定事件(如资源可用、I/O操作完成)时,它会主动请求系统暂停执行,并进入阻塞状态。当等待的事件发生时,系统会唤醒该进程,使之重新进入可执行状态。
具体来说,进程的阻塞与唤醒由进程的状态切换来实现。进程状态通常包含运行、就绪、阻塞等状态,通过改变状态值来控制进程的执行。Linux中,进程的状态转换主要依赖于`sleep_on`和`wake_up`这两个核心函数。当一个进程需要阻塞时,`sleep_on`将其状态更改为`TASK_UNINTERRUPTIBLE`,表明在特定事件发生之前,该进程将不会被调度。反之,当需要唤醒一个阻塞的进程时,`wake_up`将该进程的状态恢复为`TASK_RUNNING`,使其重新进入可调度状态。
在实现上,`sleep_on`涉及了数据结构的更新和状态的改变,同时,它还利用了一个名为`tmp`的变量来维护等待链表,确保进程间的有序唤醒。当一个进程被唤醒后,`wake_up`函数不仅将状态恢复,还会通过`tmp`变量来唤醒先前被阻塞的进程,形成一个连贯的唤醒链。
Linux中的进程控制不仅涉及阻塞与唤醒的基本原理,还包括更深层次的调度策略和资源管理。例如,通过`fork`函数创建新进程时,新进程的初始状态为运行态。在执行过程中,当进程需要I/O操作、等待资源或其他事件时,会调用相应的阻塞函数,改变状态并进入阻塞状态。当事件触发后,系统会通过调度机制选择合适的进程执行,从而实现了进程间的有效协作与资源的合理分配。
理解Linux内核中的进程控制机制,对于深入掌握现代操作系统原理和实践至关重要。它不仅为开发者提供了一种高效管理多任务环境的工具,还为操作系统研究者提供了深入探索系统内核实现细节的途径。
linux网络编程中阻塞和非阻塞socket的区别
阻塞socket和非阻塞socket的区别:
1、读操作
对于阻塞的socket,当socket的接收缓冲区中没有数据时,read调用会一直阻塞住,直到有数据到来才返回。当socket缓冲区中的数据量小于期望读取的数据量时,返回实际读取的字节数。当sockt的接收缓冲区中的数据大于期望读取的字节数时,读取期望读取的字节数,返回实际读取的长度。
对于非阻塞socket而言,socket的接收缓冲区中有没有数据,read调用都会立刻返回。接收缓冲区中有数据时,与阻塞socket有数据的情况是一样的,如果接收缓冲区中没有数据,则返回错误号为EWOULDBLOCK,表示该操作本来应该阻塞的,但是由于本socket为非阻塞的socket,因此立刻返回,遇到这样的情况,可以在下次接着去尝试读取。如果返回值是其它负值,则表明读取错误。
因此,非阻塞的rea调用一般这样写:
if((nread= read(sock_fd, buffer, len))< 0)
{
if(errno== EWOULDBLOCK)
{
return 0;//表示没有读到数据
}else return-1;//表示读取失败
}else return nread;读到数据长度
2、写操作
对于写操作write,原理是类似的,非阻塞socket在发送缓冲区没有空间时会直接返回错误号EWOULDBLOCK,表示没有空间可写数据,如果错误号是别的值,则表明发送失败。如果发送缓冲区中有足够空间或者是不足以拷贝所有待发送数据的空间的话,则拷贝前面N个能够容纳的数据,返回实际拷贝的字节数。
而对于阻塞Socket而言,如果发送缓冲区没有空间或者空间不足的话,write操作会直接阻塞住,如果有足够空间,则拷贝所有数据到发送缓冲区,然后返回.
非阻塞的write操作一般写法是:
int write_pos= 0;
int nLeft= nLen;
while(nLeft> 0)
{
int nWrite= 0;
if((nWrite= write(sock_fd, data+ write_pos, nLeft))<= 0)
{
if(errno== EWOULDBLOCK)
{
nWrite= 0;
}else return-1;//表示写失败
}
nLeft-= nWrite;
write_pos+= nWrite;
}
return nLen;
3、建立连接
阻塞方式下,connect首先发送SYN请求道服务器,当客户端收到服务器返回的SYN的确认时,则connect返回.否则的话一直阻塞.
非阻塞方式,connect将启用TCP协议的三次握手,但是connect函数并不等待连接建立好才返回,而是立即返回。返回的错误码为EINPROGRESS,表示正在进行某种过程.
4、接收连接
对于阻塞方式的倾听socket,accept在连接队列中没有建立好的连接时将阻塞,直到有可用的连接,才返回。
非阻塞倾听socket,在有没有连接时都立即返回,没有连接时,返回的错误码为EWOULDBLOCK,表示本来应该阻塞。
无阻塞的设置方法
方法一:fcntl
int flag;
if(flag= fcntl(fd, F_GETFL, 0)<0) perror("get flag");
flag|= O_NONBLOCK;
if(fcntl(fd, F_SETFL, flag)< 0)
perror("set flag");
方法二:ioctl
int b_on= 1;
ioctl(fd, FIONBIO,&b_on);