1. fork()函数         当程序调用fork()函数并返回成功之后,程序就将变成两个进程,调用fork()者为父进程,后来生成者为子进程。这两个进程将执行相同的程序文本,但却各自拥有不同的栈段、数据段以及堆栈拷贝。子进程的栈、数据以及栈段开始时是父进程内存相应各部分的完全拷贝,因此它们互不影响。
fork()函数在Linux中有两次返回,在父进程中返回子进程的pid,在子进程中返回0。
1 2 3 4 5 6 7 8 9 10 11 12 13 #include  <unistd.h>  int  main (void )     pid_t  pid;          pid = fork();     if (pid>0 ){         printf ("I'm a parent\n" );     }else  if (pid==0 ){         printf ("I'm a child\n" );     }else {         perror ("fork" );     } } 
输出
1 2 I'm a parent I'm a child 
2. 进程等待之wait() & waitpid() 
如果子进程已经退出,调用wait/waitpid会立即返回,并且释放资源,获得子进程退出信息 
如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则父进程可能阻塞 
如果不存在该子进程,则立即出错返回 
子进程的退出是个异步事件(子进程可以在父进程运行的任何时刻终止) 
 
2.1 wait() 1 2 3 4 5 6 7 8 头文件:#include <sys/wait.h>          #include <sys/type.h>   原型 pid_t  wait (int  *status) 返回值:     成功:返回被等待进程(子进程)pid     失败:返回-1 参数:输出型参数,获取子进程退出状态,不关心则可以设置为NULL  
2.2 waitpid() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 头文件: #include <sys/type.h>           #include <sys/wait.h>   返回值:    (1 )当正常返回的时候waitpid返回收集到的子进程的进程ID     (2 )如果设置了选项WNOHANG,而调用中waitpid发现已经没       有已经可以退出的子进程可收集,则返回0     (3 )如果调用中出错,则返回-1 ,这时errno会被设置成相应的       值以指示错误所在 原型:    pid_t  waitpid (pid_t  pid,int  *status,int  options)      1.pid       pid =-1 ,等待任一个子进程,与wait等效      pid>0 ,等待其进程ID与pid相等的子进程     2. status         WIFEXITED (status) :    若为正常终止子进程返回的状态,则为                              真(此参数是查看进程是否是正常退出)        WEXITSTATUS(status):  若WEXITSTATUS非零,提取子进程退                              出码(查看进程的退出码)     3. options          WNOHANG:若pid指定的子进程没有结束,则waitpid()函数                   返回0 ,不予以等待,若正常结束,则返回该子进程的ID 
3. 进程替换:exec 函数族 所谓exec函数族,其实有六种以exec开头的函数,统称exec函数:execl、execlp、execle、execv、execvp、execve。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。将当前进程的.text、.data替换为所要加载的程序的.text、.data,然后让进程从新的.text第一条指令开始执行,但进程ID不变。
3.1 exec函数族一般规律: exec函数一旦调用成功即执行新的程序,不返回。只有失败才返回,错误值-1。所以通常我们直接在exec函数调用后直接调用perror()和exit(),无需if判断。
exec函数族名字很相近,使用起来也很相近,它们的一般规律如下:
l (list)                           命令行参数列表
p (path)                       搜素file时使用path变量
v (vector)                    使用命令行参数数组
e (environment)       使用环境变量数组,不使用进程原有的环境变量,设置新加载程序运行的环境变量
3.2 带p的exec函数 这类函数有:execlp,execvp
具体说明:表示第一个参数无需给出具体的路径,只需给出函数名即可,系统会在PATH环境变量中寻找所对应的程序,如果没找到的话返回-1。
1 int  execvp (const  char  *file, char  *const  argv[]) 
execvp()会从PATH 环境变量所指的目录中查找符合参数file 的文件名,找到后便执行该文件,然后将第二个参数argv传给该欲执行的文件。
4. 代码实现和结果 github链接:GitHub - Kakaluoto/MyShell 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 #include  <iostream>  #include  <vector>  #include  <cstring>  #include  <unistd.h>  #include  <sys/wait.h>  #define  cd_failed 0 #define  cd_success 1 using  namespace  std;vector<string> argv; vector<string> history_cmds (100 )  ;string cmd; char * current_path = nullptr ;void  argparse () void  change_directory () void  execute_cmd () int  main ()      current_path = getcwd (NULL , 0 );     while  (1 ) {                  printf ("myshell:%s$ " , current_path);         getline (cin, cmd);                  if  (strcmp (cmd.data (), "exit" ) == 0 ) {             delete  current_path;             return  0 ;         }         argparse ();         execute_cmd ();         argv.clear ();              } } void  argparse ()      string param;     for  (char  i:cmd + " " ) {         if  (i != ' ' ) {             param += i;         } else  {             argv.push_back (param);             param = "" ;             continue ;         }     } } int  change_directory (int  argc)      if  (argc == 2 ) {         if  (chdir (argv[1 ].data ()) == 0 ) {             current_path = getcwd (NULL , 0 );             if  (current_path != nullptr ) {                 return  cd_success;             } else  {                 cout << "No such file or directory!\n" ;                 return  cd_failed;             }         } else  {             cout << "No such file or directory!\n" ;             return  cd_failed;         }     } else  {         cout << "too many arguments!\n" ;         return  cd_failed;     } } void  execute_cmd ()      pid_t  pid;     int  argc = argv.size ();     char ** arguments = new  char * [argc];     for  (int  i = 0 ; i < argc; i++) {         arguments[i] = (char *) argv[i].data ();     }     if  (strcmp (arguments[0 ], "cd" ) == 0 ) {         change_directory (argc);     } else  {         switch  (pid = fork()) {                          case  -1 :                 cout << "Failed to create subprocess!\n" ;                 return ;                              case  0 :                 execvp (arguments[0 ], arguments);                                  cout << "invalid input command : \""  << arguments[0 ] << "\""  << endl;                 exit (1 );             default : {                 int  status;                 waitpid (pid, &status, 0 );                 int  err = WEXITSTATUS (status);                  if  (err)cout << "Error: "  << strerror (err) << endl;             }         }     } } 
进入MyShell可执行文件所在目录执行如下命令即可
得到输出如下
1 myshell:/home/hy/myCppProject/cmake_demo/myshell$  
执行ls
1 2 myshell:/home/hy/myCppProject/cmake_demo/myshell$ ls MyShell  MyShell.cpp  readme.md 
执行ls -l:
1 2 3 4 5 6 myshell:/home/hy/myCppProject/cmake_demo/myshell$ ls  -l total 176 -rwxrwxr-x 1 hy hy 159632 12月 18 23:51 MyShell -rw-rw-r-- 1 hy hy   3020 12月 19 16:08 MyShell.cpp -rw-rw-r-- 1 hy hy  13420 12月  9 22:43 readme.md 
执行ps
1 2 3 4 5 myshell:/home/hy/myCppProject/cmake_demo/myshell$ ps     PID TTY          TIME CMD  135724 pts/0    00:00:00 bash  135766 pts/0    00:00:00 MyShell  135847 pts/0    00:00:00 ps 
执行cd:
1 2 myshell:/home/hy/myCppProject/cmake_demo/myshell$ cd  / myshell:/$  
执行pwd和ls命令
1 2 3 4 5 myshell:/$ pwd  / myshell:/$ ls  bin   cdrom  etc   lib	  lib64   media  opt   root  sbin  srv	     sys  usr boot  dev    home  lib32  libx32  mnt	 proc  run   snap  swapfile  tmp  var