(1)fork的内部原理:进程的分裂生长模式,如果操作系统需要一个新进程来运行一个程序,那么操作系统会用一个现有的进程来复制生成一个新进程。老进程叫父进程,复制生成的新进程叫子进程。子进程有自己独立的PCB子,被内核同等调度。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
pid_t p1 = -1;
p1 = fork(); // 返回2次
if (p1 == 0)
{
// 这里是子进程
printf("子进程, pid = %d.\n", getpid()); //打印子进程pid
printf("hello world.\n");
printf("子进程, 父进程ID = %d.\n", getppid()); //打印父进程pid
}
if (p1 > 0)
{
// 这里是父进程
printf("父进程, pid = %d.\n", getpid()); //打印父进程pid
printf("父进程, p1 = %d.\n", p1);
}
if (p1 < 0)
{
//出错
}
return 0;
}
(2)fork函数调用一次会返回2次,返回值等于0的就是子进程,而返回值大于0的就是父进程。
(3)典型的使用fork的方法:使用fork后然后用if判断返回值,并且返回值大于0时就是父进程,等于0时就是子进程。
(4)fork的返回值在子进程中等于0,在父进程中等于本次fork创建的子进程的进程ID。
4.父子进程对文件的操作
(1)**子进程继承父进程中打开的文件,**父进程先open打开一个文件得到fd,然后在fork创建子进程。之后在父子进程中各自write向fd中写入内容。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
// 首先打开一个文件
int fd = -1;
pid_t pid = -1;
fd = open("1.txt", O_RDWR | O_TRUNC);
if (fd < 0)
{
perror("open");
return -1;
}
// fork创建子进程
pid = fork();
if (pid > 0)
{
// 父进程中
printf("parent.\n");
write(fd, "hello", 5);
sleep(1);
}
else if (pid == 0)
{
// 子进程
printf("child.\n");
write(fd, "world", 5);
sleep(1);
}
else
{
perror("fork");
exit(-1);
}
close(fd);
return 0;
}
测试结论是:接续写,打印出hello world,实际上本质原因是父子进程之间的fd对应的文件指针是彼此关联的。
(2)**父子进程各自独立打开同一文件实现共享,**父进程open打开1.txt然后写入,子进程打开1.txt然后写入。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
// 首先打开一个文件
int fd = -1;
pid_t pid = -1;
// fork创建子进程
pid = fork();
if (pid > 0)
{
// 父进程中
fd = open("1.txt", O_RDWR );
if (fd < 0)
{
perror("open");
return -1;
}
printf("parent.\n");
write(fd, "hello", 5);
sleep(1);
}
else if (pid == 0)
{
// 子进程
fd = open("1.txt", O_RDWR );
if (fd < 0)
{
perror("open");
return -1;
}
printf("child.\n");
write(fd, "world", 5);
sleep(1);
}
else
{
perror("fork");
exit(-1);
}
close(fd);
return 0;
}
结论是:分别写。
原因是父子进程分离后才各自打开的1.txt,这时候这两个进程的PCB已经独立了,文件表也独立了,因此2次读写是完全独立的。
5.进程的诞生和消亡
(1) **进程的诞生:**进程0和进程1,fork,vfork。
(2) **进程的消亡:**正常终止和异常终止,进程终止时理应完全释放这些资源,操作系统会自动回收这个进程涉及到的所有的资源,每个进程都需要一个帮助它收尸的人,这个人就是这个进程的父进程。
(3) 僵尸进程:子进程先于父进程结束。子进程结束后父进程此时并不一定立即就能帮子进程“收尸”,在这一段(子进程已经结束且父进程尚未帮其收尸)子进程就被成为僵尸进程,
(4) 孤儿进程:父进程先于子进程结束,子进程成为一个孤儿进程,所有的孤儿进程都自动成为一个特殊进程(进程1,也就是init进程)的子进程。
6.父进程wait回收子进程
(1)wait的参数status。status用来返回子进程结束时的状态,父进程通过wait得到status后就可以知道子进程的一些结束状态信息。
(2)wait的返回值pid_t,这个返回值就是本次wait回收的子进程的PID。当前进程有可能有多个子进程,wait函数阻塞直到其中一个子进程结束wait就会返回,wait的返回值就可以用来判断到底是哪一个子进程本次被回收了。
(3)WIFEXITED、WIFSIGNALED、WEXITSTATUS这几个宏用来获取子进程的退出状态。
WIFEXITED宏用来判断子进程是否正常终止(return、exit、_exit退出)
WIFSIGNALED宏用来判断子进程是否非正常终止(被信号所终止)
WEXITSTATUS宏用来得到正常终止情况下的进程返回值的
对wait做个总结:wait主要是用来回收子进程资源,回收同时还可以得知被回收子进程的pid和退出状态。
fork后wait回收:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(void)
{
pid_t pid = -1;
pid_t ret = -1;
int status = -1;
pid = fork();
if (pid > 0)
{
// 父进程
printf("parent.\n");
ret = wait(&status);
printf("子进程已经被回收,子进程pid = %d.\n", ret);
printf("子进程是否正常退出:%d\n", WIFEXITED(status));
printf("子进程是否非正常退出:%d\n", WIFSIGNALED(status));
printf("正常终止的终止值是:%d.\n", WEXITSTATUS(status));
}
else if (pid == 0)
{
// 子进程
printf("child pid = %d.\n", getpid());
return 51;
//exit(0);
}
else
{
perror("fork");
return -1;
}
return 0;
}
7.exec族函数
(1)fork子进程是为了执行新程序(fork创建了子进程后,子进程和父进程同时被OS调度执行,因此子进程可以单独的执行一个程序,这个程序宏观上将会和父进程程序同时进行)
(2)可以直接在子进程的if中写入新程序的代码。这样可以,但是不够灵活,因为我们只能把子进程程序的源代码贴过来执行(必须知道源代码,而且源代码太长了也不好控制),譬如说我们希望子进程来执行ls -la 命令就不行了(没有源代码,只有编译好的可执行程序)
(3)使用exec族函数运行新的可执行程序(exec族函数可以直接把一个编译好的可执行程序直接加载运行)
(4)我们有了exec族函数后,我们典型的父子进程程序是这样的:子进程需要运行的程序被单独编写、单独编译连接成一个可执行程序,主程序为父进程,fork创建了子进程后在子进程中exec来执行一个可执行程序,达到父子进程分别做不同程序同时(宏观上)运行的效果。
使用execl和execv运行ls -l -a:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(void)
{
pid_t pid = -1;
pid_t ret = -1;
int status = -1;
pid = fork();
if (pid > 0)
{
// 父进程
printf("parent, 子进程id = %d.\n", pid);
}
## 最后
**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**
**深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。**
**因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
![img](https://img-blog.csdnimg.cn/img_convert/85ddf54644c3b2870ead801c57eaf5bd.png)
![img](https://img-blog.csdnimg.cn/img_convert/2d206e8f19912c68a9d0e5ef19392ff0.jpeg)
![img](https://img-blog.csdnimg.cn/img_convert/a245cbac3240e689fb477147fb0cf1c3.png)
![img](https://img-blog.csdnimg.cn/img_convert/8d15af2c79f67271ffba84d776c033aa.png)
![img](https://img-blog.csdnimg.cn/img_convert/188d7a6138842aaa6a6eedc64fb0bf72.png)
![img](https://img-blog.csdnimg.cn/img_convert/da8a4c766c233f2a87b5b297646ac6a2.png)
![](https://img-blog.csdnimg.cn/img_convert/f91a0246f7aa38fec0e9c4a07a91ac19.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**
[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!
85786806)]
[外链图片转存中...(img-PPBU7UxG-1715585786807)]
[外链图片转存中...(img-3TfKETI1-1715585786807)]
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**
[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!