4

「名前付きパイプを使用したプロセス間通信の概要-名前付きパイプを使用した全二重通信」、link ;を使用して作業しようとしています。特にfd_server.c(参考のために以下に含まれています)

これが私の情報とコンパイル行です:

:〜$ cat / etc / issue
Ubuntu 10.04 LTS \ n \ l
:〜$ gcc --version
gcc(Ubuntu 4.4.3-4ubuntu5)4.4.3
:〜$ gcc fd_server.c -o fd_server

fd_server.c2つの名前付きパイプを作成します。1つは読み取り用、もう1つは書き込み用です。できることは、次のとおりです。1つの端末でサーバーを実行し、cat書き込みパイプを読み取ります(スルーします)。

:〜$ ./fd_server&2> / dev / null
[1] 11354
:〜$ cat / tmp / np2

もう1つは、サーバーの読み取りパイプに(echoを使用して)書き込みます。

:〜$ echo "heeellloooo"> / tmp / np1

最初のターミナルに戻ると、次のことがわかります。

:〜$ cat / tmp / np2
HEEELLLOOOO
0[1]+終了13./fd_server2> / dev / null

私がやりたいのは、一種の「インタラクティブ」(または「シェル」のような)セッションを作成することです。つまり、サーバーは通常どおり実行されますが、実行する代わりに、screenに似たものを使用したいと思いcatます。つまり、画面はのように呼び出すことができ、ターミナルに入力されたものがに渡され、その応答がターミナルに書き込まれる、一種のインタラクティブセッションを作成します。もちろん、今は使用できません。私の場合、プログラムには2つの別個のノードがあり、私が知る限り、1つしか参照できないためです。echoscreen /dev/ttyS0 38400/dev/ttyS0screenscreen

このコンテキストで(2つの別々の読み取り/書き込みパイプを使用して)この種の「対話型」セッションを実現するにはどうすればよいでしょうか。

以下のコード:

#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//#include <fullduplex.h> /* For name of the named-pipe */
#define NP1     "/tmp/np1"
#define NP2     "/tmp/np2"
#define MAX_BUF_SIZE    255
#include <stdlib.h> //exit
#include <string.h> //strlen

int main(int argc, char *argv[])
{
    int rdfd, wrfd, ret_val, count, numread;
    char buf[MAX_BUF_SIZE];

    /* Create the first named - pipe */
    ret_val = mkfifo(NP1, 0666);

    if ((ret_val == -1) && (errno != EEXIST)) {
        perror("Error creating the named pipe");
        exit (1);
    }

    ret_val = mkfifo(NP2, 0666);

    if ((ret_val == -1) && (errno != EEXIST)) {
        perror("Error creating the named pipe");
        exit (1);
    }

    /* Open the first named pipe for reading */
    rdfd = open(NP1, O_RDONLY);

    /* Open the second named pipe for writing */
    wrfd = open(NP2, O_WRONLY);

    /* Read from the first pipe */
    numread = read(rdfd, buf, MAX_BUF_SIZE);

    buf[numread] = '0';

    fprintf(stderr, "Full Duplex Server : Read From the pipe : %sn", buf);

    /* Convert to the string to upper case */
    count = 0;
    while (count < numread) {
        buf[count] = toupper(buf[count]);
        count++;
    }

    /*
     * Write the converted string back to the second
     * pipe
     */
    write(wrfd, buf, strlen(buf));
}

編集:

明確にするために、非常によく似たものについて説明しているドキュメントを見つけたようですが、そこにあるスクリプトの変更です( "たとえば、次のスクリプトはデバイスを構成し、シリアルから受信したすべてのデータをコピーするためのバックグラウンドプロセスを開始します上記のプログラムのデバイスを標準出力に... ")は以下のとおりです。

# stty raw # 
( ./fd_server 2>/dev/null; )&
bgPidS=$!
( cat < /tmp/np2 ; )&
bgPid=$!
# Read commands from user, send them to device
echo $(kill -0 $bgPidS 2>/dev/null ; echo $?)
while [ "$(kill -0 $bgPidS 2>/dev/null ; echo $?)" -eq "0" ] && read cmd; do
   # redirect debug msgs to stderr, as here we're redirected to /tmp/np1
   echo "$? - $bgPidS - $bgPid" >&2
   echo "$cmd"
   echo -e "\nproc: $(kill -0 $bgPidS 2>/dev/null ; echo $?)" >&2
done >/tmp/np1
echo OUT
# Terminate background read process - if they still exist
if [ "$(kill -0 $bgPid 2>/dev/null ; echo $?)" -eq "0" ] ;
then
    kill $bgPid
