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

为何Ubuntu 20.04编译含<semaphore.h>的C文件需加-lpthread,22.04无需?

问题描述

我是C开发新手,在CI构建中遇到如下问题:

  • 主机Ubuntu 22.04系统使用gcc semaphores.c即可正常编译含信号量的C代码;
  • 但在基于Ubuntu 20.04的Docker容器中,必须添加-lpthread(或-pthread)参数编译,否则会出现sem_open、sem_unlink等函数的未定义引用链接错误。

已确认差异源于Ubuntu版本,现咨询:

  1. 该差异是配置变更还是GCC本身改动?
  2. 如何调整20.04的配置,使其无需显式指定库即可编译?

附上测试代码semaphores.c:

#include <stdio.h>          /* printf()                 */
#include <stdlib.h>         /* exit(), malloc(), free() */
#include <unistd.h>
#include <sys/types.h>      /* key_t, sem_t, pid_t      */
#include <sys/wait.h>
#include <sys/shm.h>        /* shmat(), IPC_RMID        */
#include <errno.h>          /* errno, ECHILD            */
#include <semaphore.h>      /* sem_open(), sem_destroy(), sem_wait().. */
#include <fcntl.h>          /* O_CREAT, O_EXEC          */

// Sample from https://gist.github.com/junfenglx/7412986
// In Ubuntu container, compile with: gcc semaphores.c -pthread

int main (int argc, char **argv){
    int i;                        /*      loop variables          */
    key_t shmkey;                 /*      shared memory key       */
    int shmid;                    /*      shared memory id        */
    sem_t *sem;                   /*      synch semaphore         *//*shared */
    pid_t pid;                    /*      fork pid                */
    int *p;                       /*      shared variable         *//*shared */
    unsigned int n;               /*      fork count              */
    unsigned int value;           /*      semaphore value         */

    /* initialize a shared variable in shared memory */
    shmkey = ftok ("/dev/null", 5);       /* valid directory name and a number */
    printf ("shmkey for p = %d\n", shmkey);
    shmid = shmget (shmkey, sizeof (int), 0644 | IPC_CREAT);
    if (shmid < 0){                           /* shared memory error check */
        perror ("shmget\n");
        exit (1);
    }

    p = (int *) shmat (shmid, NULL, 0);   /* attach p to shared memory */
    *p = 0;
    printf ("p=%d is allocated in shared memory.\n\n", *p);

    /********************************************************/

    printf ("How many children do you want to fork?\n");
    printf ("Fork count: ");
    scanf ("%u", &n);

    printf ("What do you want the semaphore value to be?\n");
    printf ("Semaphore value: ");
    scanf ("%u", &value);

    /* initialize semaphores for shared processes */
    sem = sem_open ("pSem", O_CREAT | O_EXCL, 0644, value);
    /* name of semaphore is "pSem", semaphore is reached using this name */
    sem_unlink ("pSem");
    /* unlink prevents the semaphore existing forever */
    /* if a crash occurs during the execution         */
    printf ("semaphores initialized.\n\n");


    /* fork child processes */
    for (i = 0; i < n; i++){
        pid = fork ();
        if (pid < 0)              /* check for error      */
            printf ("Fork error.\n");
        else if (pid == 0)
            break;                  /* child processes */
    }


    /******************************************************/
    /******************   PARENT PROCESS   ****************/
    /******************************************************/
    if (pid != 0){
        /* wait for all children to exit */
        while (pid = waitpid (-1, NULL, 0)){
            if (errno == ECHILD)
                break;
        }

        printf ("\nParent: All children have exited.\n");

        /* shared memory detach */
        shmdt (p);
        shmctl (shmid, IPC_RMID, 0);

        /* cleanup semaphores */
        printf("sem_destroy return value:%d\n",sem_destroy (sem));
        exit (0);
    }

    /******************************************************/
    /******************   CHILD PROCESS   *****************/
    /******************************************************/
    else{
        sem_wait (sem);           /* P operation */
        printf ("  Child(%d) is in critical section.\n", i);
        sleep (1);
        *p += i % 3;              /* increment *p by 0, 1 or 2 based on i */
        printf ("  Child(%d) new value of *p=%d.\n", i, *p);
        sem_post (sem);           /* V operation */
        exit (0);
    }
}
/*src:http://stackoverflow.com/a/16400833*/
解答

1. 差异原因:系统与GCC的联动配置变更

这个差异本质是Ubuntu系统对GCC默认链接行为的配置调整,而非GCC核心功能的改动。

在Ubuntu 20.04中,sem_open、sem_unlink等POSIX信号量函数属于libpthread.so库的一部分,系统默认不会自动链接该库,因此必须显式添加-lpthread或-pthread参数(推荐用-pthread,它不仅链接库还会启用线程相关编译宏)。

到了Ubuntu 22.04,系统调整了semaphore.h头文件的编译指令,或者修改了GCC的默认配置,让编译器在包含该头文件时自动链接pthread库,因此无需手动指定参数。

2. 调整Ubuntu 20.04配置的方法

以下几种方案可实现无需显式指定-lpthread编译:

方案一:设置全局编译环境变量

在用户目录的.bashrc或.profile文件中添加:

export CFLAGS="-pthread $CFLAGS"
export LDFLAGS="-lpthread $LDFLAGS"

执行source ~/.bashrc生效后,后续gcc编译会自动带上这些参数。CI环境可在构建脚本开头加入这两行命令。

方案二:使用Makefile固化编译参数

在项目目录创建Makefile:

CC = gcc
CFLAGS = -pthread
TARGET = semaphores

all: $(TARGET)

$(TARGET): semaphores.c
	$(CC) $(CFLAGS) -o $@ $^

clean:
	rm -f $(TARGET)

之后只需执行make即可完成编译,无需手动输入参数。

方案三:修改系统级GCC配置(不推荐)

编辑/etc/gcc/<gcc-version>/specs文件(需root权限),在链接器选项中添加-lpthread。但此方法会影响系统所有用户的编译行为,仅适合定制专属基础镜像的场景。

注意:从代码可移植性角度,建议在编译脚本或Makefile中显式添加-pthread参数,避免依赖特定系统的默认配置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 20:35:27