• 详解Linux的系统调用fork()函数


    Linux系统中,fork()是一个非常重要的系统调用,它的作用是创建一个新的进程。具体来说,fork()函数会在当前进程的地址空间中复制一份子进程,并且这个子进程几乎完全与父进程相同,包括进程代码、数据、堆栈以及打开的文件描述符等。因此,父进程和子进程之间的关系可以看作是一个“克隆”关系。

    fork()函数的语法如下:

    1. #include
    2. pid_t fork(void);

    其中,参数pid_t代表进程id,而fork()函数返回值则有以下两种情况:

    • 如果返回0,表示当前进程是子进程。
    • 如果返回一个正整数,表示当前进程是父进程,并且返回的整数就是新创建出来的子进程的进程ID。

    此外,如果fork()返回值为-1,表示创建子进程失败。

    fork()函数的本质是在内核中创建一个新的进程控制块(PCB),然后将原来进程的PCB中的大部分内容都复制到新的PCB中去,然后让两个进程同时运行。由于新的进程是从原来的进程所复制而来的,因此新进程会继承原来进程的所有资源和信息,包括内存、文件描述符、信号处理方式等。

    需要注意的是,fork()函数并不保证父进程和子进程的执行顺序。在fork()之后,操作系统可能会先执行父进程,也可能会先执行子进程,这完全取决于系统的调度算法。

    一般情况下,父进程和子进程之间是相互独立的,它们各自运行各自的代码,共享的只有一部分内存空间,而其他资源则是分别使用的。

    1. #include
    2. #include
    3. int i=5;
    4. int main() {
    5. if(fork()!=0)
    6. i++;
    7. else
    8. printf("%d\n",i);
    9. }

    此外,fork()函数还可以通过返回值来区分父进程和子进程,这使得父进程可以管理子进程的行为,例如等待子进程结束、获取子进程的状态等。

    1. #include
    2. #include
    3. int main() {
    4. for(int i=0;i<3;i++){
    5. printf("%d\n",fork());
    6. }
    7. }

    需要注意的是,fork函数会返回两次,一次是在父进程中返回子进程的进程ID,一次是在子进程中返回0。

    以下是七个fork例子

    ① Call once, return twice

    1. void fork0() {
    2. if (fork() == 0) {
    3. printf("Hello from child\n");
    4. } else {
    5. printf("Hello from parent\n");
    6. }
    7. }

    创建一个子进程打印hello from child,父进程打印hello from parent

    ② Parent and child both run same code

    1. void fork1() {
    2. int x = 1;
    3. pid_t pid = fork();
    4. if (pid == 0) {
    5. printf("Child has x = %d\n", ++x);
    6. } else {
    7. printf("Parent has x = %d\n", --x);
    8. }
    9. printf("Bye from process %d with x = %d\n", getpid(), x);
    10. }

    子进程会输出child has x=2和bye from process 子进程ID with x=2,父进程会输出parent has x=0和bye from process 父进程ID with x=0

    ③ Parent and child can continue forking

    1. void fork2() {
    2. printf("L0\n");
    3. fork();
    4. printf("L1\n");
    5. fork();
    6. printf("Bye\n");
    7. }

    父进程输出一个L0、一个L1和一个Bye,一个子进程输出一个L1和一个Bye,一个子进程输出一个Bye,两个孙子进程输出两个Bye,一共一个L0、两个L1和四个Bye

    ④ Parent and child can continue forking

    1. #define bork fork
    2. void borkfork() {
    3. bork();
    4. bork();
    5. bork();
    6. printf("borked\n");
    7. }

    父进程创建了一个子进程,然后二者又创建了两个子进程,然后四者又创建了四个子进程共八个进程输出八个borkfork

    ⑤ Parent and child can continue forking

    1. void fork3() {
    2. printf("L0\n");
    3. fork();
    4. printf("L1\n");
    5. fork();
    6. printf("L2\n");
    7. fork();
    8. printf("Bye\n");
    9. }

    由③和④可知将会输出一个L0、两个L1、四个L2和八个Bye

    ⑥ Nested forks in parents

    1. void fork4() {
    2. printf("L0\n");
    3. if (fork() != 0) {
    4. printf("L1\n");
    5. if (fork() != 0) {
    6. printf("L2\n");
    7. fork();
    8. }
    9. }
    10. printf("Bye\n");
    11. }

    由于只有在父进程中fork的返回值才会是进程ID,而子进程中fork的返回值永远是0,所以只有父进程会打印除L0、L1和L2并创建三个子进程,四个进程再打印出四个Bye

    ⑦ Nested forks in children

    1. void fork5() {
    2. printf("L0\n");
    3. if (fork() == 0) {
    4. printf("L1\n");
    5. if (fork() == 0) {
    6. printf("L2\n");
    7. fork();
    8. }
    9. }
    10. printf("Bye\n");
    11. }

    父进程打印出L0,子进程打印出L1,子进程创建的子进程打印出L2并创建一个子进程,四个进程打印四个Bye

  • 相关阅读:
    python之模拟登录与表单交互
    cygwin编译wget过程记录
    windows开机自启动和忘记密码-备忘
    ESP32系列--第九篇 ADC的使用
    PLC数据采集网关是如何应用的?-天拓四方
    linux————zabbix搭建
    如何使用webgl(three.js)实现煤矿隧道、井下人员定位、掘进面、纵采面可视化解决方案——第十九课(一)
    OpenWrt kernel install分析(1)
    cocos 3.x 2D角色键盘移动
    C++ std::make_unique和std::make_shared用法
  • 原文地址:https://blog.csdn.net/weixin_62264287/article/details/133529869