异步I/O操作函数aio_xxx函数

本文介绍了POLLUX库中的异步I/O函数,如aio_read、aio_write等,并展示了如何在回调和线程上下文中使用它们,以及在echo服务中的应用实例。


前言

POXSIX提供了用于异步I/O的"aio_xxx"函数集。

名称功能
aio_read异步read
aio_write异步write
aio_fsync异步fsync
aio_error获取错误状态
aio_return获取返回值
aio_cancel请求取消
aio_suspend请求等待

异步IO示例

#include <aio.h>

#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
    if (argc < 2) return 1;
    struct aiocb cb;
    const struct aiocb* cblist[1];
    char buf[BUFSIZ];
    int fd, n;

    fd = open(argv[1], O_RDONLY);
    if (fd < 0) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    memset(&cb, 0, sizeof(struct aiocb));
    cb.aio_fildes = fd;
    cb.aio_buf = buf;
    cb.aio_nbytes = BUFSIZ-1;

    n = aio_read(&cb);
    if (n < 0) {
        perror("aio_read");
        exit(EXIT_FAILURE);
    }
    cblist[0] = &cb;
    n = aio_suspend(cblist, 1, NULL);
    if (n != 0) {
        perror("aio_read");
        exit(EXIT_FAILURE);
    }
    n = aio_return(&cb);
    if (n < 0) {
        perror("aio_return");
        exit(EXIT_FAILURE);
    }
    buf[n] = '\0';
    printf("%s\n", buf);
    return 0;
}

将aio_read, aio_suspend, aio_return 放在循环中时不能正确读取内容超过缓冲区大小的内容

带回调的异步IO

在回调函数的调用上,有信号和线程两种方式,下面时使用线程进行回调的方式。
如果用SIGEV_THREAD设置回调函数并调用aio_read,从系统内部来看,实际上是用多个线程来实现异步IO。

#include <aio.h>
// #include <asm-generic/siginfo.h>

#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

static void read_done(__sigval_t sigval)
{
    struct aiocb *cb;
    int n;
    cb = (struct aiocb*)(sigval.sival_ptr);
    if (aio_error(cb)  == 0)
    {
        n = aio_return(cb);
        if (n < 0)
        {
            perror("aio_return");
            exit(EXIT_FAILURE);
        }
        printf("%d %d ---\n%.*s", n, cb->aio_nbytes, cb->aio_nbytes, cb->aio_buf);
        exit(EXIT_SUCCESS);
    }
    return;
}

int main(int argc, char* argv[])
{
    if (argc < 2) return 1;
    struct aiocb cb;
    char buf[BUFSIZ] = {'\0'};
    int fd, n;

    fd = open(argv[1], O_RDONLY);
    if (fd < 0) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    memset(&cb, 0, sizeof(struct aiocb));
    cb.aio_fildes = fd;
    cb.aio_buf = buf;
    cb.aio_nbytes = BUFSIZ-1;
    cb.aio_sigevent.sigev_notify = SIGEV_THREAD;
    cb.aio_sigevent.sigev_notify_function = &read_done;
    cb.aio_sigevent.sigev_value.sival_ptr = &cb;

    n = aio_read(&cb);
    if (n < 0) {
        perror("aio_read");
        exit(EXIT_FAILURE);
    }
    
    select(0, NULL, NULL, NULL, NULL);
    return 0;
}

使用aio_read的echo服务


#include <aio.h>
// #include <asm-generic/siginfo.h>

#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <vector>

static void read_done(__sigval_t sigval)
{
    printf("read done tid: %d\n", gettid());
    struct aiocb *cb;
    int n;
    cb = (struct aiocb*)(sigval.sival_ptr);
    if (aio_error(cb)  == 0)
    {
        n = aio_return(cb);
        if (n < 0)
        {
            perror("aio_return");
            exit(EXIT_FAILURE);
        } else if (0 == n) {
            printf("client %d gone\n", cb->aio_fildes);
            aio_cancel(cb->aio_fildes, cb);
            close(cb->aio_fildes);
            free(cb);
            return;
        }
        printf("client %d (%d)\n", cb->aio_fildes, n);
        
        std::vector<char> buf(n+1, 0);
        memcpy(buf.data(), (void*)cb->aio_buf, n);
        printf("[%s]\n", buf.data());

        write(cb->aio_fildes, (void*)cb->aio_buf, n);
        aio_read(cb);
    }
    else
    {
        printf("aio_error\n");
    }
    return;
}
static void register_read(int fd)
{
    struct aiocb *cb;
    char* buf;
    printf("client register %d\n", fd);
    cb = ( struct aiocb *)malloc(sizeof(struct aiocb));
    buf = (char*)malloc(BUFSIZ);
    memset(cb, 0, sizeof(struct aiocb));
    cb->aio_fildes = fd;
    cb->aio_buf = buf;
    cb->aio_nbytes = BUFSIZ;
    cb->aio_sigevent.sigev_notify = SIGEV_THREAD;  // 每次以创建新线程的方式呼叫回调函数
    cb->aio_sigevent.sigev_notify_function = read_done;
    cb->aio_sigevent.sigev_value.sival_ptr = cb;//
    if (aio_read(cb) != 0)
    {
        perror("aio_read");
        return;
    }
}
int main(int argc, char* argv[])
{
    if (argc < 2) return 1;
    printf("pid: %d\n", gettid());
    struct sockaddr_in addr;
    int s = socket(PF_INET, SOCK_STREAM, 0);

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(9998);
    if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
        perror("bind");
        exit(EXIT_FAILURE);
    }
    listen(s, 5);
    for (;;)
    {
        int c = accept(s, NULL, 0);
        if (c < 0) continue;
        register_read(c);
    }
    return 0;
}

