内容概要
一、进程的创建
在Linux中创建一个新进程的方法是使用 fork()
函数。
fork()
函数用于从已存在的进程中创建一个新进程。新进程称为子进程,而原进程称为父进程。
使用fork()
函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的 地址空间,包括进程上下文、代码段、进程堆栈、内存信息、打开的文件描述符、信号处理函数、进程优先级、进程组号、当前工作目录、根目录、资源限制、控制终端等。
子进程所独有的只有它的进程号、资源使用和计时器等。
因为子进程几乎是父进程的完全复制,所以父子两个进程会运行同一个程序。因此需要用一种方式来区分它们,并使它们照此运行,否则,这两个进程只能做相同的事。
父子进程一个很重要的区别是:fork()
的返回值不同。
父进程中的返回值是子进程的进程号,而子进程中返回0。可以通过返回值来判定该进程是父进程还是子进程。
注意:
子进程没有执行fork()
函数,而是从fork()
函数调用的下一条语句开始执行。PID不同
1、fork()函数的原型如下所示。
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
功能:
以精确复制父进程的方式,创建新的子进程
返回:
成功:父进程返回子进程PID, 子进程返回0
失败:父进程返回-1,没有子进程
2、获取PID以及父进程PID的函数的原型如下所示。
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
功能:获取当前进程的PID
pid_t getppid(void);
功能:获取当前进程父进程的PID
3、进程创建的程序示例如下所示。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
int x = 0;
/* 定义进程PID记录的变量 */
pid_t pid = 0;
/* 输出调试语句 */
printf("fork() function test!\n");
/* 调用fork()函数创建进程 */
pid = fork();
/* 函数返回值判断 */
if (pid == -1) /* 返回错误 */
{
perror("fork error");
return -1;
}
else if (pid == 0) /* 子进程 */
{
x = 10;
printf("I'm child, pid = %d, &x = %p, x = %d\n", pid, &x, x);
/* 子进程 */
printf("my PID = %d, my parent PID = %d\n", getpid(), getppid());
}
else /* 父进程 */
{
sleep(1); /* 延时片刻,保证子进程先运行 */
printf("I'm parent, pid = %d, &x = %p, x = %d\n", pid, &x, x);
/* 父进程 */
printf("return PID = %d, parent pid = %d\n", pid, getpid());
}
sleep(1);
return 0;
}
编译上述程序并进程运行之后,显示效果下图1.1所示。
![](https://i-blog.csdnimg.cn/blog_migrate/baca2dfc19663ac15105db7a442ebb42.png)
关于进程的退出操作,可以参考博文:Linux – exit()函数、_exit()函数、return的说明与使用
二、孤儿进程产生与处理
孤儿进程:子进程本身处于运行状态,但是其父进程提前结束,此子进程被称为孤儿进程。
孤儿进程 将被 init 进程(进程号为 1 )所收养,并由 init 进程对它们完成状态收集工作,因此孤儿进程并不会有什么危害。
孤儿进程会被1号进程收养,并最终由1号进程回收。不论子进程处于 运行状态(R)、休眠状态(S)、僵死状态(Z)、停止状态(T)都可被收养。
下面用一个简单的程序显示一下孤儿进程的状态变化。
#include <stdio.h>
#include <stdlib.h> /* exit */
#include <unistd.h> /* getpid等 */
int main(int argc, const char *argv[])
{
/* 定义进程PID记录的变量 */
pid_t pid = 0;
/* 输出调试语句 */
printf("Orphan process test!\n");
/* 调用fork()函数创建进程 */
pid = fork();
/* 函数返回值判断 */
if (pid == -1) /* 返回错误 */
{
perror("fork error");
return -1;
}
else if (pid == 0) /* 子进程 */
{
unsigned long n = 0, m = 0;
/* 子进程 */
printf("I'm child, my PID = %d, my parent PID = %d\n", getpid(), getppid());
/* 子进程延时片刻,保持子进程在父进程后面结束 */
sleep(10);
/* 调用函数退出子进程 */
exit(EXIT_SUCCESS);
}
else /* 父进程 */
{
/* 延时片刻,保证子进程先运行 */
usleep(20);
/* 父进程延时片刻 */
printf("Parent sleep(5) ...\n");
sleep(5);
printf("I'm parent, return PID = %d, my pid = %d\n", pid, getpid());
/* 调用函数退出父进程 */
exit(EXIT_SUCCESS);
}
return 0;
}
编译上述程序并进程运行之后,显示效果下图2.1所示。
![](https://i-blog.csdnimg.cn/blog_migrate/5a16006f919245b581c2245f48795657.png)
此时在另外一个终端可以查看当前进程的状态。使用指令ps -ajx
查看,显示效果下图2.2所示。
![](https://i-blog.csdnimg.cn/blog_migrate/1c7cb7ec6af8df22b9d2b5924599bd15.png)
由上图可以看出来,在父进程退出之后,子进程的 PPID 变为了 1,也就是其父进程变为了 PPID 变为了 init 进程。
好啦,废话不多说,总结写作不易,如果你喜欢这篇文章或者对你有用,请动动你发财的小手手帮忙点个赞,当然 关注一波 那就更好了,就到这儿了,么么哒(*  ̄3)(ε ̄ *)。
![](https://i-blog.csdnimg.cn/blog_migrate/c91fca836c9c9f06e853e45378ea0e4d.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/fd2a1e1aa3f9cf878d55030fd4387aa9.gif)