为什么$$返回的是与父进程相同的id?

191

我有Bash的问题,但不知道原因。
在shell下,我输入了:

echo $$    ## print 2433
(echo $$)  ## also print 2433
(./getpid) ## print 2602

这里的 getpid 是一个 C 程序,用于获取当前进程的 pid,例如:

   int main() {
    printf("%d", (int)getpid());
    return 0;
   }
使我困惑的是:
  1. 我认为“(命令)”是一个子进程(我对吗?),我认为它的pid应该与它的父pid不同,但它们是相同的,为什么...
  2. 当我使用我的程序在括号中显示pid时,它显示的pid不同,这是正确的吗?
  3. $$ 是类似于宏的东西吗?
你能帮帮我吗?

12
请注意,即使不在子 shell 中运行,getpid 也会显示不同的进程 ID。 - chepner
3
@Marian echo $$ $BASHPID ; ( echo $$ $BASHPID ) 表明它是这样的。圆括号创建一个子shell。语句可能会更改变量的值,父shell不应该看到这些变化。这是通过 fork() 操作实现的。 - Ben
8个回答

256

$$ 用于在子shell中返回父进程的进程ID。这是从“特殊参数”章节中的man手册中得到的。

$可扩展为shell的进程ID。在()子shell中,它扩展为当前shell的进程ID,而不是子shell的进程ID。

在bash 4中,您可以使用BASHPID获取子进程的进程ID。

~ $ echo $$
17601
~ $ ( echo $$; echo $BASHPID )
17601
17634

29
“parent” 这个词有点误导人,至少对我是这样的,实际上它指的是“最高层”的 shell。例如:echo $$; (echo $$; (echo $$)) 会输出相同的进程 ID 三次。 - Martin Bouladour
1
对,我应该说这个值是从父 shell 继承而来的(它又从 它的 父进程继承而来,以此类推)。顶层 shell 最初设置它,而不是从其(非 shell)父进程继承。 - chepner
“$”扩展为shell的进程ID,但是“echo $”只会输出字面上的美元符号“$”。 - Alexander Mills
@AlexanderMills 嗯,是的;$本身不是参数扩展。手册指的是特殊参数的名称,即$;它并没有声称$单独扩展。 - chepner
1
好的,老实说我不知道那是什么意思,但是在bash 4和5中echo $BASHPID可以使用(但在MacOS上的版本3.2.57不行)。 - Alexander Mills
2
所有参数扩展都以 $ 开头,但 $ 也是其中一个特殊参数的名称。$#@* 等都是一些特殊参数;$$$#$@$* 等是扩展到每个值的表达式。 - chepner

85

您可以使用以下其中之一。

  • $! 是最后一个后台进程的PID。
  • kill -0 $PID 检查它是否仍在运行。
  • $$ 是当前shell的PID。

3
如果我们在谈论后台进程,第二个子弹点不应该是 kill -0 $! 吗?默认情况下 PID 没有设置任何值。 - Isaac Freeman

30
  1. 括号在Bash中会调用一个子shell。由于它只是一个子shell,所以它可能具有相同的PID - 这取决于实现。
  2. 您调用的C程序是一个单独的进程,它有自己独特的PID - 不管它是否在子shell中。
  3. $$是Bash中当前脚本的PID的别名。请参见此处关于$$$BASHPID之间的区别, 以及上面的附加变量$BASH_SUBSHELL,其中包含嵌套级别。

8

如果你想让你的C程序打印出你的shell进程的PID,可以尝试使用getppid()函数。


或者如果您使用Rust,只需尝试nix::unistd::getppid(),它将获取当前进程父进程的pid - https://docs.rs/nix/0.18.0/nix/unistd/fn.getppid.html - joseluisq

4

获取正确pid的通用方法如下:

pid=$(cut -d' ' -f4 < /proc/self/stat)

对于子进程,同样适用。

SUB(){
    pid=$(cut -d' ' -f4 < /proc/self/stat)
    echo "$$ != $pid"
}

echo "pid = $$"

(SUB)

检查输出

pid = 8099
8099 != 8100

2
好主意,但这样做不会让你得到 shell 运行以捕获 cut 输出的 fork 的 pid 吗?如果我运行两次,一次带有 echo $(...),一次没有,那么我会得到不同的答案。 - Martin Dorey

1
如果您想要获取已知命令的PID,代码应该类似于以下内容:
如果您已经执行了以下命令: # 执行的命令是 ***

dd if=/dev/diskx of=/dev/disky


然后您将使用:
PIDs=$(ps | grep dd | grep if | cut -b 1-5)

这里发生的情况是将所有必要的唯一字符导入一个字段中,然后可以使用以下命令回显该字段:
echo $PIDs

0
如果你想要一个简单的带有变量的shell脚本来获取最大PID,可以这样做:
pid=$(cat /proc/sys/kernel/pid_max)
echo $pid

这将打印出最大的PID。


这与这个问题毫无关联。 - Adrian Günter

0

实现这一点的便携方法

get_own_pid() {
  # This function being called in a subshell,
  # it must returns the pid of the parent of the "cut" command parent
  cut -d' ' -f4 < /proc/self/stat \
    | xargs -I% sh -c 'cut -d" " -f4 < /proc/%/stat'
}

get_parent_pid() {
  # Same thing but repeating the last command once more to get the parent one level above
  cut -d' ' -f4 < /proc/self/stat \
    | xargs -I% sh -c 'cut -d" " -f4 < /proc/%/stat' \
    | xargs -I% sh -c 'cut -d" " -f4 < /proc/%/stat'
}


# Here pid is the same as the $$ pid because called from main process
MY_PID=$(get_own_pid)
echo "$$ == ${MY_PID}"

# Here called in a subprocess, the returned pid is different
(
  MY_CHILD_PID=$(get_own_pid)
  PARENT_PID_FROM_CHILD=$(get_parent_pid)
  echo "$$ != ${MY_CHILD_PID}"
  echo "$$ == ${PARENT_PID_FROM_CHILD}"
)

受到artem lapkin answer的启发,谢谢!

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