APUE学习过程中遇到的几个问题

软件和网站开发以及相关技术探讨
回复
Korz
帖子: 3
注册时间: 2013-04-16 23:57
系统: ubuntu 16.04

APUE学习过程中遇到的几个问题

#1

帖子 Korz » 2016-05-09 22:26

第一个问题是在APUE第三版中,例程8-8。书上说的结果是
second child, parent pid = 1
而我得到的结果是
second child, parent pid = 1996

代码: 全选

#include "apue.h"
#include <sys/wait.h>

int main(void)
{
        pid_t        pid;
        if ((pid = fork()) < 0) {                /* first child */
                err_sys("fork error");
        } else if (pid == 0) {
                if ((pid = fork()) < 0)
                        err_sys("fork error");
                else if (pid > 0)
                        exit(0);        /* parent from second fork == first child */

                /*
                 * We're the second child; our parent becomes init as soon
                 * as our real parent calls exit() in the statment above.
                 * Here's where we'd continue executing, knowing that when
                 * we're done, init will reap our status.
                 */
                sleep(2);   
                printf("second child, parent pid = %ld\n", (long)getppid());
                exit(0);
        }

        if (waitpid(pid, NULL, 0) != pid)        /* wait for first child */
                err_sys("waitpid error");

        /* We're the parent (the original process); we continue executing,
         * knowing that we're not parent of the second child.
         */
        exit(0);
}
第二个问题是在知乎上看到的,现在还没人回答,所以在这里顺道问一下。
在非main函数调用vfork, 却产生了2个子进程,请问这是为何?(APUE 习题8.2)

代码: 全选

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

static void f1(void), f2(void);

int main(void) {
  f1();
  f2();
  _exit(0);
}

static void f1() {
  pid_t pid;
  if ((pid = vfork()) < 0) {
    printf("vfork error.\n");
    _exit(-1);
  }
  printf("pid : %ld, getpid() : %ld, getppid() : %ld.\n",
    (long)pid, (long)getpid(), (long)getppid());
}

static void f2() {
  char buf[1000] ;
  int i;

  for (i = 0; i<sizeof(buf); i++)
    buf[i] = 0;
}

/**
  输出: 
  pid : 0, getpid() : 7720, getppid() : 7719.
  pid : 7720, getpid() : 7719, getppid() : 4364.
  pid : 0, getpid() : 7721, getppid() : 7719.
  Segmentation fault
*/
在我的环境里,会产生两个子进程。但是只调用了一此vfork啊,想不太明白。

刚学习APUE没多久,很多困惑,希望大家给小弟解惑。
谢谢大家。
rosynirvana
帖子: 893
注册时间: 2011-02-14 17:46

Re: APUE学习过程中遇到的几个问题

#2

帖子 rosynirvana » 2016-05-09 22:46

第一个应该是因为调度的关系,parent还没退出,把sleep的时间改长一点试试

第二个,引自手册中vfork部分

(From POSIX.1) The vfork() function has the same effect as fork(2),
except that the behavior is undefined if the process created by vfork()
either modifies any data other than a variable of type pid_t used to
store the return value from vfork(), or returns from the function in
which vfork() was called, or calls any other function before success‐
fully calling _exit(2) or one of the exec(3) family of functions.
头像
astolia
论坛版主
帖子: 6435
注册时间: 2008-09-18 13:11

Re: APUE学习过程中遇到的几个问题

#3

帖子 astolia » 2016-05-10 16:55

第一个并不是像楼上所说的是什么调度的问题。你可以去看一下它输出的pid对应的进程,的确是个init进程,不过这个init的pid不为1。这是近年来采用的systemd搞出来的针对用户进程的init。如果你用其他的init system,就可以达到和书上一样的效果了

第二个涉及到底层原理。如果你知道在CPU指令级别是如何实现函数调用的,那就很容易理解了。这需要汇编语言的知识。
vfork产生的子进程会跟父进程共享内存资源,包括了栈空间。而函数调用发生时的情况,会先将当前执行到的地址A保存到栈空间,然后再跳转到函数所在的地址B继续执行。当函数返回时,从栈空间中取出保存的地址A,再跳转到地址A继续执行。由于vfork保证子进程先执行,而子进程又修改了栈空间中的数据,改变了跳转的地址A,所以当父进程从函数f1返回时,返回到的并非原来的A,而是其他位置。根据修改后的数据不同,跳转位置也不同,实际执行效果也不一样。所以楼上引用的内容会说是behavior is undefined
实际上即使不去执行f2(),也有可能达到相同的效果。
Korz
帖子: 3
注册时间: 2013-04-16 23:57
系统: ubuntu 16.04

Re: APUE学习过程中遇到的几个问题

#4

帖子 Korz » 2016-05-10 21:45

astolia 写了:第一个并不是像楼上所说的是什么调度的问题。你可以去看一下它输出的pid对应的进程,的确是个init进程,不过这个init的pid不为1。这是近年来采用的systemd搞出来的针对用户进程的init。如果你用其他的init system,就可以达到和书上一样的效果了

第二个涉及到底层原理。如果你知道在CPU指令级别是如何实现函数调用的,那就很容易理解了。这需要汇编语言的知识。
vfork产生的子进程会跟父进程共享内存资源,包括了栈空间。而函数调用发生时的情况,会先将当前执行到的地址A保存到栈空间,然后再跳转到函数所在的地址B继续执行。当函数返回时,从栈空间中取出保存的地址A,再跳转到地址A继续执行。由于vfork保证子进程先执行,而子进程又修改了栈空间中的数据,改变了跳转的地址A,所以当父进程从函数f1返回时,返回到的并非原来的A,而是其他位置。根据修改后的数据不同,跳转位置也不同,实际执行效果也不一样。所以楼上引用的内容会说是behavior is undefined
实际上即使不去执行f2(),也有可能达到相同的效果。
我反汇编了,不过看不懂。看来我的姿势水平还是要提高啊。
头像
astolia
论坛版主
帖子: 6435
注册时间: 2008-09-18 13:11

Re: APUE学习过程中遇到的几个问题

#5

帖子 astolia » 2016-05-10 22:45

额,上面说错了一点,init --user不是systemd的,是upstart搞的。现在默认还是systemd和upstart混用,所以会有这个问题。如果把upstart彻底删掉只用纯的systemd,就只有一个pid为1的init进程了
回复