LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 1097|回复: 8

关于exec的问题,恳请各位高手解惑!

[复制链接]
发表于 2005-12-26 19:27:56 | 显示全部楼层 |阅读模式
程序目的:
实现一个shell程序,可以实现:
1.重定向;
2.管道;

问题:在实现功能2(由函数ispipe实现)的时候,

a:如果使用"尝试1"中的代码,则可以实现ls -al /usr/include | more之类的功能,
但是程序运行完此命令后会退出!并非我本意!
b:如果使用"尝试2"中的代码,在键入ls -al /usr/include | more之后,感觉子进程2(pid1 == 0)
对于键盘输入响应有些问题,实在不好描述,请运行实验一下;但是如果在调用fork的进程中
加入sleep(5)之后就没有问题了,但这又不是我的本意(因为这需要在5秒之内"more"完)!


恳请高手解惑!不胜感激!

另外,如何贴代码讷呢?找了半天没找到....校园网,太慢了

代码如下:

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <sys/wait.h>
  6. #include <sys/stat.h>
  7. #include <fcntl.h>
  8. #include <sys/types.h>

  9. char buf[128],cmd1[10],cmd2[10],file[20];
  10. char *var1[20],*var2[20];
  11. int flag,flagcmd1,flagcmd2;

  12. int getcmd()/*get the command and the varialble*/
  13. {
  14.         int i,j;
  15.         i = j = 0;
  16.         char *p = NULL;
  17.                 var1[0] = var2[0] = NULL;
  18.                 cmd1[0] = cmd2[0] = 0;
  19.                 printf(">");
  20.                 gets(buf);
  21.                 i = j = 0;
  22.                 flag = flagcmd1 = flagcmd2 = 0;
  23.        
  24.                 p = strtok(buf," ");
  25.                 while(p != NULL){
  26.                         if(strcmp(p,"|") == 0){
  27.                                 flag = 1;
  28.                                 p = strtok(NULL," ");
  29.                                 continue;
  30.                         }
  31.                         if(strcmp(p,"<") == 0){
  32.                                 flag = 2;
  33.                                 p = strtok(NULL," ");
  34.                                 continue;
  35.                         }
  36.                         if(strcmp(p,">") == 0){
  37.                                 flag = 3;
  38.                                 p = strtok(NULL," ");
  39.                                 continue;
  40.                         }
  41.                        
  42.                         if(flag == 0){       
  43.                                 if(flagcmd1 == 0){
  44.                                         strcpy(cmd1,p);
  45.                                         flagcmd1 = 1;
  46.                                 } else {
  47.                                              var1[i++] = p;
  48.                                         p = strtok(NULL," ");
  49.                                 }
  50.                         }else if (flag == 1){
  51.                                 if(flagcmd2 == 0){
  52.                                         strcpy(cmd2,p);
  53.                                         flagcmd2 = 1;
  54.                                 } else {
  55.                                              var2[j++] = p;
  56.                                         p = strtok(NULL," ");
  57.                                 }
  58.                         }else if(flag == 2){
  59.                                 strcpy(file,p);
  60.                                 p = strtok(NULL," ");
  61.                         }else if(flag == 3){
  62.                                 strcpy(file,p);               
  63.                                 p = strtok(NULL," ");
  64.                         }
  65.                        
  66.                 }       
  67.         var1[i] = NULL;
  68.         var2[j] = NULL;
  69.         printf("The command 1 is %s\n",cmd1);       
  70.         printf("The command 2 is %s\n",cmd2);       
  71.         return flag;       
  72. }
  73. void onecmd()
  74. {
  75.         pid_t pid;
  76.         if((pid = fork()) < 0){
  77.                 printf("fork error\n");
  78.                 exit(0);
  79.         } else if(pid == 0){        /*child*/
  80.                 execvp(cmd1,var1);
  81.                 printf("wrong command.\n");
  82.         }else if(pid > 0)        /*parent*/
  83.                 wait(NULL);
  84.        
  85. }

  86. void onecmdin(int fd[2])
  87. {
  88.         pid_t pid;
  89.         int n,m;
  90.         char ch;
  91.         int  fp;
  92.         if((fp =open(file,O_RDONLY)) == -1){
  93.                 printf("can't open %s\n",file);
  94.                 exit(0);
  95.         }
  96.         if(pipe(fd) < 0){
  97.                 printf("pipe error\n");
  98.                 exit(0);
  99.         }
  100.         if((pid = fork()) < 0){
  101.                 printf("fork error\n");
  102.                 exit(0);
  103.         } else if(pid > 0){        /*parent*/
  104.                 close(fd[0]);
  105.                 while(read(fp,&ch,1) != 0 )
  106.                          write(fd[1],&ch,1);
  107.                 close(fd[1]);
  108.                 if(waitpid(pid,NULL,0) < 0)
  109.                         printf("waitpid error\n");
  110.         }else if(pid == 0){        /*child*/
  111.                 printf("in.....");
  112.                 close(fd[1]);
  113.                 if(fd[0] != STDIN_FILENO){
  114.                         if(dup2(fd[0],STDIN_FILENO) != STDIN_FILENO){
  115.                                 printf("dup2 error to stdin\n");
  116.                                 exit(0);
  117.                         }
  118.                 }
  119.                 printf("in.....");
  120.                 execvp(cmd1,var1);
  121.                 printf("wrong command.\n");
  122.         }
  123.                        
  124. }
  125. void onecmdout(int fd[2]){
  126.         pid_t pid;
  127.         int n,m;
  128.         char line[1024];
  129.         char ch;
  130.         int  fp;
  131.         if((fp = open(file,O_WRONLY)) == -1){
  132.                 printf("can't open %s\n",file);
  133.                 exit(0);
  134.         }
  135.         if(pipe(fd) < 0){
  136.                 printf("pipe error\n");
  137.                 exit(0);
  138.         }
  139.         if((pid = fork()) < 0){
  140.                 printf("fork error\n");
  141.                 exit(0);
  142.         } else if(pid > 0){        /*parent*/
  143.                 close(fd[1]);
  144.                 while((m = read(fd[0],&ch,1)) != 0){
  145.                         write(fp,&ch,1);
  146.                 }
  147.                
  148.         }else if(pid == 0){        /*child*/
  149.                 close(fd[0]);
  150.                 if(fd[1] != STDOUT_FILENO){
  151.                         if(dup2(fd[1],STDOUT_FILENO) != STDOUT_FILENO){
  152.                                 printf("dup2 error to stdin\n");
  153.                                 exit(0);
  154.                         }
  155.                 execvp(cmd1,var1);
  156.                 printf("wrong command.\n");
  157.                 }
  158.         }
  159.        
  160. }

  161. void ispipe(int fd[2])
  162. {
  163.         pid_t pid,pid1;
  164.          if(pipe(fd) < 0)
  165.                  printf("pipe error\n");
  166.        
  167.          /*deal with the  command before '|'*/
  168.                
  169.        
  170.          if(0){//尝试1
  171.                   if((pid = fork()) < 0)
  172.                          printf("fork error");
  173.                  if(pid > 0){
  174.                         close(fd[0]);         //close read end
  175.                         dup2(fd[1],STDOUT_FILENO);
  176.                         execvp(cmd1,var1);
  177.                  } else if (pid == 0){
  178.                         close(fd[1]);        //close write end
  179.                         dup2(fd[0],STDIN_FILENO);
  180.                         execvp(cmd2,var2);
  181.                  
  182.                  }
  183.         }
  184.         if(1){ 尝试2
  185.                 if((pid = fork()) < 0)
  186.                          printf("fork error");
  187.                 if(pid == 0) {        /*child1*/
  188.                         close(fd[0]);         /*close read end*/
  189.                         dup2(fd[1],STDOUT_FILENO);
  190.                         close(fd[1]);//aaaaaa
  191.                         execvp(cmd1,var1);
  192.                 }
  193.        
  194.         /*deal with the  command after '|'*/
  195.        
  196.                  if((pid1 = fork()) < 0)
  197.                          printf("fork error");
  198.                  if(pid1 == 0){        /*child2*/
  199.                         close(fd[1]);        /*close write end*/
  200.                         dup2(fd[0],STDIN_FILENO);
  201.                         close(fd[0]);
  202.                         execvp(cmd2,var2);
  203.                  }

  204.                  //sleep(5);
  205.                  
  206.         }
  207.        
  208. }
  209. int main(int argc,char *argv[])
  210. {       
  211.         int fd[2];
  212.         int filed;
  213.         system("clear");
  214.         for( ; ; ){
  215.                 getcmd();
  216.                 if(flag == 0)                /*There is no pipe*/
  217.                         onecmd();
  218.                  else if(flag == 1)        /*There is a pipe*/
  219.                         ispipe(fd);
  220.                  else if(flag == 2)        /*There is a redirect '<'*/
  221.                         onecmdin(fd);
  222.                  else if(flag == 3)     /*There is a redirect '>'*/
  223.                         onecmdout(fd);
  224.         }
  225. }