fi
if [ "$(kill -0 $bgPidS 2>/dev/null ; echo $?)" -eq "0" ] ;
then
    kill $bgPidS
fi
# stty cooked

したがって、スクリプトをsayとして保存してstarter.sh呼び出すと、次のセッションが発生します。

$ ./starter.sh 
0
i'm typing here and pressing [enter] at end
0 - 13496 - 13497
I'M TYPING HERE AND PRESSING [ENTER] AT END
0~�.N=�(�~� �����}����@������~� [garble]
proc: 0
OUT

これは私が「対話型セッション」(デバッグステートメントを無視する)と呼ぶものです-サーバーは私がコマンドを入力するのを待ちます。コマンドを受信した後に出力を提供します(この場合のように、最初のコマンドの後に終了し、スタータースクリプトも終了します)。それを除いて、入力をバッファリングせずに、文字ごとに送信したい(つまり、上記のセッションは最初のキーを押した後に終了し、1文字だけを印刷する必要があります-これは私がstty生で役立つと思っていたものですが、そうではありません:それは両方Enterへの反応を殺すだけですCtrl- C:))

screen引数として2つの名前付きパイプを受け入れ、それらを介して「ターミナル」または「シェル」のようなセッションを確立する既存のコマンド(シリアルデバイスに関しては似ていると思います)がすでにある場合、私はたださまよっていました。または、上記のようなスクリプトを使用するか、端末として動作する独自の「クライアント」をプログラムする必要があります。

4

1 に答える 1

3

1行後に終了するのではなく、複数の行を受信できるようにしたい場合は、これは簡単です。次のように、読み取り/書き込みコードの周りにループを配置する必要があります(迅速で汚い):

while( 1 ) {
    numread = read(rdfd, buf, MAX_BUF_SIZE);

    fprintf(stderr, "Full Duplex Server : Read From the pipe : %sn", buf);

    /* Convert to the string to upper case */
    count = 0;
    while (count < numread) {
        buf[count] = toupper(buf[count]);
        count++;
    }

    /*
     * Write the converted string back to the second
     * pipe
     */
    write(wrfd, buf, strlen(buf));
}

もちろん、これでアプリケーションは終了せず、EOFなどを取得するとすぐに何も実行しなくなります。したがって、エラーをチェックするために再編成できます。

numread = read(rdfd, buf, MAX_BUF_SIZE);
while( numread > 0) {
    /* ... etc ... */
    numread = read(rdfd,buf, MAX_BUF_SIZE);
}
if( numread == 0 )  {
    /* ... handle eof ... */
}
if( numread < 0 ) {
    /* ... handle io error ... */
} 

マニュアルページから、readはEOFの場合は0を返し、エラーの場合は-1を返します(マニュアルページを読んだことがありますか? http://linux.die.net/man/2/read)。したがって、これは、EOFまたは何らかのエラーに達するまで、読み取りパイプからバイトを取得し続けます。その場合、(おそらく)メッセージを出力して終了します。そうは言っても、EOFを取得したときに再開するだけで、より多くの入力を取得できます。

継続的に読み取るようにプログラムを変更したら、複数の行をインタラクティブに入力するのは簡単です。実行するだけです:

cat - > /tmp/np1

'-'は、catにstdinから読み取るように明示的に指示します(これはデフォルトであるため、実際にはダッシュは必要ありません)。したがって、猫はあなたが入力したすべてのものをあなたのパイププログラムに渡します。Ctrl + Dを使用してEOFを挿入できます。これにより、catはstdinの読み取りを停止します。パイププログラムに何が起こるかは、読み取りループでEOFを処理する方法によって異なります。

ここで、猫なしですべてのioを実行する別のプログラムが必要な場合(つまり、stdioエコープログラムになります)、擬似コードは次のようになります。

const int stdin_fd = 0;  // known unix constant!
int readpipe_fd = open the read pipe, as before 
int writepipe_fd =  open the write pipe, as before 
read stdin into buffer
while( stdin is reading correctly ) {
     write data from stdin to read pipe
         check write is successful
     read write pipe into buffer
         check read is successful
     write buffer to stdout (fprintf is fine)
     read stdin into buffer.
}

必要に応じて、readシステムコールを使用してstdinを読み取ることができますが、stdioを使用することもできます。パイプの読み取り、書き込み、およびオープンは、読み取り/書き込みがすべて逆になっていることを除いて、すべてサーバープログラムと同じである必要があります。

于 2010-05-06T13:36:42.987 に答える