为何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版本,现咨询:
- 该差异是配置变更还是GCC本身改动?
- 如何调整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
相关产品推荐
相关产品推荐