复制代码
发表于 2005-12-27 13:17:51 | 显示全部楼层
楼主电子科大的么?我们是不是在群里见过?
回复 支持 反对

使用道具 举报

发表于 2005-12-27 13:41:38 | 显示全部楼层
尝试2那段大概改成这个样子:

  1.      if(1){// 尝试2
  2.              if((pid = fork()) < 0)
  3.               printf("fork error");
  4.          if(pid == 0) {  /*child1*/
  5.              close(fd[0]);   /*close read end*/
  6.              dup2(fd[1],STDOUT_FILENO);
  7.              execvp(cmd1,var1);
  8.          }

  9.          waitpid(pid,NULL,0);
  10.          close(fd[1]);//aaaaaa
  11.      /*deal with the  command after '|'*/

  12.           if((pid1 = fork()) < 0)
  13.               printf("fork error");
  14.           if(pid1 == 0){ /*child2*/
  15.              close(fd[1]);   /*close write end*/
  16.              dup2(fd[0],STDIN_FILENO);
  17.              close(fd[0]);
  18.              execvp(cmd2,var2);
  19.           }

  20.           waitpid(pid1,NULL,0);
  21.           //sleep(5);
  22.      }
复制代码


另外:

  1. BUGS
  2.        Never use gets().  Because it is impossible to tell without knowing the
  3.        data in advance how many  characters  gets()  will  read,  and  because
  4.        gets() will continue to store characters past the end of the buffer, it
  5.        is extremely dangerous to use.  It has  been  used  to  break  computer
  6.        security.  Use fgets() instead.
