1.替换原理
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行例外一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动历程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未变化
2.实现一个shell
#include<stdio.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>int main()
{char buf[1024] = { 0 };while(1){printf("mysell#");fflush(stdout);size_t s = read( 0, buf, sizeof( buf ) );if(s > 0){buf[s - 1] = '\0';printf("%s\n", buf);}char* start = buf;char* _argv[32];_argv[0] = buf;int i = 1;while(*start){if(*start == ' '){*start = '\0';start ++;_argv[i++] = start;}else{start ++;}}_argv[i] = NULL;pid_t pid;pid = fork();if(pid < 0){perror("fork");exit(1);}if(pid == 0){execvp(_argv[0], _argv);}else if(pid > 0){int st = 0;wait(&st);}}return 0;
}
3.system(),popen(),fork()三个函数的qubie
(1)system()函数
#include <stdlib.h>
int system(const char *command);
1)system在执行过程中经理fork -> exec -> wait ,但system在执行过程中会一直等待,直到shell命令执行完后才退出,即system为串行执行
2)system在执行过程中对SIGCHLD、SIGINT、SIGQUIT都做了处理
3)SIGCHLD是子进程退出的时候发给父进程的一个信号,system()中为什么要屏蔽SIGCHLD信号?就是为了system()调用能够及时的退出并且能够正确的获取子进程的退出状态(成功回收子进程)。
(2)popen函数
#include <stdio.h>
FILE *popen(const char *command, const char *type);
1)popen函数在执行时无须等待shell执行完才退出,即popen是并行执行
2)popen在执行时对SIGCHLD、SIGINT、SIGQUIT信号不做如何处理,
3)popen创建的子进程如果不执行pclose,popen创建的子进程就会成为僵尸进程
4)popen() 函数用创建管道的方式启动一个 进程, 并调用 shell. 因为管道是被定义成单向的, 所以 type 参数只能定义成只读或者只写, 不能是两者同时, 结果流也相应的是只读或者只写. command 参数是一个字符串指针, 指向的是一个以 null 结束符结尾的字符串, 这个字符串包含一个 shell 命令. 这个命令被送到 /bin/sh 以 -c 参数执行, 即由 shell 来执行. type 参数也是一个指向以 null 结束符结尾的字符串的指针, 这个字符串必须是 ‘r‘ 或者 ‘w’ 来指明是读还是写。
5)popen没有屏蔽SIGCHLD,主要的原因就是popen是”并行”的。如果我们在调用popen的时候屏蔽了SIGCHLD,那么如果在调用popen和pclose之间调用进程又创建了其它的子进程并且调用进程注册了SIGCHLD信号处理句柄来处理子进程的回收工作(waitpid)那么这个回收工作会一直阻塞到pclose调用。这也意味着如果调用进程在pclose之前执行了一个wait()操作的话就可能获取到popen创建的子进程的状态,这样在调用pclose的时候就会回收(waitpid)子进程失败,返回-1,同时设置errno为ECHLD,标示pclose无法获取子进程状态。
(3)fork函数
fork用来创建一个子进程.
1)系统让新的进程与旧的进程使用同一个代码段,因为它们的程序还是相同的,对于数据段和堆栈段,系统则复制一份给新的进程,这样,父进程的所有数据都可以留给子进程
2)子进程一旦开始运行,虽然它继承了父进程的一切数据,但实际上数据却已经分开,相互之间不再有影响了,也就是说,它们之间不再共享任何数据了。而如果两个进程要共享什么数据的话,就要使用另一套函数(shmget,shmat,shmdt等)来操作。现在,已经是两个进程了,对于父进程,fork函数返回了子程序的进程号,而对于子程序,fork函数则返回零,这样,对于程序,只要判断fork函数的返回值,就知道自己是处于父进程还是子进程中