You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

访问串口时收发线程疑似阻塞的代码逻辑排查咨询

UART多线程读写程序逻辑问题排查

问题场景

我用C语言写了一套UART读写程序,测试时出现异常:

  1. 初始阶段通过第三方UART工具发送test1,触发recv线程读取到result1,程序正常执行到printf("2->3");
  2. 再次用工具发送result2,recv线程完全没进入case 3:分支,而且第三方UART工具在主线程退出前无法再发送任何测试字符串。

测试示例代码

typedef struct devpara {
  int fd;
  int num;  // 这里缺分号,编译报错
} devpara_t;

int g_step = 1; // 初始步骤
// 缺少全局锁和条件变量的定义与初始化
pthread_mutex_t g_mutex;
pthread_cond_t g_cond;

int main()
{
    int ret = 0;
    devpara_t *dpara = malloc(sizeof(devpara_t));
    pthread_t rtid, wtid;  // 缺失的线程ID变量,之前未定义
    
    // 结构体成员名写错:结构体里是fd,这里用了hd
    dpara->hd = openuart("/ttyS0");  // 参数为O_RDWR | O_NOCTTY
    if (dpara->hd < 0)
        return -1;
    configuart(dpara->hd, 115200, 8, 1, NOPAR, 1 /* 停止位 */);
        
    // 第一个线程的ID存在wtid里,后面被第二个线程覆盖
    ret = pthread_create(&wtid, NULL, recvthread, (void *)dpara);
    if (ret < 0) {
        perror("pthread_create failed\n");
        return -1;
    }

    // 线程函数名写错:要创建的是sendthreadfunc,这里写了sendthread
    ret = pthread_create(&wtid, NULL, sendthread, (void *)dpara);
    if (ret < 0) {
        perror("pthread_create failed\n");
        return -1;
    }
    // rtid未定义,编译报错,主线程无法正确等待recv线程退出
    pthread_join(rtid, NULL);
    pthread_join(wtid, NULL);
    if(dpara->hd >= 0)
        closeuart(dpara->hd);
    if(NULL != dpara)
        free(dpara);
    return ret;
}

void *recvthread(void *para)
{
    unsigned char buf[128] = {0};
    int ret = -1;
    devpara_t *localpara = (devpara_t *)para;

    for (;;) {
        ret = read(localpara->fd, buf, 128);
        // 没判断read是否成功,要是read返回-1(读失败)还继续处理buf
        if (ret <= 0) continue;
        
        pthread_mutex_lock(&g_mutex);
        // 变量名写错:buf写成buff,导致字符串永远匹配失败
        switch(g_step) {
        case 1:
            if (!strncmp(buff, "result1", strlen("result1")))
              pthread_cond_signal(&g_cond);
            break;
        case 2:
            if (!strncmp(buff, "result2", strlen("result2")))
              pthread_cond_signal(&g_cond);
            break;
        case 3:
            if (!strncmp(buff, "result3", strlen("result3")))
              pthread_cond_signal(&g_cond);
            break;  
        default:
            break;
        }
        pthread_mutex_unlock(&g_mutex);
    }
}

void *sendthreadfunc(void *para)
{
    FILE *fp;
    devpara_t *localpara = (devpara_t *)para;
    
    for (;;) {
        pthread_mutex_lock(&g_mutex);
        pthread_cond_wait(&g_cond, &g_mutex);
        switch(g_step) {
            case 1:
                printf("1->2");
                senddata(localpara->fd, "test2");
                g_step = 2;
                break;
            case 2:
                printf("2->3");
                // fopen参数写错:第二个参数应该是字符串"r",不是裸r
                fp = fopen("test.config", r);
                if (NULL == fp)
                  return NULL;  // 直接退出send线程,后续case3没人处理
                // 没关闭文件,资源泄漏
                fclose(fp);
                g_step = 3;
                break;
            case 3:
                // 缺case3的处理逻辑,收到result3后没响应
                printf("3->end");
                break;
        }
        pthread_mutex_unlock(&g_mutex);
}

UART配置代码

termios_cfg.c_cflag |= CLOCAL;
    termios_cfg.c_cflag |= CREAD;
    termios_cfg.c_oflag &= ~OPOST;
    termios_cfg.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    termios_cfg.c_cc[VTIME] = 1;
    termios_cfg.c_cc[VMIN]  = 1;
    // 刷新缓冲区
    tcflush(fd, TCIFLUSH);
    ret = tcsetattr(fd, TCSANOW, &termios_cfg);
    if (0 != ret) {
        printf("tcsetattr failed!\n");
        ret = -1;
    }

问题根源分析

  • 变量/函数名拼写错误:
    • recvthread里把接收缓冲区buf写成buff,strncmp永远匹配失败,这是result2无法触发case3的核心原因。
    • 结构体成员fd被写成hd,导致UART文件描述符未正确赋值,读写操作实际无效。
    • 线程创建时指定的函数名sendthread与实际定义的sendthreadfunc不匹配,send线程根本没创建成功。
  • 线程管理错误:
    • 两个线程共用同一个wtid变量,第一个线程的ID被覆盖,pthread_join(rtid)里rtid未定义,主线程无法正确等待线程退出,可能提前终止导致UART资源未释放。
  • 同步机制未初始化:
    • 全局锁g_mutex和条件变量g_cond没初始化,调用同步接口会触发未定义行为,线程间的信号传递完全失效。
  • 资源与逻辑漏洞:
    • sendthreadfunc中打开文件后未关闭,造成资源泄漏;打开失败直接返回导致send线程提前退出,g_step=3后没有线程处理后续逻辑。
    • recvthread未判断read返回值,读取失败时仍处理无效缓冲区,可能引发程序异常。
  • UART字节流处理问题:
    • 当前UART配置是VMIN=1+VTIME=1,读到1个字符或等待100ms就返回,第三方工具发送的字符串可能被拆分成多次读取,strncmp因不完整字符串匹配失败。建议添加帧头帧尾或长度字段来确保读取到完整指令。

内容的提问来源于stack exchange,提问作者Rui

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 02:07:05