连接到近端的进程:linux中的计算器bc

一、背景

    初次接触服务器与客户端的知识,先从最简单的做起:了解linux中使用的计算器。linux中使用的计算器是最为简单的服务器与客户端协同工作的例子。

二、从管道到服务器与客户端

    之前详细的解释过管道的工作原理,但传统的Unix管道只是单方向的传送数据。打个比方,在linux命令行下使用cat /etc/passwd | grep Jack命令。这个命令将cat的输出结果通过管道传送到grep命令的标准输入中。但这只是一个单向的数据传送,grep的输出结果并不会进行回传。而若有两个进程A、B,他们两个协同进行工作,A进程将其输出结果传送给B进程,B进程处理数据完毕后再返回给A进程。这样,它们之间并不是单向的管道,而是一个双向的管道。B对A提供服务,而A是B的客户。A就叫做客户端,B叫做服务器。

三、bc与dc

    打开我们的linux命令行终端,输入bc命令调用linux的计算器,写入2+2。使用enter键则会返回结果。

    这时候大家可能会觉得我们向bc进程输入了数据,bc进行运算后将结果返回给了屏幕终端。而实际上并非如此。

    实际上bc只是一个用户的界面,让用户输入数值与给用户返回结果而已。这个可以使用man bc命令来查看。

    上面简介说bc是一个计算器语言。而看以下dc的手册。

    这里可以看到,dc是一个计算器。什么意思呢?

    其实是说,这里的bc只是一个界面,而dc才是真正数据进行计算的地方。bc将接收到的数据传送给dc进行运算,运算结束后,dc将结果返回给bc显示输出。

    那么,也就是说,bc其实一个客户端,而dc是一个服务器。

四、编写bc

        搞清楚了bc与dc之间的关系,那么,我们就可以使用现有的知识编写出自己的bc。当然,这里的bc指的是用户的交互界面。而后台的计算还是要交给dc。

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

#define oops(m,x) {perror(m);exit(x);}
void be_bc(int *,int *);
void be_dc(int *,int *);
void fatal(char *);

int main()
{
  int pid,todc[2],fromdc[2];

  if(pipe(todc) == -1 || pipe(fromdc) == -1)
      oops("pipe failed",1);
  if((pid = fork()) == -1)
      oops("fork failed",2);
  if(pid == 0)
      be_dc(todc,fromdc);
  else{
      be_bc(todc,fromdc);
      wait(NULL);
  }
}

void be_bc(int todc[2],int fromdc[2])
{
  int num1,num2;
  char operation[BUFSIZ],message[BUFSIZ],*fgets();
  FILE *fpout,*fpin,*fdopen();

  close(todc[0]);
  close(fromdc[1]);
  fpout = fdopen(todc[1],"w");
  fpin = fdopen(fromdc[0],"r");
  if(fpout == NULL || fpin == NULL)
    fatal("Error convering pipes to streams");

  while(printf("tinybc:"),fgets(message,BUFSIZ,stdin) != NULL){
    if(sscanf(message,"%d%[-+*/^]%d",&num1,operation,&num2) != 3){
      printf("syntax error\n");
      continue;
    }
    if(fprintf(fpout,"%d\n%d\n%c\np\n",num1,num2,*operation) == EOF)
      fatal("Error writing");
    fflush(fpout);
    if(fgets(message,BUFSIZ,fpin) == NULL)
      break;
    printf("%d %c %d = %s",num1,*operation,num2,message);
  }
  fclose(fpout);
  fclose(fpin);
}

void be_dc(int in[2],int out[2])
{
  if(dup2(in[0],0) == -1)
    oops("dc:cannot redirect stdin",3);
  if(dup2(out[1],1) == -1)
    oops("dc:cannot redirect stdout",4);
  close(in[1]);close(out[0]);
  close(in[0]);close(out[1]);

  execlp("dc","dc",NULL);
  oops("cannot run dc",5);
}

void fatal(char *mess)
{
  fprintf(stderr,"fatal:%s\n",mess);
  exit(1);
}

  此程序的实现原理很简单,只是使用了管道方面的系统调用然后进行重定向。将父程序的输出输入到dc中,将dc的输出输入到父进程中就行了。运行结果如下:

    在这里,我们一定要清楚哪些数据是dc通过管道传递给bc的。

五、总结

    在这里简单的阐述了一下何为服务器与客户端。并且通过一个实际的例子:linux中的计算器来具体的了解了一下协同进程的工作方式。最后编写了一段bc交互程序代码来了解bc是如何与dc进行数据的传递的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值