总结

<think>我们正在处理一个FPGA设计中的时钟约束冲突问题。具体错误是:时钟 'XSGMIIGT0TXCLK' 完全覆盖了另一个时钟定义。需要检查两个约束文件:Timing.xdc 和 GT_2Ch.xdc 中的时钟设置。 根据引用[3]:时钟约束必须最早创建。对于7系列FPGA,端口进来的时钟以及GT的输出RXCLK/TXCLK都必须由用户使用create_clock自主创建为主时钟。如果是差分输入的时钟,可以仅在差分对的P侧用get_ports获取端口并创建时钟。 引用[2]提供了设置时钟不确定性的命令语法,但当前问题更关注时钟定义冲突。 问题分析: - 错误信息表明同一个时钟被定义了两次,或者两个时钟的定义在同一个物理节点上,导致覆盖。 - 需要检查两个约束文件(Timing.xdc和GT_2Ch.xdc)中关于时钟'XSGMIIGT0TXCLK'的定义。 解决步骤: 1. 定位时钟定义:在Timing.xdc和GT_2Ch.xdc中查找所有关于'XSGMIIGT0TXCLK'的create_clock命令。 2. 检查时钟源:确认这两个文件中是否都对同一个时钟源(例如,同一个端口或同一个GT输出)进行了时钟定义。 3. 解决冲突:如果两个文件都定义了同一个时钟,那么应该保留其中一个,删除另一个。通常,我们建议将时钟定义放在一个统一的约束文件中,避免重复。 4. 如果两个时钟定义实际上指向不同的时钟,但物理节点相同(比如同一个GT输出被两个不同的时钟名约束),则需要重新考虑设计,因为一个物理节点只能有一个主时钟定义。 具体操作: - 打开Timing.xdc和GT_2Ch.xdc,搜索关键词“XSGMIIGT0TXCLK”。 - 查看定义该时钟的create_clock命令,例如: create_clock -name XSGMIIGT0TXCLK -period 6.4 [get_ports ...] 或者 create_clock -name XSGMIIGT0TXCLK [get_pins ...] - 注意:根据引用[3],GT的输出时钟(如TXCLK)必须由用户创建为主时钟,但只能创建一次。 可能的冲突场景: - 场景1:同一个时钟在Timing.xdc中定义了一次,在GT_2Ch.xdc中又定义了一次。 解决方法:删除其中一个定义,保留一个即可。通常,与GT相关的约束可以放在GT的约束文件中,而通用的时序约束放在Timing.xdc中,但同一个时钟不能重复定义。 - 场景2:两个不同的时钟定义指向了同一个物理节点。例如,一个文件定义了XSGMIIGT0TXCLK,另一个文件定义了另一个时钟名,但都指向同一个GT输出引脚。 解决方法:需要统一时钟名,或者使用同一个时钟定义。如果设计需要两个不同的时钟名(虽然不常见),则可以使用create_generated_clock来创建生成时钟,而不是主时钟。 - 场景3:一个时钟定义覆盖了另一个时钟定义,因为两个时钟定义在同一个物理节点上,且后加载的约束文件覆盖了先前的定义。 解决方法:调整约束文件的加载顺序,或者删除重复的定义。 建议: 1. 首先,确认两个约束文件中关于'XSGMIIGT0TXCLK'的定义,并确定哪一个是正确的。 2. 如果两个定义都是必要的(例如,一个定义在GT输出引脚,另一个在端口),请确保它们不是同一个物理节点。如果同一个时钟信号被路由到两个不同的节点,那么可能需要使用时钟网络(BUFG)来驱动,然后分别约束。 3. 如果确实需要两个时钟定义(比如同一个源时钟经过不同路径),那么应该使用create_generated_clock来创建生成时钟,而不是使用create_clock。 根据错误信息,很可能是重复定义,所以删除其中一个定义即可。 例如,在GT_2Ch.xdc中,可能已经定义了GT输出时钟,而Timing.xdc中又重复定义了一次。那么,应该保留GT_2Ch.xdc中的定义,并删除Timing.xdc中的定义。 注意:在修改约束文件后,重新运行综合和实现,检查是否还有冲突。 如果问题仍然存在,可能需要检查是否还有其他约束文件定义了同一个时钟。 最后,引用[1]提到时钟约束是XDC的基础,熟练掌握时钟约束很重要。因此,正确理解时钟约束的创建是解决此类问题的关键。</think>### 解决时钟约束冲突问题 在FPGA设计中,时钟约束冲突通常由**重复定义时钟**或**时钟覆盖范围重叠**引起。针对您遇到的错误 `Clock 'XSGMIIGT0TXCLK' 完全覆盖了另一个时钟定义`,以下是解决步骤和原理分析: --- ### 一、问题原因分析 1. **重复定义主时钟** 根据引用[3],每个GT输出时钟(如 `XSGMIIGT0TXCLK`)只能通过 `create_clock` **定义一次**。若在 `Timing.xdc` 和 `GT_2Ch.xdc` 中同时定义该时钟,会导致冲突: ```tcl # 错误示例:两个文件均定义相同时钟 create_clock -name XSGMIIGT0TXCLK -period 6.4 [get_pins gt0/TXOUTCLK] # 在GT_2Ch.xdc create_clock -name XSGMIIGT0TXCLK -period 6.4 [get_ports tx_clk] # 在Timing.xdc ``` 2. **时钟覆盖范围重叠** 若两个时钟定义指向**同一物理节点**(如GT输出引脚),后加载的约束会覆盖先前的定义,触发冲突警告[^3]。 --- ### 二、解决步骤 #### 步骤1:检查时钟定义位置 - 在 `GT_2Ch.xdc` 中确认是否有如下定义: ```tcl create_clock -name XSGMIIGT0TXCLK [get_pins gt0/TXOUTCLK] -period <value> ``` - 在 `Timing.xdc` 中搜索 `XSGMIIGT0TXCLK`,检查是否重复定义。 #### 步骤2:统一时钟定义 - **方案A(推荐)**:保留 `GT_2Ch.xdc` 中的定义,删除 `Timing.xdc` 中的重复定义。 GT约束文件通常包含专用时钟定义,应优先保留[^3]。 - **方案B**:若需在 `Timing.xdc` 中定义,则删除 `GT_2Ch.xdc` 中的定义,并确保路径指向正确: ```tcl create_clock -name XSGMIIGT0TXCLK [get_pins gt0/TXOUTCLK] -period 6.4 ``` #### 步骤3:处理跨时钟域约束 若涉及跨时钟域(如 `XSGMIIGT0TXCLK` 到其他时钟),按引用[2]添加不确定性约束: ```tcl set_clock_uncertainty -from XSGMIIGT0TXCLK -to OtherCLK -setup 0.250 ``` #### 步骤4:验证约束 在Vivado中执行: ```tcl report_clock_networks -name my_clock check_timing -override_defaults ``` 检查是否仍存在覆盖冲突。 --- ### 三、关键原则 1. **主时钟唯一性** 每个物理时钟源(如GT输出、外部端口)只能定义一个 `create_clock`[^3]。 2. **约束优先级** Vivado按文件加载顺序处理约束,后加载的约束会覆盖先前冲突的定义。 3. **差分时钟处理** 若为差分时钟,只需约束P侧: ```tcl create_clock -name sys_clk [get_ports sys_clk_p] -period 8.0 ``` --- ### 四、典型修复示例 #### 修复前(冲突) `GT_2Ch.xdc`: ```tcl create_clock -name XSGMIIGT0TXCLK [get_pins gt0/TXOUTCLK] -period 6.4 ``` `Timing.xdc`: ```tcl create_clock -name XSGMIIGT0TXCLK [get_ports tx_clk] -period 6.4 # 冲突! ``` #### 修复后(统一) `GT_2Ch.xdc`: ```tcl create_clock -name XSGMIIGT0TXCLK [get_pins gt0/TXOUTCLK] -period 6.4 ``` `Timing.xdc`: ```tcl # 删除重复定义,改为时钟不确定性约束 set_clock_uncertainty -from XSGMIIGT0TXCLK -to ETH_CLK -setup 0.200 ``` > **提示**:修改后需重新运行综合与实现,通过 `report_clock_interaction` 验证冲突是否解决。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值