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鍵則會返回結果。

linux中的計算器bc

這時候大家可能會覺得我們向bc進程輸入了數據,bc進行運算後將結果返回給了屏幕終端。而實際上並非如此。

實際上bc只是一個用戶的界面,讓用戶輸入數值與給用戶返回結果而已。這個可以使用man bc命令來查看。

linux中的計算器bc

上面簡介說bc是一個計算器語言。而看以下dc的手冊。

linux中的計算器bc

這裡可以看到,dc是一個計算器。什麼意思呢?

其實是說,這裡的bc只是一個界面,而dc才是真正數據進行計算的地方。bc將接收到的數據傳送給dc進行運算,運算結束後,dc將結果返回給bc顯示輸出。

那麼,也就是說,bc其實一個客戶端,而dc是一個服務器。

四、編寫bc

搞清楚了bc與dc之間的關係,那麼,我們就可以使用現有的知識編寫出自己的bc。當然,這裡的bc指的是用戶的交互界面。而後臺的計算還是要交給dc。

#include

#include

#include

#include

#include

#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的輸出輸入到父進程中就行了。運行結果如下:

linux中的計算器bc

在這裡,我們一定要清楚哪些數據是dc通過管道傳遞給bc的。

五、總結

在這裡簡單的闡述了一下何為服務器與客戶端。並且通過一個實際的例子:linux中的計算器來具體的瞭解了一下協同進程的工作方式。最後編寫了一段bc交互程序代碼來了解bc是如何與dc進行數據的傳遞的。


分享到:


相關文章: