简单进程创建
/**
* @author IYATT-yx
* @brief 创建进程
*/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
// fork创建子进程,子进程的虚拟地址空间和父进程一样(pid不一样)
// 不可确定父进程和子进程谁先执行
pid_t pid = fork();
// 父进程中fork的返回值为子进程的pid
if (pid > 0)
{
printf("我是父进程,我的pid为: %d ,我的儿子的pid为: %d\n", getpid(), pid);
}
// 子进程中fork的返回值为0
else if (pid == 0)
{
printf("我是子进程,我的pid是: %d ,我父亲的pid是: %d\n", getpid(), getppid());
}
else
{
perror("fork");
}
}
/**
* @author IYATT-yx
* @brief 创建多进程
*/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
int i;
for (i = 0; i < 3; ++i)
{
pid_t pid = fork();
// 防止子进程再创建子进程
if (pid == 0)
{
break;
}
}
// 每次循环创建一个子进程对应一个循环次数
if (i == 0)
{
printf("我是子进程1: pid = %d\tppid = %d\n", getpid(), getppid());
}
else if (i == 1)
{
printf("我是子进程2: pid = %d\tppid = %d\n", getpid(), getppid());
}
else if (i == 2)
{
printf("我是子进程3: pid = %d\tppid = %d\n", getpid(), getppid());
}
// 最后一次循环后,再次对i自增,不再满足循环条件,停止创建子进程,此时对应的是父进程
else if (i == 3)
{
// 可能出现父进程比子进程先执行完,而终端在父进程结束后可能认为程序结束了,就会切回终端
// 然后出现切回终端后继续执行子进程并将要打印输出的内容显示到切回后的命令提示符后
// 所以让父进程sleep 1秒,最后执行
sleep(1);
printf("我是父进程: pid = %d\n", getpid());
}
}
exec函数族
具体函数信息可用 man
命令查询
int execl(const char *pathname, const char *arg, ... (char *) NULL */);
int execlp(const char *file, const char *arg, ... (char *) NULL */);
int execle(const char *pathname, const char *arg, ... (char *) NULL, char *const envp[] */);
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], *const envp[]);
- 替换子进程中 .text 的内容, 让父子进程执行不相干的操作
- 在一个程序中调用另外一个程序
/**
* @author IYATT-yx
* @brief exec族函数实践示例
*/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
pid_t pid = fork();
if (pid > 0)
{
sleep(1);
// 常用于执行系统命令
execlp("pwd", "", NULL);
// execlp执行成功,则后面的代码替换掉,不执行
// 如果执行失败,则继续往下,不需要去判断返回值
perror("execlp");
return -1;
}
if (pid == 0)
{
// 常用于执行自己的程序,和 execlp 参数差不多
/**
* @param 执行程序的绝对路径
* @param 占位参数任意设定内容
* @param 指定执行程序的参数
* @param 以NULL结尾
*/
execl("/bin/ls", "", "-lh", ".", NULL);
perror("execl");
return -1;
}
}
进程回收
- 孤儿进程: 父进程结束了,而子进程还在运行. init 进程会继承成为子进程的父进程(一个进程结束,要释放它占用的系统资源,而子进程的PCB只能由父进程来释放, 因此存在了 init 继任父进程的机制)
- 僵尸进程: 子进程结束了,而父进程还在运行,但是父进程没有释放子进程的PCB,这个子进程此时即为僵尸进程.
/**
* @author IYATT-yx
* @brief 孤儿进程
*/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
pid_t pid = fork();
if (pid > 0)
{
printf("我是父进程 pid = %d\n", getpid());
}
if (pid == 0)
{
sleep(1);
printf("我是子进程,原父进程已死,当前父进程 pid = %d\n", getppid());
}
}
运行结果:
再看看 pid 为 8 的进程就是 init
/**
* @author IYATT-yx
* @brief 僵尸进程
*/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdbool.h>
int main(void)
{
pid_t pid = fork();
if (pid > 0)
{
while (true)
{
printf("我是父进程 pid = %d\n", getpid());
sleep(1);
}
}
else if (pid == 0)
{
printf("我是子进程,父进程的pid = %d\n", getppid());
}
}
运行这个程序后,打开另外一个终端查询这个程序名字,第一个就是正在运行的父进程,而第二个就是已死亡的子进程(defunct 死人),但是资源还未被回收(父进程忙碌中,无法回收子进程)
/**
* @author IYATT-yx
* @brief wait回收进程
*/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdbool.h>
int main(void)
{
pid_t pid = fork();
if (pid > 0)
{
printf("我是父进程\n");
int status;
pid_t wPid = wait(&status);
// return/exit结束
// w if exited
if (WIFEXITED(status))
{
// w exit status
printf("exit code: %d\n", WEXITSTATUS(status));
}
// kill信号终止
// w if signaled
else if (WIFSIGNALED(status))
{
// w term sig
printf("signal: %d\n", WTERMSIG(status));
}
printf("父: pid = %d,回收进程的pid = %d\n", getpid(), wPid);
}
else if (pid == 0)
{
// // 测试使用 kill 命令杀死进程
// while (true)
// {
// printf("子: pid = %d\tppid = %d\n", getpid(), getppid());
// sleep(1);
// }
sleep(3);
printf("子: pid = %d\tppid = %d\n", getpid(), getppid());
exit(66);
}
}
使用 wait
回收子进程,调用一次只能回收一个,并且不能指定回收某个子进程,只能死一个回收一个. wait通过阻塞的方式,一直等待子进程死,只要子进程一死,它就立即回收.
/**
* @author IYATT-yx
* @brief waitpid回收进程
*/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdbool.h>
int main(void)
{
pid_t pid = fork();
if (pid > 0)
{
printf("我是父进程\n");
int status;
/**
* @brief
* 返回值:
* > 0: 被回收进程的pid
* -1: 无子进程
* == 0: 第三个参数为 WNOHANG, 且子进程正在运行
* @param . 要回收的进程的pid
* @param . 回收进程的结束方式
* @param . 设置为0则阻塞, WNOHANG就是非阻塞
*/
pid_t wPid = waitpid(pid, &status, 0);
// return/exit结束
// w if exited
if (WIFEXITED(status))
{
// w exit status
printf("exit code: %d\n", WEXITSTATUS(status));
}
// kill信号终止
// w if signaled
else if (WIFSIGNALED(status))
{
// w term sig
printf("signal: %d\n", WTERMSIG(status));
}
printf("父: pid = %d,回收进程的pid = %d\n", getpid(), wPid);
}
else if (pid == 0)
{
// // 测试使用 kill 命令杀死进程
// while (true)
// {
// printf("子: pid = %d\tppid = %d\n", getpid(), getppid());
// sleep(1);
// }
sleep(3);
printf("子: pid = %d\tppid = %d\n", getpid(), getppid());
exit(66);
}
}