复制代码
回复 支持 反对

使用道具 举报

发表于 2005-12-27 13:46:07 | 显示全部楼层
对了,你的重定向实现有问题
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-12-27 19:04:09 | 显示全部楼层
to 2 楼:  呵呵,就是.


to 版主:
1.按照你的代码实验了一下,的确可以了.不过好像还有点小问题(该问题在我原来的代码中也存在):执行完ls -al /usr/include | more 之后 ls这个进程并没有退出(在root 下ps -ef | grep xxx),好像要等到下次命令执行完之后才会退出,为什么?(我原来的代码是more 这个进程永远不会退出)

2.请问版主,这样改动的原因是什么(这个问题才是我最想知道的,还请不吝赐教啊)?
为什么要waitpid(pid1,NULL,0)两次?
回复 支持 反对

使用道具 举报

发表于 2005-12-27 19:48:18 | 显示全部楼层
Post by lazycat_2005
:
1.按照你的代码实验了一下,的确可以了.不过好像还有点小问题(该问题在我原来的代码中也存在):执行完ls -al /usr/include | more 之后 ls这个进程并没有退出(在root 下ps -ef | grep xxx),好像要等到下次命令执行完之后才会退出,为什么?(我原来的代码是more 这个进程永远不会退出)

sorry,这是我的错.不应该wait两次,只需要后面一次就可以了. 就是因为wait两次,导致了ls不能结束,猜测可能是缓冲区大小的问题. /usr/include下的文件太多,加之使用-al选项,所以数据量比较大,估计管道无法接收这么多数据,所以就挂在那了.而主线程需要等他结束,他又结束不了.....

2.请问版主,这样改动的原因是什么(这个问题才是我最想知道的,还请不吝赐教啊)?
为什么要waitpid(pid1,NULL,0)两次?

上面已经说了,我搞错了.只要把
  1. close(fd[1]);   /*close write end*/
复制代码
移出就行了.因为第一个程序(ls)就是向这个管道写,所以不能关闭
至于为什么要wait,跟你使用的sleep的功能其实是一样的,防止错误输出.因为可能more进程还没结束,主进程就已经输出提示符了.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-12-27 20:41:30 | 显示全部楼层
多谢版主的回复,真是雪中送炭啊,我再好好研究一下~~~~~~~~~
另:
Post by rickxbx
对了,你的重定向实现有问题


是啊,开始没注意,实现 '>' 的时候要先把文件截短为0,否则上次的数据没有被清空.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-12-27 22:11:14 | 显示全部楼层
to rickxbx:

又仔细研究了一下,即便按照版主的方法做了,仍然存在一点点问题:第一条指令就运行
ls -al /usr/include | more,然后使用ps -efl | grep xxx查看,可以看到,ls已经成为了僵尸进程(说明只有
一条waitpid(pid1,NULL,0)会导致主进程不能对第一个子进程进行善后处理).
改为下面的代码就没有问题了(还是加一条waitpid,不过是加在waitpid(pid1,NULL,0)的后面).

猜想问题原因应该是如果waitpid如果加在fork第二个子进程之前,则写管道操作可能没有完成或者象版主所说有关缓冲区方面问题,所以在此等待则大家都挂起.如果不加,则父进程没能对第一个进程进行善后处理,第一个进程成为僵尸进程.

if(1){// 尝试2
             if((pid = fork()) < 0)
              printf("fork error");
         if(pid == 0) {  /*child1*/
             close(fd[0]);   /*close read end*/
             dup2(fd[1],STDOUT_FILENO);
             execvp(cmd1,var1);
         }

         
         close(fd[1]);//aaaaaa
     /*deal with the  command after '|'*/

          if((pid1 = fork()) < 0)
              printf("fork error");
          if(pid1 == 0){ /*child2*/
             close(fd[1]);   /*close write end*/
             dup2(fd[0],STDIN_FILENO);
             close(fd[0]);
             execvp(cmd2,var2);
          }

          waitpid(pid1,NULL,0);
          waitpid(pid,NULL,0);
         
     }
回复 支持 反对

使用道具 举报

发表于 2005-12-28 10:09:07 | 显示全部楼层
多谢兄弟提醒. 本来想那个第一个进程让init来接收就是了,看来还不大好,因为如果shell一直不退出,这样的进程可能会很多
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表