fork,wait和exec

2022-11-13 09:15:04 浏览数 (1)

fork,wait和exec

  • fork系统调用
  • wait系统调用
  • exec系统调用
  • 为什么要把fork和exec分开

fork系统调用

1、子进程不会从 main()函数开始执行,而是直接从 fork()系统调用返回。

2、子进程拥有自己的地址空间(即拥有自己的私有内存)、寄存器、程序计数器等

3、父进程获得的返回值是新创建子进程的 PID,而子进程获得的返回值是 0

4、子进程和父进程的运行顺序取决于CPU调度顺序

代码实现

代码语言:javascript复制
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>

 int main(int argc, char *argv[])
 {
   printf("hello world (pid:%d)n", (int) getpid());
   
   int rc = fork();
    
    if (rc < 0) { // fork failed; exit
    fprintf(stderr, "fork failedn");
    exit(1);
  
  } else if (rc == 0) { // child (new process)
   printf("hello, I am child (pid:%d)n", (int) getpid());
  
  } else { // parent goes down this path (main)
   printf("hello, I am parent of %d (pid:%d)n",
   rc, (int) getpid());
  
  }
  
  return 0;
 }

wait系统调用

父进程调用 wait(),延迟自己的执行,直到子进程执行完毕。当子进程结束,wait()才返回父进程。

代码实现

代码语言:javascript复制
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
 {
 printf("hello world (pid:%d)n", (int) getpid());
 int rc = fork();
 if (rc < 0) { // fork failed; exit
 fprintf(stderr, "fork failedn");
 exit(1);
 } else if (rc == 0) { // child (new process)
 printf("hello, I am child (pid:%d)n", (int) getpid());
 } else { // parent goes down this path (main)
 int wc = wait(NULL);
 printf("hello, I am parent of %d (wc:%d) (pid:%d)n",
 rc, wc, (int) getpid());
 }
 return 0;
 }

exec系统调用

这个系统调用可以让子进程执行与父进程不同的程序。例如,在p2.c 中调用 fork(),这只是在你想运行相同程序的拷贝谁有用。但是,我们常常想运行不同的程序,exec()正好做这样的事

这个例子中,子进程调用 execvp()来运行字符计数程序 wc。

exec()会从可执行程序中加载代码和静态数据,并用它覆写自己代码段(以及静态数据),堆、栈及其他内存空间也会被重新初始化。然后操作系统就执行该程序,将参数通过 argv 传递给该进程。因此,它并没有创建新进程,而是直接将当前运行的程序(以前的 p3)替换为不同的运行程序(wc)。子进程执行 exec()之后,几乎就像p3.c 从未运行过一样。对 exec()的成功调用永远不会返回

代码语言:javascript复制
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
 #include <sys/wait.h>

 int
 main(int argc, char *argv[])
 {
 printf("hello world (pid:%d)n", (int) getpid());
 int rc = fork();
 if (rc < 0) { // fork failed; exit
 fprintf(stderr, "fork failedn");
 exit(1);
 } else if (rc == 0) { // child (new process)
 printf("hello, I am child (pid:%d)n", (int) getpid());
 char *myargs[3];
 myargs[0] = strdup("wc"); // program: "wc" (word count)
 myargs[1] = strdup("p3.c"); // argument: file to count
 myargs[2] = NULL; // marks end of array
 execvp(myargs[0], myargs); // runs word count
 printf("this shouldn't print out");
 } else { // parent goes down this path (main)
 int wc = wait(NULL);
 printf("hello, I am parent of %d (wc:%d) (pid:%d)n",
 rc, wc, (int) getpid());
 }
 return 0;
 }

为什么要把fork和exec分开

在构建UNIX shell 的时候非常有用,因为这给了shell 在fork 之后exec 之前运行代码的机会,这些代码可以在运行新程序前改变环境,实现一些有趣的功能。

例: prompt> wc p3.c > newfile.txt

在上面的例子中,wc 的输出结果被重定向(redirect)到文件newfile.txt 中(通过newfile.txt之前的大于号来指明重我向)。

shell 实现结果重定向的方式也很简单,当完成子进程的创建后,shell在调用exec()之前先关闭了标准输出(standardoutput),打开了文件newfile.txt。

这样,即将运行的程序wc 的输出结果就被发送到该文件,而不是打印在屏幕上。

0 人点赞