【问题标题】:is popen really thread safe?popen 真的是线程安全的吗?
【发布时间】:2019-01-21 14:12:00
【问题描述】:

最近调用popen遇到了麻烦,好像不是线程安全的。

以下是源代码链接中的代码sn-p:http://androidxref.com/9.0.0_r3/xref/bionic/libc/upstream-netbsd/lib/libc/gen/popen.c

static struct pid {
   struct pid *next;
   FILE *fp;
   int fd;
   pid_t pid;
} *pidlist;

static rwlock_t pidlist_lock = RWLOCK_INITIALIZER;

FILE *
popen(const char *command, const char *type)
{
   struct pid *cur, *old;
   ...
   pipe2(pdes, flags)                 // A
   ...
   (void)rwlock_rdlock(&pidlist_lock);     // C
   ...
   switch (pid = vfork()) {                // C.1
     case 0:             /* Child. */
        ...
        _exit(127);
        /* NOTREACHED */
    }

    /* Parent; */
   ...
   /* Link into list of file descriptors. */
   cur->fp = iop;
   cur->pid =  pid;
   cur->next = pidlist;          // D
   pidlist = cur;                // E

   (void)rwlock_unlock(&pidlist_lock); // F
   ...
}

观察上面的代码,它在 C 处获得了一个读锁,但在作用域内,它在 E 处做了一些写操作。因此,可能会有多个读线程正在写入变量“pidlist”同一时间。

有人知道这是否是一个真正的问题吗?

【问题讨论】:

  • vfork() 之后的下一行是什么?如果它不是对exec...() 函数之一的调用,那将是一件坏事。一般来说,在创建一个或多个新线程之后分叉一个 Linux 进程也是一件坏事,但我不知道当你谈论 vfork()/exec() 时该规则如何适用。
  • 一目了然,我同意。我看不出有什么会阻止两个不同的struct pid 节点被初始化并竞相设置新的pidlist 值。这将导致两个节点之一被排除在列表之外。在我看来它应该是一个写锁。

标签: multithreading concurrency thread-safety popen


【解决方案1】:

看代码,不是你的大纲。

这是来自给定链接http://androidxref.com/9.0.0_r3/xref/bionic/libc/upstream-netbsd/lib/libc/gen/popen.c的粘贴

80FILE *
81popen(const char *command, const char *type)
82{
83  struct pid *cur, *old;
94  if (strchr(xtype, '+')) {
...
100 } else  {
103     if (pipe2(pdes, flags) == -1)
104         return NULL;
105 }
106
...
113
114 (void)rwlock_rdlock(&pidlist_lock);
115 (void)__readlockenv();
116 switch (pid = vfork()) {
117 case -1:            /* Error. */
...
127 case 0:             /* Child. */
...
154     execl(_PATH_BSHELL, "sh", "-c", command, NULL);
155     _exit(127);
156     /* NOTREACHED */
157 }
158 (void)__unlockenv();
159
160 /* Parent; assume fdopen can't fail. */
161 if (*xtype == 'r') {
...
167 } else {
...
173 }
...
179 pidlist = cur;
180 (void)rwlock_unlock(&pidlist_lock);
181
182 return (iop);
183}

do the file stuff (pipe2() or socketpair())
acquire the lock (this will avoid the child doing silly things)
vfork()
on the child, reorganize filedes and execl
on parent do some filedes stuff unlock and return

我看不出会有什么问题。

【讨论】:

  • 我认为你没有抓住重点。问题仅存在于父级中——与pidlist 在没有写锁的情况下被操纵有关(同时在不同的线程中)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多