精华内容
下载资源
问答
  • 阻塞赋值与阻塞赋值

    万次阅读 多人点赞 2019-06-17 16:13:11
    Verilog语言中最容易被误解的构造之一是非阻塞赋值。 即使是非常有经验的Verilog设计人员也不能完全理解在IEEE兼容的Verilog模拟器中如何安排非阻塞分配,并且不了解应该何时以及为何应该使用非阻塞赋值。 本文详细...

    1.0简介

    编码指南:
    指南:在为生成组合逻辑[1]而编写的always块中使用阻塞赋值。
    指南:在为生成顺序逻辑[1]而编写的always块中使用非阻塞赋值。
    竞争:
    如果两个或多个计划在同一模拟时间步骤中执行的语句在IEEE Verilog标准允许的情况下更改语句执行顺序时会产生不同的结果,则会发生Verilog竞争。

    在本文中,将使用以下缩写:
    RHS - 方程右侧的表达式或变量将缩写为RHS方程,RHS表达式或RHS变量。
    LHS - 方程左侧的表达式或变量将缩写为LHS方程,LHS表达式或LHS变量。

    2.0阻塞赋值&非阻塞赋值

    2.1阻塞赋值

    在Verilog HDL的概念中阻塞赋值操作符用等号(即=)表示。
    在赋值时先计算等号右手方向(RHS)部分的值,这时赋值语句不允许任何别的Verilog语句的干扰,直到现行的赋值完成时刻,即把RHS赋值给LHS(等号左手方向)的时刻,它才允许别的赋值语句的执行。一般可综合的阻塞赋值操作在RHS不能设定有延迟(即使是零延迟也不允许)。从理论上讲,它与后面的赋值语句只有概念上的先后,而无实质上的延迟。若在RHS上加延迟,则在延迟期间会阻止赋值语句的执行,延迟后才执行赋值,这种赋值语句是不可综合的,在需要综合的模块设计中不可使用这种风格的代码。
    问题
    如果在一个过程块中阻塞赋值的RHS变量正好是另一个过程块中阻塞赋值的LHS变量,这两个过程块又用同一个时钟沿触发,这时阻塞赋值操作会出现问题,即如果阻塞赋值的顺序安排不好,就会出现竞争。若这两个阻塞赋值操作用同一个时钟沿触发,则执行的顺序是无法确定的。
    为了说明这一点,请查看示例1中的Verilog代码。

    module fbosc1 (y1, y2, clk, rst);
        output y1, y2;
        input clk, rst;
        reg y1, y2;
    always @(posedge clk or posedge rst)
         if (rst) y1 = 0; // reset
         else y1 = y2;
    always @(posedge clk or posedge rst)
         if (rst) y2 = 1; // preset
         else y2 = y1;
    endmodule
    

    Example 1 - Feedback oscillator with blocking assignments

    根据IEEE Verilog标准,可以按任何顺序安排两个始终块。 如果在复位后执行第一个always块,则y1和y2都将取值1.如果第二个always块在复位后首先执行,则y1和y2都将取值0.这显然代表一个Verilog 竞争条件。

    2.2非阻塞赋值

    非阻塞赋值操作符用小于等于号(即<=)表示。
    为在赋值操作时刻开始时计算非阻塞赋值符的RHS表达式,赋值操作时刻结束时更新LHS。在计算非阻塞赋值的RHS表达式和更新LHS期间,其他的Verilog语句,包括其他的Verilog非阻塞赋值语句都能同时计算RHS表达式和更新LHS。非阻塞赋值允许其他的Verilog语句同时进行操作。
    非阻塞赋值是由时钟节拍决定,在时钟上升到来时,执行赋值语句右边,然后将begin-end之间的所有赋值语句同时赋值到赋值语句的左边,注意:是begin—end之间的所有语句,一起执行,且一个时钟只执行一次。
    为了说明这一点,请查看示例2中的Verilog代码

    module fbosc2 (y1, y2, clk, rst);
        output y1, y2;
        input clk, rst;
    reg y1, y2;
    always @(posedge clk or posedge rst)
        if (rst) y1 <= 0; // reset
         else y1 <= y2;
    always @(posedge clk or posedge rst)
        if (rst) y2 <= 1; // preset
         else y2 <= y1;
    endmodule
    

    Example 2 - Feedback oscillator with nonblocking assignments
    同样,根据IEEE Verilog标准,可以按任何顺序安排两个始终块。 无论在复位后哪个块始终首先启动,都将在时间步的开始时评估两个非阻塞RHS表达式,然后在同一时间步的末尾更新两个非阻塞LHS变量。 从用户的角度来看,这两个非阻塞语句的执行是并行发生的。

    2.3区别

    非阻塞赋值
    always@(posedge clk)
    begin
    b<=a;
    c<=b;
    end
    阻塞赋值
    always@(posedge clk)
    begin
    b=a;
    c=b;
    end
    两种不同的赋值方式结果是不同的,非阻塞赋值b<=a;c<=b;两条语句是同时执行的,而阻塞赋值b=a;c=b;两条语句先执行b=a后执行c=b。

    3.0编码准则

    Clifford在《Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kill!》
    一文中有给出八条编码准则:
    1)时序电路建模时,用非阻塞赋值;
    2)锁存器电路建模时,用非阻塞赋值;
    3)用always块建立组合逻辑模型时,用阻塞赋值;
    4)在同一个always块中建立时序和组合逻辑电路时,用非阻塞赋值;
    5)在同一个alway块中,不要即用非阻塞又用阻塞赋值;
    6)不要在一个以上的always块中为同一个变量赋值;
    7)用$strobe系统任务来显示用非阻塞赋值的变量值;
    8)在赋值时不要使用#0延迟。

    4.0 举例

    准则1)时序电路建模时,用非阻塞赋值;

    在这里插入图片描述
    图2显示了简单的顺序流水线寄存器最简单的时序电路)的框图。 示例5 - 示例8显示了工程师可能选择使用阻塞赋值四的种不同编码方式。
    在例5中,顺序排序的阻塞分配将使输入值d放在下一个posedge clk上每个寄存器的输出上。 在每个时钟边沿,输入值无延迟地直接传输到q3输出。 这显然不会对流水线寄存器进行建模,实际上会合成一个寄存器! (见图3)。
    在这里插入图片描述

    module pipeb1 (q3, d, clk);
    output [7:0] q3;
    input [7:0] d;
    input clk;
    reg [7:0] q3, q2, q1;
    always @(posedge clk) begin
    q1 = d;
    q2 = q1;
    q3 = q2;
    end
    endmodule
    

    Example 5 - Bad blocking-assignment sequential coding style #1
    在示例6中,已仔细排序阻塞分配,以使模拟正确地像流水线寄存器一样运行。 该模型合成了图2所示的流水线寄存器。

    module pipeb2 (q3, d, clk);
    output [7:0] q3;
    input [7:0] d;
    input clk;
    reg [7:0] q3, q2, q1;
    always @(posedge clk) begin
    q3 = q2;
    q2 = q1;
    q1 = d;
    end
    endmodule
    

    Example 6 - Bad blocking-assignment sequential coding style #2 - but it works!
    在示例7中,阻塞分配已拆分为单独的always块。
    允许Verilog以任何顺序模拟always块,这可能导致此管道模拟错误。 这是Verilog的竞争条件! 以不同顺序执行always块会产生不同的结果。 但是,这个Verilog代码将合成到正确的流水线寄存器。 这意味着预合成和后合成模拟之间可能存在不匹配。 pipeb4示例或同一个始终块语句的任何其他顺序也将合成到正确的管道逻辑,但可能无法正确模拟。

    module pipeb3 (q3, d, clk);
    output [7:0] q3;
    input [7:0] d;
    input clk;
    reg [7:0] q3, q2, q1;
    always @(posedge clk) q1=d;
    always @(posedge clk) q2=q1;
    always @(posedge clk) q3=q2;
    endmodule
    

    Example 7 - Bad blocking-assignment sequential coding style #3

    module pipeb4 (q3, d, clk);
    output [7:0] q3;
    input [7:0] d;
    input clk;
    reg [7:0] q3, q2, q1;
    always @(posedge clk) q2=q1;
    always @(posedge clk) q3=q2;
    always @(posedge clk) q1=d;
    endmodule
    

    Example 8 - Bad blocking-assignment sequential coding style #4
    如果四个阻塞分配示例中的每一个都用非阻塞赋值重写,则每个都将正确模拟并合成所需的流水线逻辑。

    module pipen1 (q3, d, clk);
    output [7:0] q3;
    input [7:0] d;
    input clk;
    reg [7:0] q3, q2, q1;
    always @(posedge clk) begin
    q1 <= d;
    q2 <= q1;
    q3 <= q2;
    end
    endmodule
    

    Example 9 - Good nonblocking-assignment sequential coding style #1

    module pipen2 (q3, d, clk);
    output [7:0] q3;
    input [7:0] d;
    input clk;
    reg [7:0] q3, q2, q1;
    always @(posedge clk) begin
    q3 <= q2;
    q2 <= q1;
    q1 <= d;
    end
    endmodule
    

    Example 10 - Good nonblocking-assignment sequential coding style #2

    module pipen3 (q3, d, clk);
    output [7:0] q3;
    input [7:0] d;
    input clk;
    reg [7:0] q3, q2, q1;
    always @(posedge clk) q1<=d;
    always @(posedge clk) q2<=q1;
    always @(posedge clk) q3<=q2;
    endmodule
    

    Example 11 - Good nonblocking-assignment sequential coding style #3

    module pipen4 (q3, d, clk);
    output [7:0] q3;
    input [7:0] d;
    input clk;
    reg [7:0] q3, q2, q1;
    always @(posedge clk) q2<=q1;
    always @(posedge clk) q3<=q2;
    always @(posedge clk) q1<=d;
    endmodule
    

    Example 12 - Good nonblocking-assignment sequential coding style #4

    由此可见,对流水线逻辑进行编码时 ,非阻塞赋值会比阻塞赋值好,虽然阻塞赋值也可以通过严谨的拍序实现。

    准则2)锁存器电路建模时,用非阻塞赋值;

    类似的分析表明,使用非阻塞分配来模拟锁存器也是最安全的。

    准则3)用always块建立组合逻辑模型时,用阻塞赋值;

    使用Verilog对组合逻辑进行编码的方法有很多种,但是当使用always块对组合逻辑进行编码时,应该使用阻塞分配。

    例19中的代码构建了三个顺序执行语句的y输出。
    由于非阻塞分配在更新LHS变量之前评估RHS表达式,因此tmp1和tmp2的值是在进入此始终块时这两个变量的原始值,而不是在模拟时间步骤结束时将更新的值。
    y输出将反映tmp1和tmp2的旧值,而不是始终块的当前传递中计算的值。

    module ao4 (y, a, b, c, d);
    output y;
    input a, b, c, d;
    reg y, tmp1, tmp2;
    always @(a or b or c or d) begin
    tmp1 <= a & b;
    tmp2 <= c & d;
    y <= tmp1 | tmp2;
    end
    endmodule
    

    Example 19 - Bad combinational logic coding style using nonblocking assignments
    将temp1和temp2放入敏感信号列表,当非阻塞赋值更新更新事件队列中的LHS变量时,always块将自触发并用新计算的tmp1和tmp2值更新y输出。 在通过always块两次后,y输出值现在将是正确的。 通过始终块的多次传递等同于降低的模拟性能,并且如果存在合理的替代方案则应该避免。

    module ao5 (y, a, b, c, d);
    output y;
    input a, b, c, d;
    reg y, tmp1, tmp2;
    always @(a or b or c or d or tmp1 or tmp2) begin
    tmp1 <= a & b;
    tmp2 <= c & d;
    y <= tmp1 | tmp2;
    end
    endmodule
    

    Example 20 - Inefficient multi-pass combinational logic coding style with nonblocking assignments
    一个更好的开发习惯,即不需要多次遍历always块的习惯,就是只使用写入模型组合逻辑的always块中的阻塞赋值。

    module ao2 (y, a, b, c, d);
    output y;
    input a, b, c, d;
    reg y, tmp1, tmp2;
    always @(a or b or c or d) begin
    tmp1 = a & b;
    tmp2 = c & d;
    y = tmp1 | tmp2;
    end
    endmodule
    

    Example 21 - Efficient combinational logic coding style using blocking assignments
    示例21中的代码与示例19中的代码相同,只是非阻塞分配已被阻塞分配替换,这将保证只有一次通过always块后,y将输出正确的值;

    准则4)在同一个always块中建立时序和组合逻辑电路时,用非阻塞赋值

    使用非阻塞分配有时可以方便地将简单组合逻辑与时序逻辑电路组合。 将组合和时序代码组合到单个always块中时,将always块编码为具有非阻塞赋值的顺序始终块,如例22所示。

    module nbex2 (q, a, b, clk, rst_n);
    output q;
    input clk, rst_n;
    input a, b;
    reg q;
    always @(posedge clk or negedge rst_n)
    if (!rst_n) q <= 1'b0;
    else q <= a ^ b;
    endmodule
    

    Example 22 - Combinational and sequential logic in a single always block

    在例22中实现的相同逻辑也可以实现为两个单独的始终块,一个用阻塞赋值编码的纯组合逻辑,一个具有用非阻塞赋值编码的纯时序逻辑,如例23所示。

    module nbex1 (q, a, b, clk, rst_n);
    output q;
    input clk, rst_n;
    input a, b;
    reg q, y;
    always @(a or b)
    y = a ^ b;
    always @(posedge clk or negedge rst_n)
    if (!rst_n) q <= 1'b0;
    else q <= y;
    endmodule
    

    Example 23 - Combinational and sequential logic separated into two always blocks

    准则5)在同一个alway块中,不要即用非阻塞又用阻塞赋值;

    Verilog允许阻塞和非阻塞赋值在always块内自由混合。 通常,即使Verilog允许,在同一个总块中混合阻塞和非阻塞赋值也是一种糟糕的编码风格。
    例24中的代码将正确模拟和综合,因为阻塞赋值不是与非阻塞赋值相同的变量。 虽然这可行,但clifford不鼓励这种编码风格。

    module ba_nba2 (q, a, b, clk, rst_n);
    output q;
    input a, b, rst_n;
    input clk;
    reg q;
    always @(posedge clk or negedge rst_n) begin: ff
    reg tmp;
    if (!rst_n) q <= 1'b0;
    else begin
    tmp = a & b;
    q <= tmp;
    end
    end
    endmodule
    

    Example 24 - Blocking and nonblocking assignment in the same always block - generally a bad idea!
    示例25中的代码很可能在大多数情况下正确模拟,但Synopsys工具将报告语法错误,因为阻塞赋值被赋值给与非阻塞赋值之一相同的变量。 必须修改此代码才能合成。

    module ba_nba6 (q, a, b, clk, rst_n);
    output q;
    input a, b, rst_n;
    input clk;
    reg q, tmp;
    always @(posedge clk or negedge rst_n)
    if (!rst_n) q = 1'b0; // blocking assignment to "q"
    else begin
    tmp = a & b;
    q <= tmp; // nonblocking assignment to "q"
    end
    endmodule
    

    Example 25 - Synthesis syntax error - blocking and nonblocking assignment to the same variable

    准则6)不要在一个以上的always块中为同一个变量赋值;

    即使使用非阻塞赋值,从多个always快对同一变量进行多次赋值也是Verilog竞争条件。
    在示例26中,两个始终块正在对q输出进行分配,两者都使用非阻塞分配。 由于这些始终块可以按任何顺序进行调度,因此模拟输出是竞争条件。

    module badcode1 (q, d1, d2, clk, rst_n);
    output q;
    input d1, d2, clk, rst_n;
    reg q;
    always @(posedge clk or negedge rst_n)
    if (!rst_n) q <= 1'b0;
    else q <= d1;
    always @(posedge clk or negedge rst_n)
    if (!rst_n) q <= 1'b0;
    else q <= d2;
    endmodule
    

    Example 26 - Race condition coding style using nonblocking assignments
    综合工具运行这段代码的时候会报错:
    Warning: In design ‘badcode1’, there is 1 multiple-driver net with unknown wired-logic type.
    当忽略警告并编译示例26中的代码时,推断出两个触发器,其输出馈送2输入和门。 在该示例中,预合成模拟甚至不与后合成模拟紧密匹配。

    准则7)用$strobe系统任务来显示用非阻塞赋值的变量值;

    Myth:“使用带有非阻塞赋值的$ display命令不起作用”
    Truth:在所有$ display命令之后更新非阻塞赋值

    module display_cmds;
    reg a;
    initial $monitor("\$monitor: a = %b", a);
    initial begin
    $strobe ("\$strobe : a = %b", a);
    a = 0;
    a <= 1;
    $display ("\$display: a = %b", a);
    #1 $finish;
    end
    endmodule
    

    上面模拟显示,在执行非阻塞赋值更新事件之前,$ display命令在活动事件队列中执行。
    $display: a = 0
    $monitor: a = 1
    $strobe : a = 1

    准则8)在赋值时不要使用#0延迟

    Myth: “#0强制分配到时间步的末尾”
    Truth: #0强制分配给“非活动事件队列

    module nb_schedule1;
    reg a, b;
    initial begin
    a = 0;
    b = 1;
    a <= b;
    b <= a;
    $monitor ("%0dns: \$monitor: a=%b b=%b", $stime, a, b);
    $display ("%0dns: \$display: a=%b b=%b", $stime, a, b);
    $strobe ("%0dns: \$strobe : a=%b b=%b\n", $stime, a, b);
    #0 $display ("%0dns: #0 : a=%b b=%b", $stime, a, b);
    #1 $monitor ("%0dns: \$monitor: a=%b b=%b", $stime, a, b);
    $display ("%0dns: \$display: a=%b b=%b", $stime, a, b);
    $strobe ("%0dns: \$strobe : a=%b b=%b\n", $stime, a, b);
    $display ("%0dns: #0 : a=%b b=%b", $stime, a, b);
    #1 $finish;
    end
    endmodule
    

    模拟显示,在执行非阻塞赋值更新事件之前,在非活动事件队列中执行了#0-delay命令。

    0ns: $display: a=0 b=1
    0ns: #0 : a=0 b=1
    0ns: $monitor: a=1 b=0
    0ns: $strobe : a=1 b=0
    1ns: $display: a=1  b=0
    1ns: #0 : a=1 b=0
    1ns: $monitor: a=1 b=0
    1ns: $strobe : a=1 b=0
    
    展开全文
  • 阻塞与非阻塞

    2020-02-02 10:17:58
    阻塞与非阻塞 我们先来看一看什么是同步与异步。 同步和异步的区别 现在大概知道什么是同步与异步了,我们来看一看什么是阻塞与非阻塞。 当客户端向服务器端发出请求,服务器端的处理方式有同步与异步,即服务器端...

    推荐:Java网络编程汇总

    阻塞与非阻塞

    我们先来看一看什么是同步与异步。
    同步和异步的区别

    现在大概知道什么是同步与异步了,我们来看一看什么是阻塞与非阻塞。

    当客户端向服务器端发出请求,服务器端的处理方式有同步与异步,即服务器端可以先处理完请求再回复客服端(同步),也可以先回复客户端再去处理请求(异步)。

    而阻塞与非阻塞,则是客户端的处理方式,所以说同步与异步、阻塞与非阻塞往往是结合起来讨论的,当客户端向服务器端发出请求,我们先不管服务器端的处理方式,我们只在乎客户端的处理方式。
    当客户端发出请求后,不管服务器端的处理方式,客户端便一直在等待处理结果,并且不能去处理其他事情(茶不思、饭不想的感觉),这便是阻塞。
    而当客户端发出请求后,同样不去管服务器端的处理方式,客户端不等待处理结果,只要请求发出成功和完成一些必要操作即可,并且之后可以去处理其他事情。

    例子

    这里举男生向女生表白的例子。

    同步阻塞

    男生向女生表白,女生陷入沉思,想清楚之后再回复(同步),此时男生一直在等着女生回复(阻塞)。

    同步非阻塞

    男生向女生表白,女生陷入沉思,想清楚之后再回复(同步),而男生已经在想如果女生答应表白了,以后约会该怎样表现(非阻塞)。

    异步阻塞

    男生向女生表白,女生想了想,说:“我需要想几天,你先回家等我消息(微信)”(异步),男生在家一直等着她的微信消息,茶不思、饭不想的(阻塞)。

    异步非阻塞

    男生向女生表白,女生想了想,说:“我需要想几天,你先回家等我消息(微信)”(异步),男生回家后还打游戏、打篮球、写博客(非阻塞)。

    如果有说错的地方,请大家不吝赐教(记得留言哦~~~~)。

    展开全文
  • 阻塞、非阻塞IO

    千次阅读 2017-11-14 20:06:22
    阻塞与非阻塞是对于文件而言的,而不是指read、write等的属性。 阻塞IO 应用程序调用IO函数,导致应用程序阻塞,等待数据准备好。如果数据没有准备好,一直等待数据准备好了,从内核拷贝到用户空间,IO函数返回...

    阻塞与非阻塞是对于文件而言的,而不是指read、write等的属性。

    阻塞IO

    应用程序调用IO函数,导致应用程序阻塞,等待数据准备好。如果数据没有准备好,一直等待数据准备好了,从内核拷贝到用户空间,IO函数返回成功指示。

    这里写图片描述

    读常规文件是不会阻塞的,不管读多少字节,read一定会在有限的时间内返回。一般网络、终端设备IO都是阻塞I/O。

    如果从终端输入的数据没有换行符,调用read读终端设备就会阻塞,如果网络上没有接收到数据包,调用read从网络读就会阻塞,至于会阻塞多长时间也是不确定的,如果一直没有数据到达就一直阻塞在那里。

    示例代码:

    #include <errno.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    int main()
    {
        char buf[1024] = {0};
        printf("读数据前:\n");
        read(0, buf, sizeof(buf));  //默认是阻塞的,从标准输入读取内容
        printf("读取数据后:buf = %s\n", buf);
    
        return 0;
    }
    

    运行结果:
    这里写图片描述

    非阻塞IO

    我们把一个文件设置为非阻塞就是告诉内核,当所请求的I/O操作无法完成时,不要将进程睡眠,而是返回一个错误(EAGAIN)。

    这样我们的I/O操作函数将不断的测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止。在这个不断测试的过程中,会大量的占用CPU的时间,所有一般Web服务器都不使用这种I/O模型。
    这里写图片描述

    示例代码:

    #include <errno.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    int main()
    {
        int fd;
        //以非阻塞方式打开当前终端
        fd = open("/dev/tty", O_RDONLY|O_NONBLOCK); 
        if(fd < 0)
        {
            perror("open\n");
            return -1;
        }
    
        char buf[1024];
        while(1)
        {   
            int n = read(fd, buf, sizeof(buf));
            printf("n = %d, errno = %d, EAGAIN = %d\n", n, errno, EAGAIN);
            sleep(1); //延时
    
            if(n <= 0)
            {
                //如果为非阻塞,但是没有数据可读,此时全局变量 errno 被设置为 EAGAIN
                if(errno == EAGAIN)
                {
                    printf("没有数据可读\n");
                }
                else
                {
                    perror("read");
                    break;
                }
            }
            else
            {//有数据
                printf("buf = %s\n", buf);
                break;
            }
        }
    
        return 0;
    }
    

    运行结果:
    这里写图片描述

    20180210174507305

    展开全文
  • socket阻塞和非阻塞

    万次阅读 多人点赞 2018-09-27 20:45:22
    一、socket阻塞和非阻塞有哪些不同 1. 建立连接 阻塞方式下,connect首先发送SYN请求到服务器,当客户端收到服务器返回的SYN的确认时,则connect返回,否则的话一直阻塞。 非阻塞方式,connect将启用TCP协议的三...

    一、socket阻塞和非阻塞有哪些不同

    1. 建立连接

    阻塞方式下,connect首先发送SYN请求到服务器,当客户端收到服务器返回的SYN的确认时,则connect返回,否则的话一直阻塞。

    非阻塞方式,connect将启用TCP协议的三次握手,但是connect函数并不等待连接建立好才返回,而是立即返回,返回的错误码为EINPROGRESS,表示正在进行某种过程。

    2. 接收连接

    阻塞模式下调用accept()函数,而且没有新连接时,进程会进入睡眠状态,直到有可用的连接,才返回。

    非阻塞模式下调用accept()函数立即返回,有连接返回客户端套接字描述符,没有新连接时,将返回EWOULDBLOCK错误码,表示本来应该阻塞。

    3. 读操作

    阻塞模式下调用read(),recv()等读套接字函数会一直阻塞住,直到有数据到来才返回。当socket缓冲区中的数据量小于期望读取的数据量时,返回实际读取的字节数。当sockt的接收缓冲区中的数据大于期望读取的字节数时,读取期望读取的字节数,返回实际读取的长度。

    对于非阻塞socket而言,socket的接收缓冲区中有没有数据,read调用都会立刻返回。接收缓冲区中有数据时,与阻塞socket有数据的情况是一样的,如果接收缓冲区中没有数据,则返回错误号为EWOULDBLOCK,表示该操作本来应该阻塞的,但是由于本socket为非阻塞的socket,因此立刻返回。遇到这样的情况,可以在下次接着去尝试读取。如果返回值是其它负值,则表明读取错误。

    因此,非阻塞的read调用一般这样写:

    if ((nread = read(sock_fd, buffer, len)) < 0)
    {
       if (errno == EWOULDBLOCK)
       {
           return 0;	//表示没有读到数据
        }
       else return -1; 	//表示读取失败
    }
    else return nread;	//读到数据长度
    

    读取指定字节数据可封装readn()函数为:

    ssize_t readn(int fd, void *vptr, size_t n)
    {
    	int32 nleft = 0;
    	int32 nread = 0;
    	int8 *pread_buf = NULL;
    
    	pread_buf = (int8 *)vptr;
    	nleft = n;
    
    	while (nleft > 0)
    	{
    		nread = recv(fd,  (char *)pread_buf, nleft, 0);
    		if (nread < 0)
    		{
    			if (EINTR == errno || EWOULDBLOCK == errno || EAGAIN == errno)
    			{
    				nread = 0;
    			}
    			else
    			{
    				return -1;
    			}
    		}
    		else if (nread == 0)
    		{
    			break;
    		}
    		else
    		{
    			nleft -= nread;
    			pread_buf += nread;
    		}
    	}
    	return (ssize_t)(n - nleft);
    }
    

    4.写操作

    对于阻塞Socket而言,如果发送缓冲区没有空间或者空间不足的话,write操作会直接阻塞住,如果有足够空间,则拷贝所有数据到发送缓冲区,然后返回.

    对于写操作write,原理和read是类似的,非阻塞socket在发送缓冲区没有空间时会直接返回错误号EWOULDBLOCK,表示没有空间可写数据,如果错误号是别的值,则表明发送失败。如果发送缓冲区中有足够空间或者是不足以拷贝所有待发送数据的空间的话,则拷贝前面N个能够容纳的数据,返回实际拷贝的字节数。

    非阻塞的write操作一般写法是:

    ssize_t writen(int connfd, const void *pbuf, size_t nums)
    {
    	int32 nleft = 0;
    	int32 nwritten = 0;
    	char *pwrite_buf = NULL;
    
    	if ((connfd <= 0) || (NULL == pbuf) || (nums < 0))
    	{
    		return -1;
    	}
    
    	pwrite_buf = (char *)pbuf;
    	nleft = nums;
    
    	while(nleft>0)
    	{
    		if (-1 == (nwritten = send(connfd, pwrite_buf, nleft, MSG_NOSIGNAL)))
    		{
    			if (EINTR == errno || EWOULDBLOCK == errno || EAGAIN == errno)
    			{
    				nwritten = 0;
    			}
    			else
    			{
    				errorf("%s,%d, Send() -1, 0x%x\n", __FILE__, __LINE__, errno);
    				return -1;
    			}
    		}
    		nleft -= nwritten;
    		pwrite_buf += nwritten;
    	}
    
    	return(nums);
    }
    

    尤其注意非阻塞的socket,在建立连接时要兼容处理返回EINPROGRESS情况,在接收连接、读操作、写操作时要兼容处理返回EWOULDBLOCK错误码的情况,以上情况并非连接异常,而是网络延时或者套接字缓冲区满造成的,一般不宜做异常处理(如返回异常关闭套接字)。

    简单点理解就是:

    1. 同步,就是我调用一个功能,该功能没有结束前,我死等结果。
    2. 异步,就是我调用一个功能,不需要知道该功能结果,该功能有结果后通知我(回调通知)。
    3. 阻塞,就是调用我(函数),我(函数)没有接收完数据或者没有得到结果之前,我不会返回。
    4. 非阻塞,就是调用我(函数),我(函数)立即返回,通过select通知调用者

    同步IO和异步IO的区别就在于:数据拷贝的时候进程是否阻塞
    阻塞IO和非阻塞IO的区别就在于:应用程序的调用是否立即返回

    二、设置套接字阻塞或非阻塞

    socket()函数创建的套接字默认是阻塞的,可通过如下方式将socket套接字设置为非阻塞:

    方式一:fcntl函数 //文件控制函数

    //设置套接字非阻塞
    if (fcntl(sock_v4, F_SETFL, fcntl(sock_v4, F_GETFL) | O_NONBLOCK) == -1)
    {
    	printf( "set sockfd nonblock -1, errno=%d\n", errno);	//fcntl返回-1表示失败
    }
    
    

    方式二:ioctl //设备控制接口函数

    int b_on = 1;
    ioctl (fd, FIONBIO, &b_on);
    

    设置套接字阻塞后,也可再设置会阻塞:

    方式一:fcntl函数 //文件控制函数

    //设置套接字非阻塞
    if (fcntl(sock_v4, F_SETFL, fcntl(sock_v4, F_GETFL) & ~O_NONBLOCK) == -1)
    {
    	printf( "set sockfd nonblock -1, errno=%d\n", errno);	//fcntl返回-1表示失败
    }
    
    

    方式二:ioctl //设备控制接口函数

    int b_on = 0;
    ioctl (fd, FIONBIO, &b_on);
    

    针对fcntl函数可做如下封装:

    /** @fn	set_nonblock(int32 sock_fd, bool b_set)
     *  @brief	设置socket fd为阻塞模式或者非阻塞模式
     *  @param[in]  sock_fd 已经连接成功的连接fd。范围:大于0
     *  @param[in]  b_set 是否设置为非阻塞模式。0-否,非0-是;
     *  @param[out] 无
     *  @return 成功返回0;失败返回-1
     */
    int set_nonblock(int32 sock_fd, bool b_set)
    {
    	int32 flags = 0;
    
    	flags = fcntl(sock_fd, F_GETFL, 0);
    	if (-1 == flags)
    	{
    		printf("get sockfd flag -1, errno=%d\n", errno);
    		return -1;
    	}
    
    	if (b_set)
    	{
    		if (fcntl(sock_fd, F_SETFL, flags | O_NONBLOCK) == -1)
    		{
    			printf("set sockfd nonblock -1, errno=%d\n", errno);
    			return -1;
    		}
    	}
    	else
    	{
    		if (fcntl(sock_fd, F_SETFL, flags & (~O_NONBLOCK)) == -1)
    		{
    			printf("set sockfd nonblock -1, errno=%d\n", errno);
    			return -1;
    		}
    	}
    
    	return 0;
    }
    

    三、Linux fcntl函数介绍

    功能描述:根据文件描述词来操作文件的特性。
    fcntl系统调用可以用来对已打开的文件描述符进行各种控制操作以改变已打开文件的的各种属性。

    头文件:

    #include <unistd.h>
    #include <fcntl.h>
    

    函数原型:

    int fcntl(int fd, int cmd);
    int fcntl(int fd, int cmd, long arg);        
    int fcntl(int fd, int cmd, struct flock *lock);
    

    参数:

    fd:文件描述词。 
    cmd:操作命令。 
    arg:供命令使用的参数。 
    lock:同上。
    

    描述:
    fcntl()针对(文件)描述符提供控制。参数fd是被参数cmd操作(如下面的描述)的描述符。针对cmd的值,fcntl能够接受第三个参数arg。

    返回值: 与命令有关。如果出错,所有命令都返回-1,如果成功则返回某个其他值。
    下列三个命令有特定返回值:F_DUPFD,F_GETFD,F_GETFL以及F_GETOWN。
    F_DUPFD 返回新的文件描述符
    F_GETFD 返回相应标志
    F_GETFL , F_GETOWN 返回一个正的进程ID或负的进程组ID

    fcntl函数有5种功能:

    1. 复制一个现有的描述符(cmd=F_DUPFD).
    2. 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).
    3. 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
    4. 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
    5. 获得/设置记录锁(cmd=F_GETLK , F_SETLK或F_SETLKW).

    cmd 选项:
    F_DUPFD:返回一个如下描述的(文件)描述符:
        (1)最小的大于或等于arg的一个可用的描述符
        (2)与原始操作符一样的某对象的引用
        (3)如果对象是文件(file)的话,返回一个新的描述符,这个描述符与arg共享相同的偏移量(offset)
        (4)相同的访问模式(读,写或读/写)
        (5)相同的文件状态标志(如:两个文件描述符共享相同的状态标志)
        (6)与新的文件描述符结合在一起的close-on-exec标志被设置成交叉式访问execve(2)的系统调用
    F_GETFD:取得与文件描述符fd联合close-on-exec标志,类似FD_CLOEXEC.如果返回值和FD_CLOEXEC进行与运算结果是0的话,文件保持交叉式访问exec(),       否则如果通过exec运行的话,文件将被关闭(arg被忽略)
    F_SETFD:设置close-on-exec旗标。该旗标以参数arg的FD_CLOEXEC位决定。
    F_GETFL:取得fd的文件状态标志,如同下面的描述一样(arg被忽略)
    F_SETFL:设置给arg描述符状态标志,可以更改的几个标志是:O_APPEND, O_NONBLOCK,O_SYNC和O_ASYNC。
    F_GETOWN:取得当前正在接收SIGIO或者SIGURG信号的进程id或进程组id,进程组id返回成负值(arg被忽略)
    F_SETOWN:设置将接收SIGIO和SIGURG信号的进程id或进程组id,进程组id通过提供负值的arg来说明,否则,arg将被认为是进程id

    F_GETFL和F_SETFL的标志如下面的描述:
    O_NONBLOCK:非阻塞I/O;如果read(2)调用没有可读取的数据,或者如果write(2)操作将阻塞,read或write调用返回-1和EAGAIN错误      O_APPEND :强制每次写(write)操作都添加在文件大的末尾,相当于open(2)的O_APPEND标志
    O_DIRECT :最小化或去掉reading和writing的缓存影响.系统将企图避免缓存你的读或写的数据.
    如果不能够避免缓存,那么它将最小化已经被缓存了的数 据造成的影响.如果这个标志用的不够好,将大大的降低性能
    O_ASYNC :当I/O可用的时候,允许SIGIO信号发送到进程组,例如:当有数据可以读的时候

    注意: 在修改文件描述符标志或文件状态标志时必须谨慎,先要取得现在的标志值,然后按照希望修改它,最后设置新标志值。不能只是执行F_SETFD或F_SETFL命令,这样会关闭以前设置的标志位。

    参考资料:
    https://www.cnblogs.com/sunziying/p/6501045.html

    展开全文
  • 简单理解什么是同步阻塞/同步非阻塞,异步阻塞/异步非阻塞 举个栗子 1、你在家做饭,用普通的汤锅,米放进去,就站在锅边,傻等饭熟。——这叫同步阻塞 是不是觉得浪费了大量的时间,于是你想提高时间的利用效率。 2...
  • 同步异步阻塞阻塞杂记

    万次阅读 2017-11-10 12:19:18
    gevent实现的协程是同步非阻塞还是异步非阻塞? gevent是一个使用完全同步编程模型的可扩展的异步I/O框架。 IO是不是阻塞的和协程是没有关系的,python本来就能支持非阻塞IO, 比如在linux只要用API,更改了...
  • socket阻塞与非阻塞,同步与异步、I/O模型

    万次阅读 多人点赞 2012-04-12 16:35:42
    socket阻塞与非阻塞,同步与异步 作者:huangguisu 1. 概念理解  在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式: 同步:  所谓同步,就是在发出一个...
  • 阻塞和非阻塞

    千次阅读 2018-07-02 23:10:16
    在介绍阻塞和非阻塞之前,我们先来了解一下多线程间一个重要的概念——临界区。临界区——一种公有的资源或者共享数据,它可以被多个线程使用。临界区资源一次只能被一个线程使用,其它线程必须等待上一个线程执行...
  • 面试问我同步、异步、阻塞阻塞,答的并不是很好,回来后必然要出一篇博文加深印象了
  • 阻塞IO与非阻塞IO

    万次阅读 2018-08-20 21:38:09
    在《Unix网络编程》一书中提到了五种IO模型,分别是:阻塞IO、非阻塞IO、多路复用IO、信号驱动IO以及异步IO。 1、阻塞IO模型  最传统的一种IO模型,即在读写数据过程中会发生阻塞现象。  当用户线程发出IO请求...
  • 网上闲逛技术贴,看见一个关于理解同步阻塞、同步非阻塞、异步阻塞、异步非阻塞比较风趣的故事,简单明了,很容易理解,因此记录一下,希望更多人能看见。 故事原文: 老张爱喝茶,废话不说,煮开水。出场人物:老张...
  • 同步异步+阻塞阻塞-一述

    万次阅读 2018-07-11 08:33:02
    前言在实际的开发中,我们经常会听到同步,异步,阻塞,非阻塞这些编程概念,每次遇到的时候都会蒙圈,然后就各种查网上似是而非的资料,结果越查越迷糊,大部分文章都千篇一律,没有说到本质上的区别,所以下次再...
  • 阻塞赋值和非阻塞赋值

    千次阅读 2019-05-11 19:17:06
    一、阻塞赋值 阻塞赋值的操作符为=; 阻塞赋值的执行可以认为是只有一个步骤的操作:所谓阻塞的概念是指在同一个always块中,其后面的赋值语句从概念上(即使不设定延迟)是在前一句赋值语句结束后在开始赋值的。 ...
  • CSS阻塞与JS阻塞

    千次阅读 2019-05-23 15:00:06
    CSS阻塞与JS阻塞 https://www.cnblogs.com/hgonlywj/p/4857151.html https://www.cnblogs.com/bibiafa/p/9364986.html
  • 串口阻塞和非阻塞

    千次阅读 2018-12-10 13:46:26
    阻塞方式是文件读写操作的默认方式,但是应用程序员可通过使用O_NONBLOCK 标志来人为  的设置读写操作为非阻塞方式 .( 该标志定义在 &lt; linux/fcntl.h &gt; 中,在打开文件时指定 ) . 阻塞:  在...
  • (1)阻塞block  所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回。  例如socket编程中connect、accept、recv...
  • 同步异步和阻塞阻塞

    千次阅读 2016-07-21 15:40:02
    下面的内容不一定对,但是有利于理解同步异步和阻塞阻塞。 同步异步和阻塞阻塞是从不同层面定义的。同步异步指的是要不要等待I/O结果同步:同步最好理解,发出I/O请求之后,会一直处于等待状态,直到得到I/O结果...
  • NIO同步阻塞与同步非阻塞

    万次阅读 2019-02-27 15:09:26
    BIO与NIO IO与NIO区别:其本质就是阻塞和非阻塞的区别。...IO为同步阻塞形式,NIO为同步非阻塞形式,NIO并没有实现异步,在JDK1.7升级NIO库包,支持异步非阻塞。 BIO:同步阻塞式IO,服务器实现模式为一个连接...
  • 阻塞队列和非阻塞队列

    千次阅读 2018-06-21 14:27:56
    1.阻塞队列和非阻塞队列的区别:阻塞队列可以阻塞,非阻塞队列不能阻塞,只能使用队列wait(),notify()进行队列消息传送。而阻塞队列当队列里面没有值时,会阻塞直到有值输入。输入也一样,当队列满的时候,会阻塞,...
  • 通信:阻塞和非阻塞

    2019-04-25 15:41:28
    Socket通信有阻塞和非阻塞两者模式; 阻塞模式: 阻塞模式从某种意义上来说也是一种同步模式,当发送消息一端,未收到服务器确认收到消息的返回时,线程会被一直挂起,直到接收到反馈;所以,当Socket处于阻塞模式时...
  • 阻塞队列与非阻塞队列

    千次阅读 2017-08-14 08:48:45
    阻塞队列与非阻塞队列
  • css阻塞与js阻塞

    2018-03-09 08:24:04
    js的阻塞特性:所有浏览器在下载JS的时候,会阻止一切其他活动,比如其他资源的下载,内容的呈现等等。直到JS下载、解析、执行完毕后才开始继续并行下载其他资源并呈现内容。为了提高用户体验,新一代浏览器都支持...
  • Linux阻塞与非阻塞

    千次阅读 2019-05-13 17:42:20
    1.阻塞(block)概念:指进程或线程在执行设备操作或管道,或则网络时,不能获取到资源就被挂起, 直到满足可操作的条件后在进行操作,被挂起的进程进入休眠状态,从运行队列移走,直到 等待的条件满足才继续执行。也...
  • 阻塞式I/O 默认情况下,所有的套接字的方法都是阻塞的,如上面的accept、recv。 对应的代码如下: package com.morris.bio; import java.io.BufferedReader; import java.io.IOException; import java.io....
  • 同步异步,阻塞阻塞是一些非常常见的概念,但是对于开发者来说往往是用到了很难说清楚。 笔者专门整理了下这方面的概念,作此文以记之。 这部分内容可能存在一些争议,如有不同意见欢迎评论交流。 概念 个人理解...
  • 阻塞和非阻塞的概念

    2018-11-08 14:36:49
    阻塞和非阻塞的概念 1.补充导读:阻塞和非阻塞的概念 1)阻塞: ATM排队取款,你只能等待。 例如:排队打饭 2)非阻塞: 柜台取款,取个号,然后坐在椅子上做其它事,等号广播会通知你办理,没到号你就不能去,你可以...
  • http阻塞模式和非阻塞模式

    万次阅读 2016-11-26 18:15:49
    同步会阻塞线程,一直等到网络回调,而异步不会阻塞 同步可以设置TimeOut,异步则对TimeOut无视 异步需要自己对TimeOut进行计算,通过监听接收到网络回调 在Unity的表现 一般做连接launch sever的时候用http请求 ...
  • 同步异步+阻塞阻塞-三述

    万次阅读 2018-11-27 14:56:19
    在IT圈混饭吃,不管你用什么编程语言、从事前端还是后端,阻塞、非阻塞、异步、同步这些概念,都需要清晰地掌握,否则,怎么与面试官谈笑风生(chui niu pi)?但是,掌握这些概念又不是非常容易,尤其对非科班出身...
  • Redis阻塞

    千次阅读 2018-07-14 01:03:41
    Redis阻塞 Redis阻塞 发现阻塞 内在原因 API或数据结构使用不合理 CPU饱和 持久化阻塞 外在原因 CPU竞争 内存交换 网络问题 发现阻塞 线上应用服务最先感知到,可在应用方加入异常统计并通过邮件、...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 133,141
精华内容 53,256
关键字:

阻塞