getppid系统调用及示例

getppid - 获取父进程ID

data-ad-format="fluid" data-ad-layout-key="-7k+ex-4a-9w+4a">

getppid是Linux系统调用,用于获取当前进程的父进程ID(PPID)。该函数无需参数,总是成功返回父进程ID,在进程管理和监控中非常有用。示例代码展示了基础用法、父子进程关系、孤儿进程现象以及构建进程树结构,通过getpid()、fork()等相关函数配合使用,可完整呈现Unix/Linux的进程层级关系。当父进程终止后,子进程PPID会变为1(init进程),成为孤儿进程。

1. 函数介绍

getppid 是一个 Linux 系统调用,用于获取当前进程的父进程 ID(Parent Process ID)。每个进程(除了初始进程)都有一个父进程,父进程 ID 是进程管理、进程监控和进程间通信的重要信息。

在 Unix/Linux 系统中,进程以树状结构组织,getppid 提供了访问这种父子关系的方法,对于进程监控、调试和管理工具非常有用。

2. 函数原型

1
2
3
4
5
#include <sys/types.h>
#include <unistd.h>

pid_t getppid(void);

3. 功能

返回当前进程的父进程 ID。这是一个只读操作,不会修改任何系统状态。

4. 参数

  • 无参数

5. 返回值

  • 成功时:返回父进程 ID(pid_t 类型)

  • 不会失败:该系统调用总是成功返回

6. 相似函数,或关联函数

  • getpid(): 获取当前进程 ID

  • getpgid(): 获取进程组 ID

  • getpgrp(): 获取进程组 ID(当前进程)

  • getsid(): 获取会话 ID

  • fork(): 创建新进程

  • wait(), waitpid(): 等待子进程

  • kill(): 向进程发送信号

  • prctl(): 进程控制

  • /proc/[pid]/stat: 查看进程状态信息

7. 示例代码

示例1:基本使用 - 获取进程父子关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

int main() {
pid_t my_pid, parent_pid;

printf("=== 进程父子关系信息 ===\n");

// 获取当前进程 ID
my_pid = getpid();
printf("当前进程 ID: %d\n", my_pid);

// 获取父进程 ID
parent_pid = getppid();
printf("父进程 ID: %d\n", parent_pid);

// 获取父进程的父进程 ID(祖父进程)
// 注意:我们无法直接获取祖父进程 ID,需要通过其他方式

printf("进程关系: %d -> %d (父 -> 子)\n", parent_pid, my_pid);

// 检查特殊情况
if (parent_pid == 1) {
printf("注意: 父进程是 init 进程 (PID 1)\n");
} else if (parent_pid == 0) {
printf("注意: 父进程是调度进程 (内核进程)\n");
}

return 0;
}

示例2:父子进程关系演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

void print_process_info(const char *label) {
printf("%s:\n", label);
printf(" 进程 ID: %d\n", getpid());
printf(" 父进程 ID: %d\n", getppid());
printf(" 进程组 ID: %d\n", getpgrp());
printf("\n");
}

int main() {
pid_t child_pid;

printf("=== 父子进程关系演示 ===\n");

// 显示父进程信息
print_process_info("父进程(创建子进程前)");

// 创建子进程
child_pid = fork();

if (child_pid == -1) {
perror("fork 失败");
exit(EXIT_FAILURE);
}

if (child_pid == 0) {
// 子进程
print_process_info("子进程");

// 子进程睡眠一段时间
printf("子进程睡眠 3 秒...\n");
sleep(3);

// 再次检查父进程 ID(父进程可能已经结束)
printf("子进程睡眠结束,再次检查父进程 ID:\n");
printf(" 当前进程 ID: %d\n", getpid());
printf(" 父进程 ID: %d\n", getppid());

if (getppid() == 1) {
printf(" 父进程已结束,现在由 init 进程收养\n");
}

} else {
// 父进程
printf("父进程创建了子进程,子进程 ID: %d\n", child_pid);
print_process_info("父进程(创建子进程后)");

// 父进程立即结束
printf("父进程即将结束...\n");
exit(0);
}

return 0;
}

