第一步:调通PS侧网口GEM0(Xilinx BSP默认配好)。
第二步:调通PS侧网口GEM1(见前一篇文档:开发笔记(1))。
第三步:调通PL侧网口(本文阐述)。
第四步:在PL侧网口上验证Jumbo Frame特性,并在应用层适配gigE Vision协议。
根据《xapp1082》可知,PL侧的PHY支持1000Base-X和SGMII两种配置,这两种配置对应两种不同的PHY引脚接口(连接到MAC)。而我们的hdf文件使用的是1000Base-X的配置。
关于网口的Linux驱动,我们在官网找到一份资料: Xilinx Wiki - Zynq PL Ethernet 。资料很长,我们只看与我们相关的2.4.1 PL Ethernet BSP installation for 1000Base-X”这一章节就可以了。
首先导入FPGA设计同事提供的hdf文件:
在d出的图形界面里,进入Subsystem AUTO Hardware Settings——Ethernet Settings——Primary Ethernet,确认可以看到PL侧网络设备axi_ethernet_0,说明hdf文件里已包含了必要的网口硬件信息:
上图中被选中的网口将成为Linux上的设备eth0。这里我们默认选择ps7_ethernet_0,即使用GEM0作为首选网口。
启用Xilinx AXI Ethernet驱动
进入Device Drivers -- Network device support – 选中Xilinx AXI Ethernet(以及Xilinx Ethernet GEM,这是PS侧网口的驱动)
进入Networking support – 选中 Random ethaddr if unset
进入Device Drivers -- Network device support -- PHY Device support and infrastructure – 启用Drivers for xilinx PHYs
进入~~~~Device Drivers -- DMA Engine Support -– 禁用~~~~Xilinx AXI DMAS Engine~~~ (对应的配置项名为 ~~ CONFIG_XILINX_DMA ~~~)
注意: Xilinx Wiki里对设备树节点的引用有误(&axi_ethernet),导致编译报错,应改为&axi_ethernet_0。
注:PL-ETH驱动所在路径:<project>/build/tmp/work-shared/plnx_arm/kernel-source/drivers/net/ethernet/xilinx/xilinx_axienet_main.c和xilinx_axienet_mdio.c。对应的内核配置项为CONFIG_NET_VENDOR_XILINX和CONFIG_XILINX_AXI_EMAC。
启用ethtool和tcpdump(调试用,非必须):
然后将生成的BOOT.BIN和image.ub拷贝到SD卡根目录下,将SD卡插入板子上,上电运行。
上电后,使用ifconfig eth1查看网口信息,观察MAC地址与设置的一致,且ifconfig eth1 192.168.1.11 up没有报错。
测试网络通路:ping PC是通的。说明网口工作正常。
Linux下eth1(即PL-ETH)的MAC地址有误
问题描述:
开机打印:
注意:
MAC地址是错的,驱动里解析出的是GEM0的MAC地址。
试验发现,即使在system-user.dtsi里不写local-mac-address,也照样解析出的是GEM0的MAC。
而将system-user.dtsi里的local-mac-address改名为pl-mac-address,并将驱动里解析的字符串也对应更改为pl-mac-address,则可以正确解析出来:
Passing MAC address to kernel via Device Tree Blob and U-Boot:
http://zedboard.org/content/passing-mac-address-kernel-device-tree-blob
通过更改u-boot环境变量和设备树,为每个板子设置一个独特的MAC地址:
https://www.xilinx.com/support/answers/53476.html
U-Boot里的环境变量ethaddr会覆盖掉设备树里pl-eth的local-mac-addr字段,从而影响Linux启动后的网卡MAC地址;
但U-Boot里的环境变量ipaddr不会对Linux启动后的配置产生任何影响。因为设备树里根本就没有关于IP地址的配置。
phy-mode怎么会是sgmii?查了下官方的提供的BSP里,也是“sgmii”。说明这个没问题。具体原因不清楚。
@TODO: 设备树里的中断号的顺序如何影响功能?
为何读出来的IRQ号不对呢?这是因为这里读到的不是硬件的中断号,而是经过系统映射之后的软件IRQ number。两者不具有线性关系。
关于中断号的疑问:
Linux上的网口eth0、eth1的顺序,似乎是按照phy地址从小到大来排布的。
Xilinx xapp1082-zynq-eth.pdf (v5.0) July 16, 2018
https://www.xilinx.com/support/documentation/application_notes/xapp1082-zynq-eth.pdf
Xilinx Wiki - Zynq PL Ethernet:
https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18841633/Zynq+PL+Ethernet
Xilinx Wiki - Linux Drivers:
https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18841873/Linux+Drivers
Xilinx Wiki - Linux Drivers - Macb Driver:
https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18841740/Macb+Driver
Xilinx Wiki - Zynq Ethernet Performance:
https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18841743/Zynq+Ethernet+Performance
查到关于Jumbo frame MTU的定义,当前值为9000,可否改大一些?
驱动源码里关于jumbo frame的说明:
设置MTU为9000,发现ping包最大长度只能设为ping 192.168.1.10 -s 1472
https://lore.kernel.org/patchwork/patch/939535/
【完】
首先我们要对硬盘分区的基本概念进行一些初步的了解,硬盘的分区主要分为基本分区(primary partion)和扩充分区(extensionpartion)两种,基本分区和扩充分区的数目之和不能大于四个。且基本分区可以马上被使用但不能再分区。扩充分区必须再进行分区后才能使用,也就是说它必须还要进行二次分区。那么由扩充分区再分下去的是什么呢?它就是逻辑分区(logical
partion),况且逻辑分区没有数量上限制。
在学习linux安装时进行linux安装分区时的笔记,暂时记录下来,以供自己查阅,虚拟机共 12GB.
首先分区
第一分区:/boot 固定大小,100M,强制为主分区,引导程序。
第二分区:/ 根目录 4000M
第三分区:/usr 4500M,相当于windows C盘下Program Files,装软件用的。
第四分区:/var 1500M,日志存放的地方,用户登陆,读取磁盘log。
第五分区:选择文件系统类型 swap,1200M。内存置换空间,虚拟内存。
第六分区:/www 500M,放网站的。随便加的。
其中SWAP分区在真实内存的1~1.5之间为宜,物理内存越大,可设置相对小一些;
对习惯于使用dos或windows的用户来说,有几个分区就有几个驱动器,并且每个分区都会获得一个字母标识符,然后就可以选用这个字母来指定在这个分区上的文件和目录,它们的文件结构都是独立的,非常好理解。但对这些初上手
red hat linux的用户,可就有点恼人了。因为对red hat
linux用户来说无论有几个分区,分给哪一目录使用,它归根结底就只有一个根目录,一个独立且唯一的文件结构。red hat
linux中每个分区都是用来组成整个文件系统的一部分,因为它采用了一种叫“载入”的处理方法,它的整个文件系统中包含了一整套的文件和目录,且将一个分区和一个目录联系起来。这时要载入的一个分区将使它的存储空间在一个目录下获得。
对windows用户来说, *** 作系统必须装在同一分区里,它是商业软件! 所以你没有选择的余地!对red hat linux来说,你有了较大的选择余地,你可以把系统文件分几个区来装(必须要说明载入点),也可以就装在同一个分区中(载入点是“/”)。
下面从这两个方面入手,来讲解这个困扰大家的问题。
Linux 的分区规定
1. 设备管理
在 Linux 中,每一个硬件设备都映射到一个系统的文件,对于硬盘、光驱等 IDE 或 SCSI 设备也不例外。Linux 把各种 IDE 设备分配了一个由 hd 前缀组成的文件;而对于各种 SCSI 设备,则分配了一个由 sd 前缀组成的文件。
对于ide硬盘,驱动器标识符为“hdx~”,其中“hd”表明分区所在设备的类型,这里是指ide硬盘了。“x”为盘号(a为基本盘,b为基本从属盘,c为辅助主盘,d为辅助从属盘),“~”代表分区,前四个分区用数字1到4表示,它们是主分区或扩展分区,从5开始就是逻辑分区。例,hda3表示为第一个ide硬盘上的第三个主分区或扩展分区,hdb2表示为第二个ide硬盘上的第二个主分区或扩展分区。对于scsi硬盘则标识为“sdx~”,scsi硬盘是用“sd”来表示分区所在设备的类型的,其余则和ide硬盘的表示方法一样,不在多说。
例如,第一个 IDE 设备,Linux 就定义为 hda;第二个 IDE 设备就定义为 hdb;下面以此类推。而 SCSI 设备就应该是 sda、sdb、sdc 等。
2. 分区数量
要进行分区就必须针对每一个硬件设备进行 *** 作,这就有可能是一块IDE硬盘或是一块SCSI硬盘。对于每一个硬盘(IDE 或 SCSI)设备,Linux 分配了一个 1 到 16 的序列号码,这就代表了这块硬盘上面的分区号码。
例如,第一个 IDE 硬盘的第一个分区,在 Linux 下面映射的就是 hda1,第二个分区就称作是 hda2。对于 SCSI 硬盘则是 sda1、sdb1 等。
3. 各分区的作用
在 Linux 中规定,每一个硬盘设备最多能有 4个主分区(其中包含扩展分区)构成,任何一个扩展分区都要占用一个主分区号码,也就是在一个硬盘中,主分区和扩展分区一共最多是 4 个。
对于早期的 DOS 和 Windows(Windows 2000 以前的版本),系统只承认一个主分区,可以通过在扩展分区上增加逻辑盘符(逻辑分区)的方法,进一步地细化分区。
主分区的作用就是计算机用来进行启动 *** 作系统的,因此每一个 *** 作系统的启动,或者称作是引导程序,都应该存放在主分区上。
这就是主分区和扩展分区及逻辑分区的最大区别。
我们在指定安装引导 Linux 的 bootloader 的时候,都要指定在主分区上,就是最好的例证。
Linux 规定了主分区(或者扩展分区)占用 1 至 16 号码中的前 4 个号码。以第一个 IDE 硬盘为例说明,主分区(或者扩展分区)占用了 hda1、hda2、hda3、hda4,而逻辑分区占用了 hda5 到 hda16 等 12 个号码。
因此,Linux 下面每一个硬盘总共最多有 16 个分区。
对于逻辑分区,Linux 规定它们必须建立在扩展分区上(在 DOS 和 Windows 系统上也是如此规定),而不是主分区上。
因此,我们可以看到扩展分区能够提供更加灵活的分区模式,但不能用来作为 *** 作系统 的引导。 除去上面这些各种分区的差别,我们就可以简单地把它们一视同仁了。
4. 分区指标
对于每一个 Linux 分区来讲,分区的大小和分区的类型是最主要的指标。容量的大小读者很容易理解,但是分区的类型就不是那么容易接受了。分区的类型规定了这个分区上面的文件系统的格式。
Linux 支持多种的文件系统格式,其中包含了我们熟悉的FAT32、FAT16、NTFS、HP-UX,以及各种 Linux 特有的 Linux Native和 Linux Swap分区类型。
在 Linux 系统中,可以通过分区类型号码来区别这些不同类型的分区。各种类型号码在介绍Fdisk的使用方式的时候将会介绍。
5 常用分区
/boot分区,它包含了 *** 作系统的内核和在启动系统过程中所要用到的文件,建这个
分区是有必要的,因为目前大多数的pc机要受到bios的限制,况且如果有了一个单独的/boot启动分区,即使主要的根分区出现了问题,计算机依然能够
启动。这个分区的大小约在50mb—100mb之间。但是如果想用lilo启动red hat
linux系统的话,含有/boot的分区必须完全在柱面1023以下。又由于8gb后的数据lilo不能读取,所以red hat
linux要安装在8gb的区域以内。
/usr分区,是red hat linux系统存放软件的地方,如有可能应将最大空间分给它。
/home分区,是用户的home目录所在地,这个分区的大小取决于有多少用户。如
果是多用户共同使用一台电脑的话,这个分区是完全有必要的,况且根用户也可以很好地控制普通用户使用计算机,如对用户或者用户组实行硬盘限量使用,限制普
通用户访问哪些文件等。其实单用户也有建立这个分区的必要,因为没这个分区的话,那么你只能以根用户的身份登陆系统,这样做是危险的,因为根用户对系统有
绝对的使用权,可一旦你对系统进行了误 *** 作,麻烦也就来了。
/var/log分区,是系统日志记录分区,如果设立了这一单独的分区,这样即使系统的日志文件出现了问题,它们也不会影响到 *** 作系统的主分区。
/tmp分区,用来存放临时文件。这对于多用户系统或者网络服务器来说是有必要的。
这样即使程序运行时生成大量的临时文件,或者用户对系统进行了错误的 *** 作,文件系统的其它部分仍然是安全的。因为文件系统的这一部分仍然还承受着读写 ***
作,所以它通常会比其它的部分更快地发生问题。
/bin分区,存放标准系统实用程序。
/dev分区,存放设备文件。
/opt分区,存放可选的安装的软件。
/sbin分区,存放标准系统管理文件。
上面介绍了几个常用的分区,一般来说我们需要一个swap分区,一个/boot分区,一个/usr分区,一个/home 分区,一个/var/log分区。当然这没有什么规定,完全是依照你个人来定的。但记住至少要有两个分区,一个swap分区,一个/分区。
Fdisk 使用详解
下面通过介绍 Fdisk 的使用方法,来巩固上面所学到的各种关于 Linux 分区的知识。
Fdisk 是各种 Linux 发行版本中最常用的分区工具,是被定义为 Expert 级别的分区工具,它让初学者有点望而却步。
1. Fdisk 参数说明
运行Fdisk的时候,首先映入眼帘的是欢迎界面,用户通过在这个界面中输入命令参数来 *** 作Fdisk。用户通过提示键入 “m”,可以显示
Fdisk 命令各个参数的说明。 读者可以看到 Fdisk 有很多参数,可是经常使用的就是几个,如果读者熟练掌握这几个参数就可以流畅地运用
Fdisk,对 Linux 的硬盘进行分区。我们先简单介绍各个参数的意义,然后详细说明几个重点参数。
用户在 Linux 中进行分区的时候,最常用的参数分别是 d、l、m、n、p、q、t、w 等。
2.用 Fdisk 进行分区
在 Linux 分区过程,一般是先通过 p 参数来显示出硬盘分区表信息,然后根据信息确定将来的分区。如果想完全改变硬盘的分区格式,就可以通过 d 参数一个个删除存在的硬盘分区。
例如
d1,d2。 删除完毕,就可以通过 n 参数来增加新的分区。当按下 “n”
后,我们就可以看到新增的分区。这里要选择新建的分区类型,是主分区还是扩展分区;然后选择 p 或是
e。它们的区别在上文中已经说明。然后就是设置分区的大小。需要提醒注意的是,如果硬盘上有扩展分区,就只能增加逻辑分区,不能增加扩展分区了,在增加分区的时候,其类型都是默认的
Linux Native,如果需要把其中的某些分区改变为其它类型,例如 Linux Swap 或 FAT32 等,可以通过命令 t 来改变,
当按下 “t” 改变分区类型的时候,系统会提示要改变哪个分区,并且改变为什么类型(如果想知道系统所支持的分区类型,键入 l)。Linux
所支持的分区类型号码和其对应的分区类型,可以参考表 2(这些信息可以用 l 命令得到)。改变完了分区类型,就可以按下
“w”,保存并且退出。如果不想保存,那么可以选择 “q” 直接退出。
fdisk 是一款强大的磁盘 *** 作工具,来自util-linux软件包,我们在这里只说他如何查看磁盘分区表及分区结构;参数 -l ,通过-l 参数,能获得机器中所有磁盘的个数,也能列出所有磁盘分区情况;
[root@localhost beinan]# fdisk -l
Disk /dev/hda: 80.0 GB, 80026361856 bytes
255 heads, 63 sectors/track, 9729 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Device Boot Start End Blocks Id System
/dev/hda1 * 1 765 6144831 7 HPFS/NTFS
/dev/hda2 766 2805 16386300 c W95 FAT32 (LBA)
/dev/hda3 2806 7751 39728745 5 Extended
/dev/hda5 2806 3825 8193118+ 83 Linux
/dev/hda6 3826 5100 10241406 83 Linux
/dev/hda7 5101 5198 787153+ 82 Linux swap / Solaris
/dev/hda8 5199 6657 11719386 83 Linux
/dev/hda9 6658 7751 8787523+ 83 Linux
在上面Blocks中,表示的是分区的大小,Blocks的单位是byte
,我们可以换算成M,比如第一个分区/dev/hda1的大小如果换算成M,应该是6144831/1024=6000M,也就是6G左右,其实没有这么
麻烦,粗略的看一下把小数点向前移动三位,就知道大约的体积有多大了;
System 表示的文件系统,比如/dev/hda1 是NTFS格式的;/dev/hda2 表示是fat32格式的文件系统;.
在此例中,我们要特别注意的是/dev/hda3分区,这是扩展分区;他下面包含着逻辑分区,其实这个分区相当于一个容器;从属于她的有 hda5,hda6,hda7,hda8,hda9 ;
我们还注意到一点,怎么没有hda4呢?为什么hda4没有包含在扩展分区?一个磁盘最多有四个主分区;
hda1-4算都是主分区;hda4不可能包含在扩展分区里,另外扩展分区也算主分区;在本例中,没有hda4这个分区,当然我们可以把其中的一个分区设
置为主分区,只是我当时分区的时候没有这么做而已;
再仔细统计一下,我们看一看这个磁盘是不是还有空间?hda1+hda2+hda3=实际已经分区的体积,所以我们可以这样算
hda1+hda2+hda3=6144831+16386300+39728745 = 62259876
(b),换算成M单位,小数点向前移三位,所以目前已经划分好的分区大约占用体积是62259.876(M),其实最精确的计算
62259876/1024=60800.67(M);而这个磁盘大小是80.0 GB
(80026361856byte),其实实际大小也就是78150.744(M);通过我们一系列的计算,我们可以得出这个硬盘目前还有使用的空间;大约还有18G未分区的空间;
我们也可以指定fdisk -l 来查看其中一个硬盘的分区情况;
[root@localhost beinan]# fdisk -l /dev/sda
Disk /dev/sda: 60.0 GB, 60011642880 bytes
64 heads, 32 sectors/track, 57231 cylinders
Units = cylinders of 2048 * 512 = 1048576 bytes
Device Boot Start End Blocks Id System
/dev/sda1 1 57231 58604528 83 Linux
通过上面情况可以知道,在/dev/sda 这个磁盘中,只有一个分区;使用量差不多是百分百了;
我们还可以来查看 /dev/hda的
[root@localhost beinan]# fdisk -l /dev/hda
自己试试看?
我们也可以通过: cat /proc/partitions查看目前机器中的所有磁盘及分区情况
df 命令;
df 是来自coreutils 软件包,系统安装时,就自带的;我们通过这个命令可以查看磁盘的使用情况以及文件系统被挂载的位置;
举例:
[root@localhost beinan]# df -lh
Filesystem 容量 已用 可用 已用% 挂载点
/dev/hda8 11G 6.0G 4.4G 58% /
/dev/shm 236M 0 236M 0% /dev/shm
/dev/sda1 56G 22G 35G 39% /mnt/sda1
我们从中可以看到,系统安装在/dev/hda8 ;还有一个56G的磁盘分区/dev/sda1挂载在 /mnt/sda1中;
其它的参数请参考 man df
通过以上两个方面的学习,相信对于初学者来说,分区已经不再是 Linux 进阶中的绊脚石了。
现在 *** 作系统都是采用虚拟存储器,那么对32位 *** 作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方)。 *** 作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。 为了保证用户进程不能直接 *** 作内核(kernel),保证内核的安全, *** 心系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间 。针对linux *** 作系统而言, 将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF) ,供内核使用,称为内核空间, 而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为用户空间。
文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述 指向文件的引用的抽象化概念 。文件描述符在形式上是一个非负整数。 实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表 。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的 *** 作系统。
刚才说了,对于一次IO访问(以read举例),数据会先被拷贝到 *** 作系统内核的缓冲区中,然后才会从 *** 作系统内核的缓冲区拷贝到应用程序的地址空间。所以说,当一个read *** 作发生时,它会经历两个阶段:
1、等待数据准备 (Waiting for the data to be ready)
2、将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)
正式因为这两个阶段,linux系统产生了下面 五种网络模式 的方案。
阻塞 I/O(blocking IO)
非阻塞 I/O(nonblocking IO)
I/O 多路复用( IO multiplexing)
异步 I/O(asynchronous IO)
信号驱动 I/O( signal driven IO)
注:由于signal driven IO在实际中并不常用,所以我这只提及剩下的四种IO Model。
阻塞 I/O(blocking IO)
在linux中,默认情况下所有的socket都是blocking,一个典型的读 *** 作流程大概是这样:
当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据(对于网络IO来说,很多时候数据在一开始还没有到达。比如,还没有收到一个完整的UDP包。这个时候kernel就要等待足够的数据到来)。这个过程需要等待,也就是说数据被拷贝到 *** 作系统内核的缓冲区中是需要一个过程的。而在用户进程这边,整个进程会被阻塞(当然,是进程自己选择的阻塞)。当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除block的状态,重新运行起来。
所以,blocking IO的特点就是在IO执行的两个阶段都被block了(内核阻塞读取数据,内核将数据复制到应用户态)。
非阻塞 I/O(nonblocking IO)
linux下,可以通过设置socket使其变为non-blocking。当对一个non-blocking socket执行读 *** 作时,流程是这个样子:
当用户进程发出read *** 作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。从用户进程角度讲 ,它发起一个read *** 作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read *** 作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回。
所以,nonblocking IO的特点是用户进程需要 不断的主动询问 kernel数据好了没有( 内核读取数据时,用户态不需要阻塞,内核将数据复制到用户态时,需要阻塞 )。
I/O 多路复用( IO multiplexing)
IO multiplexing就是我们说的select,poll,epoll,有些地方也称这种IO方式为event driven IO。select/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。它的基本原理就是 select,poll,epoll这个function会不断的轮询所负责的所有socket ,当某个socket有数据到达了,就通知用户进程。
当用户 进程调用了select , 那么整个进程会被block ,而同时,kernel会“监视”所有 select负责的socket(一个管理多个socket连接),当任何一个socket中的数据准备好了,select就会返回 。这个时候用户进程再调用read *** 作, 将数据从kernel拷贝到用户进程 。
所以,I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回。
这个图和blocking IO的图其实并没有太大的不同,事实上,还更差一些。 因为这里需要使用两个system call (select 和 recvfrom),而blocking IO只调用了一个system call (recvfrom) 。但是,用select的优势在于它可以同时处理多个connection。
所以,如果处理的 连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大 。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。)
在IO multiplexing Model中,实际中,对于每一个socket,一般都设置成为non-blocking,但是,如上图所示,整个用户的process其实是一直被block的。只不过process是被select这个函数block,而不是被socket IO给block。
总结:IO多路复用其实也是阻塞的,阻塞的地方在用当有socket连接有数据以后, 会阻塞知道数据从内核复制到用户态(第二步阻塞)。
异步 I/O(asynchronous IO)
inux下的asynchronous IO其实用得很少。先看一下它的流程:
用户进程发起read *** 作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read *** 作完成了。
总结:两个阶段都不需要用户进程干涉,内核将数据准备好以后通知用户态去读取
总结
blocking和non-blocking的区别
调用blocking IO会一直block住对应的进程直到 *** 作完成,而non-blocking IO在kernel还准备数据的情况下会立刻返回。
synchronous IO和asynchronous IO的区别
在说明synchronous IO和asynchronous IO的区别之前,需要先给出两者的定义。POSIX的定义是这样子的:
- A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes
- An asynchronous I/O operation does not cause the requesting process to be blocked
两者的区别就在于synchronous IO做”IO operation”的时候会将process阻塞。按照这个定义,之前所述的 blocking IO,non-blocking IO,IO multiplexing都属于synchronous IO 。
有人会说,non-blocking IO并没有被block啊。这里有个非常“狡猾”的地方,定义中所指的”IO operation”是指真实的IO *** 作,就是例子中的recvfrom这个system call。non-blocking IO在执行recvfrom这个system call的时候,如果kernel的数据没有准备好,这时候不会block进程。但是, 当kernel中数据准备好的时候,recvfrom会将数据从kernel拷贝到用户内存中,这个时候进程是被block了,在这段时间内,进程是被block的。
而asynchronous IO则不一样,当进程发起IO *** 作之后,就直接返回再也不理睬了,直到kernel发送一个信号,告诉进程说IO完成。在这整个过程中,进程完全没有被block。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)