Meaning of Pid, Ppid and Tgid

Meaning of PID, PPID and TGID

  • PID: Process Id
  • PPID: Parent Process Id (the one which launched this PID)
  • TGID: Thread Group Id

see this question for more details

What is the difference between a Process' pid, ppid, uid, euid, gid and egid?

In order:

  • pid: The is the process ID (PID) of the process you call the Process.pid method in.
  • ppid: The PID of the parent process (the process that spawned the current one). For example, if you run ruby test.rb in a bash shell, PPID in that process would be the PID of Bash.
  • uid: The UNIX ID of the user the process is running under.
  • euid: The effective user ID that the process is running under. The EUID determines what a program is allowed to do, based on what the user with this UID is allowed to do. Typically the same as uid, but can be different with commands like sudo.
  • gid: The UNIX group ID the program is running under.
  • egid: Like euid, but for groups.

How to read the thread group id of a running process in linux

The "go to" file will be /proc/PID/status. Most information you ever wanted to know about a Linux process is usually there. A random example (an actual thread group id is either Tgid or NStgid, which are usually the same, lacking container environment):

# cat /proc/8646/status

Name:   udevd
Umask: 0022
State: S (sleeping)
Tgid: 8646
Ngid: 0
Pid: 8646
PPid: 1584
TracerPid: 0
Uid: 0 0 0 0
Gid: 0 0 0 0
FDSize: 64
Groups:
NStgid: 8646
NSpid: 8646
NSpgid: 1584
NSsid: 1584
// skipped

If I have a process, and I clone it, is the PID the same?

Not quite. If you clone a process via fork/exec, or vfork/exec, you will get a new process id. fork() will give you the new process with a new process id, and exec() replaces that process with a new process, but maintaining the process id.

From here:

The vfork() function differs from
fork() only in that the child process
can share code and data with the
calling process (parent process). This
speeds cloning activity significantly
at a risk to the integrity of the
parent process if vfork() is misused.

Relation between Thread ID and Process ID

I got the answer here on stackoverflow. It states that if we run a program on Linux that contains the libc libuClibc-0.9.30.1.so (1). Basically an older version of libc then thread created will have different PID as shown below

root@OpenWrt:~# ./test
main thread pid is 1151
child thread pid is 1153

and I tried to run this program with a linux that contains the libc from ubuntu libc6 (2) i.e newer version of libc then Thread created will have the same PID as the process.

$ ./test
main thread pid is 2609
child thread pid is 2609
The libc (1) use linuxthreads implementation of pthread

And the libc (2) use NPTL ("Native posix thread library") implementation of pthread

According to the linuxthreads FAQ (in J.3 answer):

each thread is really a distinct process with a distinct PID, and signals sent to the PID of a thread can only be handled by that thread

So in the old libc which use linuxthreads implementation, each thread has its distinct PID

In the new libc version which use NPTL implementation, all threads has the same PID of the main process.

The NPTL was developed by redhat team. and according to the redhat NPTL document: One of the problems which are solved in the NPTL implementation is:

(Chapter: Problems with the Existing Implementation, page5)

Each thread having a different process ID causes compatibility problems with other POSIX thread implementations. This is in part a moot point since signals can't be used very well but is still noticeable

And that explain this issue.

I am using the new libc version that contains the NPTL ("Native posix thread library") implementation of pthread.

How can I determine which namespaces a PID is in from kernel space?

You can use the (Linux 4.8+ only) bpf_get_current_task helper to retrieve the struct task_struct of the current process. Then the PID as seen by processes inside the container is in t->nsproxy->pid_ns_for_children->last_pid.

The following shows this in action when tracing execve syscalls (you can use top inside the container to check that the upid is correct):

from bcc import BPF
BPF(text="""
#include <linux/pid_namespace.h>
int kprobe__sys_execve(void *ctx) {
u32 pid = bpf_get_current_pid_tgid();
struct task_struct *t = (struct task_struct *)bpf_get_current_task();
u32 upid = t->nsproxy->pid_ns_for_children->last_pid;
bpf_trace_printk("pid=%d; upid=%d!\\n", pid, upid);
return 0;
}
""").trace_print()

The following diff (based of a44d26ed3) extends bcc's execsnoop.py to retrieve the upid:

diff --git a/tools/execsnoop.py b/tools/execsnoop.py
index 5711fd1..2134f69 100755
--- a/tools/execsnoop.py
+++ b/tools/execsnoop.py
@@ -53,6 +53,7 @@ bpf_text = """
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
#include <linux/fs.h>
+#include <linux/pid_namespace.h>

#define ARGSIZE 128

@@ -63,6 +64,7 @@ enum event_type {

struct data_t {
u32 pid; // PID as in the userspace term (i.e. task->tgid in kernel)
+ u32 upid;
char comm[TASK_COMM_LEN];
enum event_type type;
char argv[ARGSIZE];
@@ -119,6 +121,8 @@ int kretprobe__sys_execve(struct pt_regs *ctx)
{
struct data_t data = {};
data.pid = bpf_get_current_pid_tgid() >> 32;
+ struct task_struct *t = (struct task_struct *)bpf_get_current_task();
+ data.upid = t->nsproxy->pid_ns_for_children->last_pid;
bpf_get_current_comm(&data.comm, sizeof(data.comm));
data.type = EVENT_RET;
data.retval = PT_REGS_RC(ctx);
@@ -134,7 +138,7 @@ b = BPF(text=bpf_text.replace("MAXARG", args.max_args))
# header
if args.timestamp:
print("%-8s" % ("TIME(s)"), end="")
-print("%-16s %-6s %-6s %3s %s" % ("PCOMM", "PID", "PPID", "RET", "ARGS"))
+print("%-16s %-6s %-6s %-6s %3s %s" % ("PCOMM", "PID", "UPID", "PPID", "RET", "ARGS"))

TASK_COMM_LEN = 16 # linux/sched.h
ARGSIZE = 128 # should match #define in C above
@@ -142,6 +146,7 @@ ARGSIZE = 128 # should match #define in C above
class Data(ct.Structure):
_fields_ = [
("pid", ct.c_uint),
+ ("upid", ct.c_uint),
("comm", ct.c_char * TASK_COMM_LEN),
("type", ct.c_int),
("argv", ct.c_char * ARGSIZE),
@@ -189,8 +194,8 @@ def print_event(cpu, data, size):
if args.timestamp:
print("%-8.3f" % (time.time() - start_ts), end="")
ppid = get_ppid(event.pid)
- print("%-16s %-6s %-6s %3s %s" % (event.comm.decode(), event.pid,
- ppid if ppid > 0 else "?", event.retval,
+ print("%-16s %-6s %-6s %-6s %3s %s" % (event.comm.decode(), event.pid,
+ event.upid, ppid if ppid > 0 else "?", event.retval,
b' '.join(argv[event.pid]).decode()))
try:
del(argv[event.pid])


Related Topics



Leave a reply



Submit