示例3:孤儿进程演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main() {
pid_t child_pid;

printf("=== 孤儿进程演示 ===\n");
printf("父进程 ID: %d\n", getpid());

child_pid = fork();

if (child_pid == -1) {
perror("fork 失败");
exit(EXIT_FAILURE);
}

if (child_pid == 0) {
// 子进程
printf("子进程启动,PID: %d, PPID: %d\n", getpid(), getppid());

// 子进程循环检查父进程状态
for (int i = 0; i < 10; i++) {
printf("子进程 %d: 父进程 ID = %d\n", getpid(), getppid());
sleep(2);
}

printf("子进程 %d 结束\n", getpid());

} else {
// 父进程
printf("父进程创建了子进程 %d\n", child_pid);
printf("父进程即将结束,子进程将成为孤儿进程\n");

// 父进程很快结束
sleep(1);
printf("父进程 %d 结束\n", getpid());
exit(0);
}

return 0;
}

示例4:进程树构建和分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

typedef struct {
pid_t pid;
pid_t ppid;
pid_t pgid;
int generation;
char name&#91;32];
} process_info_t;

void collect_process_info(process_info_t *info, int generation, const char *name) {
info->pid = getpid();
info->ppid = getppid();
info->pgid = getpgrp();
info->generation = generation;
snprintf(info->name, sizeof(info->name), "%s", name);
}

