Linux下fork()函数浅析
实验环境:Ubuntu 3.5.0-32-generic
头文件:
#include <unistd.h>
#include <sys/types.h>
函数原型:
pid_t fork(void);
(pid_t 是一个宏定义,其实质是int 被定义在#include<sys/types.h>中)
返回值: 若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1
fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。
一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。当子进程被调度得以执行时,其从程序中fork()语句下一条指令开始执行。
由fork创建的新进程被称为子进程(child process)。该函数被调用一次,但返回两次。两次返回的区别是子进程的返回值是0,而父进程的返回值则是新进程(子进程)的进程
id。将子进程id返回给父进程的理由是:因为一个进程的子进程可以多于一个,没有一个函数使一个进程可以获得其所有子进程的进程id。 对子进程来说,之所以fork返回0给它,是因为它随时可以调用getpid()来获取自己的pid;也可以调用getppid()来获取父进程的id。(进程id 0总是由交换进程使用,所以一个子进程的进程id不可能为0 )。
Program1:
-
#include<stdlib.h>
-
#include<stdio.h>
-
#include<sys/types.h>
-
#include<unistd.h>
-
-
intmain(intargc,char*argv[])
-
{
-
pid_tpid=0;
-
-
printf("Beforefork!--with\"\\n\"\n");
-
printf("Beforefork!--without\"\\n\"");
-
-
pid=fork();
-
-
if(pid==0){
-
printf("Childprocess!\n");
-
pid_tpid_child=getpid();
-
printf("Pid=%d\n",pid_child);
-
}
-
-
elseif(pid>0){
-
printf("Parentprocess!\n");
-
pid_tpid_parent=getpid();
-
printf("Pid=%d\n",pid_parent);
-
}
-
-
else{
-
printf("forkfailure!\n");
-
}
-
-
exit(0);
-
}
Output:
程序浅析:
在父进程中利用fork()系统调用生成子进程,此时父进程pid_parent = 3804,子进程pid_child = 3805;生成子进程后,父进程继续执行输出父进程pid,到遇见exit(0)后父进程退出,并退出Linux控制台。调度函数调度执行子进程,子进程在从程序fork语句下一语句开始执行,输出子进程pid,直到遇见exit(0)结束。
在程序中字符串"Before fork!--with "\n""与字符串"Before fork--without "\n""的输出语句均在原程序中fork语句前执行,但前者只在父进程中输出一次,而后者在父、子进程都有输出一次,主要在于输出语句“printf”的实现中对输出缓存的处理,输出语句中的换行符"\n"会强制刷新输出缓存,故带换行符的输出语句只在父进程中输出一次,而后者在父进程结束后仍在输出缓存中保存,在子进程执行至输出语句printf("Child process!\n")时一并输出。
Program2:
-
#include<stdlib.h>
-
#include<stdio.h>
-
#include<sys/types.h>
-
#include<unistd.h>
-
-
intmain(intargc,char*argv[])
-
{
-
pid_tpid=0;
-
intcount=13;
-
-
pid=fork();
-
-
if(pid==0){
-
sleep(1);
-
count=31;
-
printf("Childprocess!\nPid=%d\n",getpid());
-
}
-
-
elseif(pid>0){
-
wait(NULL);
-
printf("Parentprocess!\nPid=%d\n",getpid());
-
}
-
-
else{
-
printf("Forkerror!\n");
-
}
-
-
printf("Thereare%dapples!\n",count);
-
-
exit(0);
-
}
Output:
wait()会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用wait()时子进程已经结束,则wait()会立即返回子进程结束状态值。子进程的结束状态值会由参数status
返回,而子进程的进程识别码也会一快返回。如果不在意结束状态值,则参数status 可以设成NULL。
在本程序中,父进程调用fork()函数创建子进程3837后,父进程继续执行,当执行到wait(NULL);时,阻塞自己,调度函数调度子进程开始执行,子进程结束后,父进程从阻塞前的地方开始继续执行,此时子进程对count值的改变并未传递至父进程,输出count值,直到exit(0)结束。
Program 3:
-
#include<stdlib.h>
-
#include<stdio.h>
-
#include<sys/types.h>
-
#include<unistd.h>
-
-
intmain(intargc,char*argv[])
-
{
-
fork();
-
fork()&&fork()||fork();
-
fork();
-
exit(0);
-
}
以上程序共有多少个进程?
共创建19个新进程,与父进程一共20个进程。
我们对以上程序作一些修改,让每个进程执行均有一个标志性的输出,即可验证我们的猜想!
Program 3‘:
-
#include<stdlib.h>
-
#include<stdio.h>
-
#include<sys/types.h>
-
#include<unistd.h>
-
-
intmain(intargc,char*argv[])
-
{
-
fork();
-
fork()&&fork()||fork();
-
fork();
-
-
printf("Endofprocess!\n");
-
exit(0);
-
}
Output:
由以上程序可见,共执行输出语句20次,即我们的分析得以验证:共20个进程。
*由于某些原因,先前所用Ubuntu系统故障,此程序在centos平台完成测试。
附录:
wait()与waitpid()浅析
sleep()系统调用浅析
分享到:
相关推荐
从一道面试题深入探讨Linux下fork的运行机制 在Linux操作系统中,`fork()`系统调用是进程管理的核心功能之一,它允许一个已存在的进程创建一个新的进程,即子进程。子进程几乎完全复制父进程的状态,包括内存映像、...
### Linux 下 `fork()` 运行机制解析及面试题解答 #### 预备知识 在深入了解本面试题之前,我们需要掌握一些关于Linux进程管理的基础概念: 1. **进程的概念**:在Linux环境下,进程可以被视为一个正在执行的程序...
### Linux 下进程、线程与 fork 的深入理解 #### 题目背景及解析 本篇文章将基于一道经典的面试题目来探讨 Linux 下进程创建机制,特别是 `fork` 函数的工作原理。该题目不仅考验应试者对进程创建的理解,还涉及了...
【Linux内核经典面试题详解】 1. Linux内核锁:Linux内核中主要有自旋锁和信号量两种锁机制。自旋锁用于保护短暂的、不会引起阻塞的临界区,而信号量则允许任务在无法获取锁时进入睡眠状态,适合处理可能长时间持有...
Linux程序员面试题通常涵盖操作系统原理、C语言编程基础、Shell脚本、网络编程、文件系统、进程管理等多个方面。这份“linux程序员面试题”资源很可能包含这些关键领域的精选问题及解答,对于准备Linux相关岗位面试...
【Linux C语言面试题】是针对C语言编程和Linux系统操作的面试准备资料,涵盖了从基础知识到高级概念的多个方面。这些题目旨在测试面试者对C语言的理解深度、编程技巧以及在Linux环境下解决问题的能力。以下是一些...
Linux进程的创建通常在用户态通过系统调用,如sys_fork、sys_vfork和sys_clone实现,而在内核态则由do_fork函数负责处理。线程创建在用户态是通过pthread_create实现,线程是轻量级进程,本质上和进程一样由task_...
《Linux/UNIX系统编程手册》是一本经典的教材,它深入浅出地介绍了Linux和UNIX操作系统下的编程接口。这本书涵盖了各种核心API,包括文件操作、进程管理、网络通信、信号处理等,是学习系统级编程的重要参考资料。...
- 进程管理:理解进程的生命周期,掌握fork()、exec()、wait()等系统调用。 - 网络基础:了解TCP/IP协议栈,理解socket编程,熟悉netstat、ping等网络命令。 - 系统管理:掌握用户和组管理,磁盘分区与挂载,软件...
6. **系统调用**:在Linux下,通过系统调用接口(如syscalls.h头文件)可以访问操作系统提供的功能,如fork、exec、waitpid用于进程管理,open、read、write、close等用于文件操作,pipe、socket、send、recv用于...
- 系统调用,如`fork()`、`exec()`和`waitpid()`,用于进程间的通信和控制。 - 掌握信号处理,了解`signal()`和`sigaction()`函数。 - 链表、树等数据结构的实现及其在Linux编程中的应用。 - 熟悉Linux系统调用接口...
1. **Linux系统调用**:Linux提供了丰富的系统调用接口,如open、read、write、fork、exec等,它们是用户空间程序与内核交互的主要方式。 2. **头文件和预处理指令**:在Linux下,如`<unistd.h>`、`<sys/types.h>`...
在Linux操作系统中,C语言的`fork()`函数是创建新进程的关键系统调用。它允许一个正在运行的进程(父进程)创建一个与其完全相同的副本,即子进程。`fork()`函数的特性在于,一旦调用成功,它将返回两次:一次在父...
Linux 驱动面试题 ...Linux 下的 Socket 套接字和 Windows 下的 WinSock 都具有以下共同点: * 都使用 Berkeley套接字API * 都支持 TCP/IP 协议 在 C/C++ 语言开发中,可以使用 Socket 编程来实现网络通信。
- **知识点:** 在Linux中,所有硬件设备都被视为文件,通过特定路径下的文件来进行交互。这种方式极大地简化了与硬件的交互过程,使得开发人员和系统管理员能够更方便地管理和控制硬件资源。 2. **Linux内核引导...
根据提供的信息,我们可以总结出以下关于Linux Shell面试题的相关知识点: ### Linux Shell 面试题概述 #### 1. Linux Shell 基础概念 - **Shell 的定义**: Shell 是用户与操作系统之间的接口,是一种命令解释器。...
《深入理解Linux下C语言的fork()子进程函数》 在Linux操作系统中,C语言中的`fork()`函数是一个关键的系统调用,用于创建一个新的进程,也就是子进程。这个新进程与父进程几乎完全相同,它们共享相同的内存空间,但...