福州阳楠科技网站建设有限公司,安卓一键制作app软件,字体艺术设计在线生成,织梦网站栏目添加fork 函数
fork 函数是 Linux 中一个非常重要的函数#xff0c;它的作用是从已存在的进程中创建一个新进程。这个新进程就是当前进程的子进程。
fork() 函数使用方法#xff1a;它在头文件 #include unistd.h 中#xff0c;函数原型为
pid_t fork(void);
用一个…fork 函数
fork 函数是 Linux 中一个非常重要的函数它的作用是从已存在的进程中创建一个新进程。这个新进程就是当前进程的子进程。
fork() 函数使用方法它在头文件 #include unistd.h 中函数原型为
pid_t fork(void);
用一个 pid_t 类型的变量来接收 fork() 函数的返回值。当创建进程成功时fork() 函数会给子进程返回 0给父进程返回新创建的子进程的 id当创建失败时fork() 函数会返回 -1给父进程返回因为子进程根本没创建出来
当创建子进程成功后操作系统会做
分配新的内存块和内核数据结构给子进程将父进程部分数据结构内容拷贝至子进程添加子进程到系统进程列表当中fork返回开始调度器调度
当一个进程调用fork之后就有两个二进制代码相同的进程。但它们都运行到相同的地方后每个进程都会可以开始它们自己的旅程。可以使用下面的代码测试Linux系统
#includestdio.h
#includeunistd.h
#includestdlib.h
int main(void)
{pid_t pid fork();printf(Before: pid is %d\n, getpid());if (pid -1) perror(fork()), exit(1);if (pid 0){printf(child:pid is %d, fork return %d\n, getpid(), pid);}else{printf(After:pid is %d, fork return %d\n, getpid(), pid);}sleep(1);return 0;
} 运行结果
fork()函数 内核示意图 fork() 函数的常规用法及错误原因
常规用法
一个父进程希望复制自己使父子进程同时执行不同的代码段。例如父进程等待客户端请求生成子进程来处理请求。一个进程要执行一个不同的程序。例如子进程从fork返回后调用exec函数
错误原因
系统中有太多的进程实际用户的进程数超过了限制
C/C中捕捉错误的方式代码运行完毕结果错误
errno 是C语言调用C函数时的错误码当调用函数出错时它会在内部给出错误码数字但这个数字往往我们不知道是什么错误那我们就要用 strerror 函数来解析这个错误码。strerror 函数可以将错误码以字符串的形式描述起来。
代码异常终止
当代码异常终止时一般操作系统会给这个进程发信号让这个进程以某种错误而终止其表现就是这个进程被操作系统杀掉它的内部逻辑类似于手动调用 kill 指令。
kill -n id
n 为选项表示各种信号id 为被发送信号进程的 id
使用如下指令可以查看 Linux 中全部的信号
kill -l
进程终止
进程退出有三种场景
代码运行完毕结果正确代码运行完毕结果不正确代码异常终止
这三种情况可以用2个数字组合完全覆盖
当进程正常终止时可以通过 echo $? 查看进程退出码。
进程正常终止有三种情况1. 从main返回2. 调用exit3. 调用_exit
异常退出ctrl c信号终止
exit() 函数
#include unistd.h
void exit(int status);
status 定义了进程的终止状态也就是进程的结果正确还是不正确。status 是一个整形值父进程可以通过 wait函数来获取该值得到子进程的最终执行结果
_exit() 函数
#include unistd.h
void _exit(int status);
_exit() 函数与 exit() 函数的作用可以说是一模一样只是在细节上有所差异在终止进程时exit() 函数会自动刷新缓冲区而_exit() 函数不会 其实相当于 exit()函数 是先调用了 _exit() 函数如图 由此可以得出一个结论我们所认识的缓冲区并不在操作系统内部否则 exit() 和 exit() 都应该能刷新缓冲区。
进程等待
什么是进程等待
通过 wait/waitpid 的方式让父进程对子进程进行资源回收的等待过程。那为什么要进行等待呢
第一可以解决子进程僵尸问题带来的内存泄漏问题进程僵尸只有父进程回收才能解决且不能被杀掉所以这是目前必须使用进程等待解决的问题第二父进程创建子进程的目的就是让子进程来完成任务而父进程需要知道子进程任务到底完成的如何就必须通过等待的方式来获取子进程退出的信息两个数字退出码和信号这个退出信息也许并不是必须的但是系统需要提供这样的基础功能
如何进行等待
在 Xshell 终端中输入如下指令查看 waitpid 和 wait 的使用方式
wiat 和 waitpid 方法
man waitpid wait 函数
返回值成功返回被等待进程 pid失败则返回 -1。
参数输出型参数获取子进程退出状态若不关心则可以设置成为NULL
waitpid 函数
返回值当正常返回的时候waitpid返回收集到的子进程的进程ID如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在
参数pidPid-1,等待任一个子进程。与wait等效。Pid0等待其进程ID与pid相等的子进程statusWIFEXITED(status) 若为正常终止子进程返回的状态则为真。查看进程是否是正常退出WEXITSTATUS(status): 若WIFEXITED非零提取子进程退出码。查看进程的退出码
options:
WNOHANG: 若pid指定的子进程没有结束则waitpid()函数返回0不予以等待。若正常结束则返回该子进程的ID。
那么父进程是如何得知子进程的退出信息呢
子进程在退出的时候要修改状态 Z并将自己的退出信息和退出码写入 pcd 中父进程通过读取子进程的 pcd 获取这些信息。注意不能使用全局变量获取子进程的信息因为进程之间具有独立性各有各的进程地址空间子进程对数据的修改不改变父进程中的数据
获取子进程的 status
当需要获取子进程的 status时通常采用位图的思想使用位运算 如图经过位运算得到需要的数字
是否收到信号的判定方法exit sig 是否等于0等于0说明没收到信号不等于0说明收到了异常的信号当一个进程异常了收到信号exit code 就变得没有意义了。
进程的阻塞等待和非阻塞等待 设 wait/waitpid 的返回值为 rid
rid 0 等待成功rid 0等待成功但对方子进程还没有退出rid 0等待失败
对于 waitpid 方法而言它的第三个参数 options 可以控制父进程是阻塞等待还是非阻塞等待。
阻塞等待子进程不退出父进程就一直等待子进程waitpid 不返回。这种情况在计算机中叫 宕dang机或应用夯hang住了。这种等待的缺点就是父进程在等待的过程中什么事都做不了
进程的阻塞式等待代码
int main()
{pid_t pid;pid fork();if (pid 0) {printf(%s fork error\n, __FUNCTION__);return 1;}else if (pid 0) { //childprintf(child is run, pid is : %d\n, getpid());sleep(5);exit(257);}else {int status 0;pid_t ret waitpid(-1, status, 0);//阻塞式等待等待5Sprintf(this is test for wait\n);if (WIFEXITED(status) ret pid) {printf(wait child 5s success, child return code is :%d.\n, WEXITSTATUS(status));}else {printf(wait child failed, return.\n);return 1;}}return 0;
}
非阻塞等待如果子进程的退出条件不满足wait/waitpid 不会阻塞而是立即返回所以非阻塞等待一般要重复读多次调用这种一般叫做非阻塞轮询方案进行进程等待 这样做的好处是在子进程没有退出的情况下父进程可以在等待的过程中顺便做一些占用时间比较少的事情。
进程的非阻塞式等待代码
#include stdio.h
#include unistd.h
#include stdlib.h
#include sys/wait.h
int main()
{pid_t pid;pid fork();if (pid 0) {printf(%s fork error\n, __FUNCTION__);return 1;}else if (pid 0) { //childprintf(child is run, pid is : %d\n, getpid());sleep(5);exit(1);}else {int status 0;pid_t ret 0;do{ret waitpid(-1, status, WNOHANG);//非阻塞式等待if (ret 0) {printf(child is running\n);}sleep(1);} while (ret 0);if (WIFEXITED(status) ret pid) {printf(wait child 5s success, child return code is :%d.\n, WEXITSTATUS(status));}else {printf(wait child failed, return.\n);return 1;}}return 0;
}