文章目录
1. 进程ID
- 每个进程都有一个非负整型表示的唯一进程ID
- 进程ID唯一,但进程终止后进程ID可复用
#include <stdio.h>
#include <unistd.h>
int main() {
int pid = getpid();
printf("pid = %d\n", pid);
int uid = getuid();
printf("uid = %d\n", uid);
return 0;
}
2. 创建进程 (fork)
#include <unistd.h>
pid_t fork(void);
- 调用成功返回两次
- 子进程返回0,父进程返回子进程ID
- 失败返回-1
fork创建进程会复制父进程存储空间,有全拷贝和写时拷贝(子进程使用父进程中数据时才拷贝)
1#include <stdio.h>
2 #include <unistd.h>
3
4 int main()
5 {
6 pid_t pid;
7
8 printf("parent id = %d\n", getpid());
9
10 pid = fork(); // fork后的代码父子进程都执行
11 if (pid > 0)
12 {
13 printf("this parent, pid = %d\n", getpid());
14 }
15 if (pid == 0)
16 {
17 printf("this child, pid = %d\n", getpid());
18 }
19
20 return 0;
21 }
输出:
parent id = 17277
this parent, pid = 17277
this child, pid = 17278
3. 使用vfork 创建进程
#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);
与fork 区别:
- vfork直接使用父进程存储空间,不拷贝
- vfork 保证子进程先运行,当子进程调用exit退出后,父进程才执行。
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <sys/types.h>
4 #include <stdlib.h>
5
6 int main()
7 {
8 pid_t pid;
9 int count = 0;
10
11 printf("parent id = %d\n", getpid());
12
13 pid = vfork();
14 if (pid > 0)
15 {
16 while (1)
17 {
18 printf("this parent, pid = %d\n", getpid());
19 printf("count = %d\n", count);
20 sleep(1);
21 }
22 }
23 if (pid == 0)
24 {
25 while (1)
26 {
27 printf("this child, pid = %d\n", getpid());
28 sleep(1);
29 count++;
30 if (count == 4)
31 {
32 exit(0);
33 }
34 }
35 }
36
37 return 0;
38 }
输出:
parent id = 17387
this child, pid = 17388
this child, pid = 17388
this child, pid = 17388
this child, pid = 17388
this parent, pid = 17387
count = 4
this parent, pid = 17387
count = 4
this parent, pid = 17387
count = 4
注:子进程非正常退出会影响变量状态
4. 进程终止
4.1 正常终止
- 在main函数内执行return语句
- 调用exit函数(标准c库)
- 调用_exit或_Exit函数(属于系统调用)
- 进程的最后一个线程在其启动例程中执行return语句,该线程的返回值不作用于进程的返回值。当最后一个线程从启动例程返回时,该进程以终止状态0返回。
- 最后一个线程调用pthread_exit函数。
4.2 异常终止
- 调用abort
- 当进程接收到某些信号时。
- 最后一个线程对取消(cancellation)请求做出响应
5. 等待子进程退出
僵尸进程:当子进程退出状态不被父进程收集会变成僵尸进程,kill对其无效。
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus);
pid_t waitpid(pid_t pid, int *wstatus, int options);
5.1 wait
参数:(传出类型参数) 回收进程的状态。
返回值:
- 成功: 回收进程的pid
- 失败: -1, errno
函数作用:
- 1: 阻塞等待子进程退出
- 2: 清理子进程残留在内核的 pcb 资源
- 3: 通过传出参数,得到子进程结束状态
获取子进程正常终止值:
- WIFEXITED(status) 为真 调用 WEXITSTATUS(status) 得到 子进程 退出值。
获取导致子进程异常终止信号:
- WIFSIGNALED(status)为真 调用 WTERMSIG(status) 得到 导致子进程异常终止的信号编号。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
int count = 0;
int status = 0;
pid = vfork();
if (pid > 0)
{
wait(&status);
if (WIFEXITED(status))
{
printf("child quit status = %d\n", WEXITSTATUS(status));
}
while (1)
{
printf("this parent, pid = %d\n", getpid());
printf("count = %d\n", count);
sleep(1);
}
}
if (pid == 0)
{
while (1)
{
printf("this child, pid = %d\n", getpid());
sleep(1);
count++;
if (count == 2)
{
exit(3);
}
}
}
return 0;
}
输出:
this child, pid = 17559
this child, pid = 17559
child quit status = 3
this parent, pid = 17558
count = 2
this parent, pid = 17558
count = 2
注:
- 如果其所有子进程都还在运行,则阻塞。
- 如果一个子进程已终止,正等待父进程获取其终止状态,则获得该子进程的终止状态立即返回。
- 如果它没有任何子进程,则立即出错返回。
5.2 waitpid
参数:
- pid:指定回收某一个子进程pid
- status:(传出) 回收进程的状态。
- options:WNOHANG 指定回收方式为,非阻塞。
返回值: -
0 : 表成功回收的子进程 pid
- 0 : 函数调用时, 参3 指定了WNOHANG, 并且,没有子进程结束。
- -1: 失败。errno
总结:
wait、waitpid 一次调用,回收一个子进程
使用while 回收多个进程
5.3 init进程
- 它是内核启动的第一个用户级进程。init有许多很重要的任务,比如像启动getty(用于用户登录)、实现运行级别、以及处理孤立进程。
- 父进程如果在子进程退出前退出,子进程就成为孤儿进程,Linux避免系统存在过多的孤儿进程,init进程会收留孤儿进程,成为其父进程。
6. exec族函数
https://blog.csdn.net/u014530704/article/details/73848573
7. system
#include <stdlib.h>
int system(const char *command);
函数原型:
返回值:
- 执行成功,返回进程的状态值
- sh不能执行时,返回127
- 失败返回-1
#include <stdio.h>
#include <stdlib.h>
int main()
{
if (system("ps") == -1)
{
printf("system failed\n");
perror("status:");
}
printf("system after");
return 0;
}
输出:
PID TTY TIME CMD
18895 pts/2 00:00:00 bash
19001 pts/2 00:00:00 a.out
19002 pts/2 00:00:00 sh
19003 pts/2 00:00:00 ps
8. peopen
相比于system,可以获取运行的输出结果
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
参数:
- command: 是一个指向以 NULL 结束的 shell 命令字符串的指针。这行命令将被传到 bin/sh 并使用 -c 标志,shell 将执行这个命令。
- mode: 只能是读或者写中的一种,得到的返回值(标准 I/O 流)也具有和 type 相应的只读或只写类型。如果 type 是 “r” 则文件指针连接到 command 的标准输出;如果 type 是 “w” 则文件指针连接到 command 的标准输入。
- stream:popen返回的文件指针
https://blog.csdn.net/libinbin_1014/article/details/51490568
返回值: - 成功:则返回一个读或者打开文件的指针
- 失败:返回NULL,具体错误根据errno判断
#include <stdio.h>
#include <stdlib.h>
int main()
{
char buf[1024] = {0};
FILE *fp = NULL;
fp = popen("ps", "r");
int n = fread(buf, 1, 1024, fp);
printf("read %d byte, context: %s\n", n, buf);
return 0;
}
输出:
read 151 byte, context: PID TTY TIME CMD
18895 pts/2 00:00:00 bash
19470 pts/2 00:00:00 a.out
19471 pts/2 00:00:00 sh
19472 pts/2 00:00:00 ps