void print_process_tree(process_info_t *processes, int count) {
printf("\n=== 进程树结构 ===\n");
printf("%-8s %-8s %-8s %-12s %s\n", "PID", "PPID", "PGID", "代数", "名称");
printf("%-8s %-8s %-8s %-12s %s\n", "---", "----", "----", "----", "----");

for (int i = 0; i < count; i++) {
printf("%-8d %-8d %-8d %-12d %s\n",
processes&#91;i].pid,
processes&#91;i].ppid,
processes&#91;i].pgid,
processes&#91;i].generation,
processes&#91;i].name);
}
}

int main() {
pid_t pid1, pid2, pid3;
process_info_t processes&#91;4];
int process_count = 0;

printf("=== 复杂进程树演示 ===\n");

// 收集根进程信息
collect_process_info(&processes&#91;process_count++], 0, "根进程");

// 创建第一层子进程
pid1 = fork();
if (pid1 == 0) {
// 第一个子进程
collect_process_info(&processes&#91;process_count++], 1, "子进程1");

// 第一个子进程创建孙子进程
pid2 = fork();
if (pid2 == 0) {
// 孙子进程1
collect_process_info(&processes&#91;process_count++], 2, "孙子进程1");
sleep(3);
exit(0);
}

// 第一个子进程再创建另一个孙子进程
pid3 = fork();
if (pid3 == 0) {
// 孙子进程2
collect_process_info(&processes&#91;process_count++], 2, "孙子进程2");
sleep(3);
exit(0);
}

// 等待孙子进程结束
wait(NULL);
wait(NULL);
exit(0);
}

// 等待第一层子进程结束
wait(NULL);

// 在实际应用中,这里需要通过 IPC 机制收集所有进程信息
// 这里为了演示,只显示当前进程信息
printf("当前进程信息:\n");
printf(" PID: %d\n", getpid());
printf(" PPID: %d\n", getppid());
printf(" PGID: %d\n", getpgrp());

return 0;
}

示例5:进程监控和管理工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <errno.h>
#include <string.h>

// 检查父进程是否还存在
int is_parent_alive() {
pid_t ppid = getppid();

// 特殊情况:父进程是 init (PID 1) 或内核进程 (PID 0)
if (ppid == 1 || ppid == 0) {
return 1; // 认为父进程"存在"(虽然可能已结束)
}

// 尝试向父进程发送 0 信号来检查是否存在
if (kill(ppid, 0) == 0) {
return 1; // 父进程存在
} else {
if (errno == ESRCH) {
return 0; // 父进程不存在
} else {
return 1; // 其他错误,假设父进程存在
}
}
}

// 监控父进程状态
void monitor_parent_status() {
pid_t original_ppid = getppid();
pid_t current_ppid;

printf("开始监控父进程状态...\n");
printf("原始父进程 ID: %d\n", original_ppid);

while (1) {
current_ppid = getppid();

if (current_ppid != original_ppid) {
printf("父进程 ID 发生变化: %d -> %d\n", original_ppid, current_ppid);

if (current_ppid == 1) {
printf("进程已被 init 进程收养\n");
break;
}

original_ppid = current_ppid;
}

if (!is_parent_alive()) {
printf("检测到父进程可能已结束\n");
break;
}

sleep(1);
}
}

// 守护进程检查
int is_daemon_process() {
pid_t ppid = getppid();

// 守护进程的特征
if (ppid == 1) {
return 1; // 由 init 进程管理
}

// 检查是否在后台运行
if (getpgrp() != tcgetpgrp(STDIN_FILENO)) {
return 1; // 不在前台进程组
}

return 0;
}

int main() {
printf("=== 进程状态监控工具 ===\n");

printf("当前进程信息:\n");
printf(" PID: %d\n", getpid());
printf(" PPID: %d\n", getppid());
printf(" PGID: %d\n", getpgrp());

// 检查是否为守护进程
if (is_daemon_process()) {
printf("✓ 当前进程是守护进程或后台进程\n");
} else {
printf("✗ 当前进程是前台进程\n");
}

// 检查父进程状态
if (is_parent_alive()) {
printf("✓ 父进程存在\n");
} else {
printf("✗ 父进程不存在\n");
}

// 如果需要,可以启动监控
char choice;
printf("\n是否启动父进程监控? (y/N): ");
if (scanf("%c", &choice) == 1 && (choice == 'y' || choice == 'Y')) {
monitor_parent_status();
}

return 0;
}

8. 特殊父进程 ID

1
2
3
4
5
6
7
8
9
10
11
12
// 特殊的父进程 ID 值
if (ppid == 0) {
// 内核进程或调度进程
printf("父进程是内核进程\n");
} else if (ppid == 1) {
// init 进程或 systemd
printf("父进程是 init 进程\n");
} else if (ppid == getppid()) {
// 自己是父进程(不太可能)
printf("异常:自己是自己的父进程\n");
}

9. 实际应用场景

场景1:守护进程实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void create_daemon() {
pid_t pid = fork();
if (pid == 0) {
// 第一次 fork 的子进程
setsid(); // 创建新会话

pid = fork();
if (pid == 0) {
// 第二次 fork 的子进程(真正的守护进程)
// 检查父进程是否为 init
if (getppid() == 1) {
printf("成功创建守护进程\n");
}
} else {
exit(0); // 第一次 fork 的子进程退出
}
} else {
wait(NULL); // 等待第一次 fork 的子进程退出
exit(0); // 父进程退出
}
}

场景2:进程监控

1
2
3
4
5
6
7
int monitor_process_tree(pid_t target_pid) {
// 实现进程树监控逻辑
pid_t current_ppid = getppid();
// ... 监控逻辑
return 0;
}

场景3:安全检查

1
2
3
4
5
6
7
8
9
10
11
12
int check_parent_process() {
pid_t ppid = getppid();

// 检查父进程是否为预期的进程
if (ppid != expected_parent_pid) {
syslog(LOG_WARNING, "父进程异常: %d", ppid);
return -1;
}

return 0;
}

10. 注意事项

使用 getppid 时需要注意:

孤儿进程: 父进程结束后,子进程被 init 进程收养(PPID 变为 1)

竞态条件: 父进程可能在检查期间结束

权限问题: 通常可以访问父进程信息,但在某些安全环境中可能受限

跨平台兼容: 在所有 Unix-like 系统中都可用

性能考虑: 调用成本很低,可以频繁使用

11. 进程生命周期中的变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 进程生命周期中 PPID 的变化示例
void demonstrate_ppid_changes() {
printf("进程生命周期 PPID 变化:\n");

pid_t original_ppid = getppid();
printf("1. 启动时 PPID: %d\n", original_ppid);

// fork 后子进程的 PPID
pid_t child = fork();
if (child == 0) {
printf("2. 子进程 PPID: %d\n", getppid());
// 如果父进程结束,PPID 会变为 1
}
}

总结

getppid 是一个简单但重要的系统调用,用于获取当前进程的父进程 ID。关键要点:

  1. 总是成功: 不会失败,总是返回父进程 ID2. 进程管理: 是进程管理和监控的基础信息3. 孤儿处理: 理解父进程结束后的孤儿进程处理机制4. 安全相关: 可用于进程身份验证和安全检查5. 系统工具: 广泛用于 ps、top 等系统工具

在编写需要了解进程关系的程序时,getppid 是必不可少的工具函数,它为程序提供了进程层次结构的重要信息。

getppid系统调用及示例-CSDN博客

data-ad-format="auto" data-full-width-responsive="true">