如何防止 sem_open() 函数因为 ENOSYS 错误而失败?

4

我有两个Slackware Linux系统,其中POSIX信号量sem_open()调用失败,errno设置为38。以下是重现问题的示例代码(该代码在CentOS / RedHat上可以正常工作)。

是否有任何内核或系统配置选项会导致这种情况?其他建议?

有问题的系统是Slackware 10.1.0内核2.6.11 /lib/librt-2.3.4.so /lib/libpthread-0.10.so,但同样的代码可以在旧得多的RedHat 9内核2.4.20 /lib/librt-2.3.2.so /lib/tls/libpthread-0.29.so(也可以在CentOS 5内核2.6.18 /lib/librt-2.5.so /lib/i686/nosegneg/libpthread-2.5.so)上运行。

man sem_open建议这个errno表示系统不支持sem_open()

#define ENOSYS          38      /* Function not implemented */
sem_open()函数的用户空间在librt中,我们与之动态链接,在受影响的系统上存在librt库。
受影响的系统声称支持POSIX信号量: _POSIX_SEMAPHORES为真,并且sysconf(_SC_SEMAPHORES)也确认了这一点。
谢谢, Kieran
编辑1:我已经添加了更多有关使用的软件版本的详细信息并删除了一些不相关的注释。
编辑2:在正常运行的系统上,/dev/shm已挂载,在受影响的系统上未挂载。将其挂载到受影响的系统上并没有改变行为。我认为/dev/shm也是必需的,但在sem_open()失败之前,strace就不支持了。
# /* Quick'n'dirty test program to illustrate sem_open failure
#Run this file to auto-build test and run as a.out

# Build
gcc $0 -lrt
if [ $? -ne 0 ] ; then exit ; fi

# Run
$( dirname $0)/a.out
exit
*/

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <semaphore.h>


int main(int argc, char *argv[]) {

 const char *SEM_NAME = "SHRMEM_SCXL";  /* name of mutex */
 sem_t *mutex = SEM_FAILED;             /* ptr to mutex */

#ifdef _POSIX_SEMAPHORES
  printf("_POSIX_SEMAPHORES %ld\n", _POSIX_SEMAPHORES);
#else
  puts("Undefined");
#endif

 printf("sysconf %s\n", sysconf(_SC_SEMAPHORES) ? "Yes" : "No" );

 mutex = sem_open(SEM_NAME, O_CREAT, 0666, 1);

 if (mutex == SEM_FAILED) printf("Failed %d\n", errno);
 else {
        puts("Success - pause while you check /dev/shm ");
        sleep(5);
        sem_close(mutex);
        sem_unlink(SEM_NAME);
 }
}
5个回答

6

/dev/shm是否已挂载?旧版的Slackware可能没有在启动时挂载此文件系统。请查看/etc/fstab文件:

tmpfs  /dev/shm  tmpfs  defaults  0   0

编辑:那可能不是问题所在。我认为您可能只需要升级您的内核,或者甚至是librt。

编辑2:我认为对于您正在使用的slackware 11,您需要一个比2.6.13更新的内核才能使用NPTL线程库(/lib/tls中的库),这似乎是sem_open工作所必需的。

编辑3:我通过挂载/dev/shm并将环境变量LD_ASSUME_KERNEL设置为2.6.13(任何大于2.6.12的内核版本都可以)来使它与我拥有的slackware 11盒子一起工作。尽管内核是2.6.11.11,但这似乎有效,但其他像线程之类的东西可能不行。


谢谢你的帮助 - 这可能会在Slackware 11上修复它,但事实证明我们使用的是Slackware 10。在运行中的系统上升级库/内核太冒险了,所以我正在寻找代码解决方法。 - Kieran Tully

2

旧版本的线程库不支持在进程间共享POSIX信号量。来自man sem_init

pshared参数指示信号量是本地于当前进程(pshared为零),还是要在多个进程之间共享(pshared不为零)。LinuxThreads目前不支持进程间共享信号量,因此如果pshared不为零,则sem_init始终返回错误ENOSYS。

由于sem_open()创建命名信号量,因此它总是尝试在进程之间共享它们。

为了支持在Slackware 10上使用sem_init()共享匿名信号量

  • 升级libpthread和(可能)librt
  • 升级内核

此外,为了支持使用sem_open()共享命名信号量

  • /etc/fstab中添加一行以将/dev/shm挂载为tmpfs

    tmpfs /dev/shm tmpfs defaults 0 0

  • 运行mount /dev/shm或重新启动


1

“进程共享信号量不起作用”的假设对我来说有些道理。虽然这并不能帮助你,但如果你有时间和意愿,可以尝试以下方法,看看是否是“进程共享”方面出了问题:

  1. 在未共享内存中(针对线程)使用sem_init创建一个信号量。如果它能正常工作,则表示信号量在进程内部工作正常。

  2. 在共享内存中重复实验。这应该可以告诉你它们是否在进程之间工作。请注意,您可能需要实际尝试使用信号量,以确定它们是否在进程之间工作。


谢谢。在我写下我的“答案”之前,我验证了sem_init(pshared=0)可以在受影响的系统上创建未共享的信号量。我没有测试未共享的信号量是否可以可靠地在进程间共享。手册中说它们不适合共享,所以我不想依赖它们。 - Kieran Tully

0

我正在使用POSIX消息队列进行工作,但是遇到了相同的错误,mq_open失败并显示错误码38(ENOSYS)。

解决方法是在内核配置中启用POSIX MESSAGE QUEUE并重新构建内核。

这将为内核构建POSIX消息队列支持,对我很有效。


0

SYSV IPC已经过时,不应在新的应用程序中使用。但是当别无选择时... POSIX IPC在Linux中使用futex进行了优化。 - Rachid K.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接