掲示板利用宣言

 次のフォームをすべてチェックしてからご利用ください。

 私は

 題名と投稿者名は具体的に書きます。
 課題の丸投げはしません。
 ソースの添付は「HTML変換ツール」で字下げします。
 返信の引用は最小限にします。
 環境(OSとコンパイラ)や症状は具体的に詳しく書きます。
 返信の付いた投稿は削除しません。
 マルチポスト(多重投稿)はしません。

掲示板1

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    記事検索    ログ    タグ一覧

No.7683

親プロセス内のファイル書き込みと子プロセス内のファイル書き込み
投稿者---fork socket初心者(2007/07/18 16:06:39)


以下についてアドバイスを頂ければ幸いです.

socketを用いたサーバクライアントプログラムの勉強をしております.

各クライアントから接続があったら,
サーバはクライアントに文字列"OK!"を送り,
ファイルに接続情報を書き込もうとしています.
複数クライアントからの接続に対応できるようforkを用いました.

接続情報を書き込むファイルは次のようになると想定しました.
------------------------
Parent
Child:0
Child:1
Child:2
------------------------
"Child:0", "Child:1", "Child:2"の書き込み順序が異なるかも知れないと
は思っていたのですが,
実際には,次のように"Parent"が何度も書かれてしまいました.
-------------------------
Parent
Child:0
Parent
Child:1
Parent
Child:2
Parent
Parent
Child:3
---------------------------

自分の理解では,
int main(void){
  /*親プロセス*/
  /**"Parent"のファイル書き込み**/
  while(1){
    if((pid=fork())==0{
      /*子プロセス*/
      /**"Child:[0-3]の書き込み**/
    }
    else if(pid>0){
      /*親プロセスの続き*/
    }
  }
}

と思っていて,while文の前にある"Parent"の書き込みが
複数回起こり得ないと思っているのですが,
何を勘違いしているのか見当がついておりません.
何かアドバイスを頂ければ幸いです.

通信は問題なく行われているようですので,
クライアントプログラムは掲載せず,
サーバプログラムだけ以下に示させて頂きます.
(必要ならばクライアントプログラムも掲載させて頂きます)

なお,環境はFreeBSD 4.11R, gcc-2.95.4です.

よろしくお願いします.

サーバ側プログラム↓(ここから最後まで)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

#define PORT     10000
#define BUF_LEN  64

int main(void){
    int  soc_waiting, soc;
    struct sockaddr_in me;
    int pid, cnt;
    FILE *fp;

    if((fp=fopen("receive.txt", "w"))==NULL){
        perror("fopen");
        exit(1);
    }
    fprintf(fp, "Parent\n");

    if((soc_waiting = socket(AF_INET, SOCK_STREAM, 0))<0){
        perror("socket");
        exit(1);
    }
    memset((char *)&me,0,sizeof(me));
    me.sin_addr.s_addr = htonl(INADDR_ANY);
    me.sin_port = htons(PORT);
    me.sin_family = AF_INET; 

    if(bind(soc_waiting, (struct sockaddr *)&me, sizeof(me))==-1){
        perror("bind");
        exit(1);
    }
  
    listen(soc_waiting,1);

    cnt=0;
    while(1){
        soc = accept(soc_waiting,NULL,NULL);
        if((pid=fork())==-1){
                perror("fork");
                exit(1);
        }
        else if(pid==0){
              close(soc_waiting);
              fprintf(fp, "Child:%d\n", cnt);
              send(soc, "OK!", BUF_LEN, 0);
                exit(0);
        }
        else{
              close(soc);
        }
        if(cnt>=3) break;
        cnt++;
    }
    fclose(fp);
    return 0;
}




この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:親プロセス内のファイル書き込みと子プロセス内のファイル書き込み 7684 かずま 2007/07/18 21:23:33


No.7684

Re:親プロセス内のファイル書き込みと子プロセス内のファイル書き込み
投稿者---かずま(2007/07/18 21:23:33)


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    int cnt = 0;
    FILE *fp = fopen("receive.txt", "w");
    if (!fp) perror("fopen"), exit(1);
    fprintf(fp, "Parent\n");
    // fflush(fp); // これの有無を比べてみてください。
    do {
        sleep(1);
        pid = fork();
        if (pid == -1) perror("fork"), exit(1);
        if (pid == 0) fprintf(fp, "Child:%d\n", cnt), exit(0);
    } while (++cnt <= 3);
    fclose(fp);
    return 0;
}
もっと詳しい説明が必要ですか?


この投稿にコメントする

削除パスワード

No.7685

Re:親プロセス内のファイル書き込みと子プロセス内のファイル書き込み
投稿者---かずま(2007/07/18 21:31:52)


    int pid;
を追加してください。


この投稿にコメントする

削除パスワード

No.7686

Re:親プロセス内のファイル書き込みと子プロセス内のファイル書き込み
投稿者---yoh2(2007/07/18 23:01:53)


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    int cnt = 0;
    FILE *fp = fopen("receive.txt", "w");
    if (!fp) perror("fopen"), exit(1);
    setbuf(fp, NULL);  /* fpのバッファリング禁止 */
    fprintf(fp, "Parent\n");
    do {
        ...
    } while (++cnt <= 3);
    fclose(fp);
    return 0;
}
という手も。

そもそも、プロセス間で共有するファイルをFILE*越しに扱うのが
OKだったかどうか覚えていません。
最悪の場合、フォーマット付き出力が面倒になってしまいますが、
生のファイルデスクリプタを使わないといけないかもしれません。


この投稿にコメントする

削除パスワード

No.7692

Re:親プロセス内のファイル書き込みと子プロセス内のファイル書き込み
投稿者---fork socket初心者(2007/07/19 21:10:33)


別法のご提案を有難うございます。

>
    //省略
    FILE *fp = fopen("receive.txt", "w");
    if (!fp) perror("fopen"), exit(1);
    setbuf(fp, NULL);  /* fpのバッファリング禁止 */
    //省略


所望の結果を得ることができました。

私の拙い知識ではバッファリングしない状態の方が理解し易いのですが、
動作が緩慢になるというような記述を見かけて
そういうものかなとも思っております。(分かってません)

それよりも大事そうな以下のご指摘を有難うございます。

>そもそも、プロセス間で共有するファイルをFILE*越しに扱うのが
>OKだったかどうか覚えていません。
>最悪の場合、フォーマット付き出力が面倒になってしまいますが、
>生のファイルデスクリプタを使わないといけないかもしれません。

今のままだと
子プロセスが一斉にファイルに書き込むことによって
-----
CChhiilldd::01
-----
となる可能性もあるのですよね。

「複数プロセスでファイルを共有する場合に、
標準ライブラリ入出力関数か低水準入出力関数か」
について勉強したいと思います。

有難うございました。






この投稿にコメントする

削除パスワード

No.7693

Re:親プロセス内のファイル書き込みと子プロセス内のファイル書き込み
投稿者---yoh2(2007/07/20 00:27:04)


プロセス関で共有するファイルをFILE*で扱ってよいかどうかの問題は脇に置いておくとして。

>私の拙い知識ではバッファリングしない状態の方が理解し易いのですが、
>動作が緩慢になるというような記述を見かけて
>そういうものかなとも思っております。(分かってません)

1行に出力するのがちょうど1メッセージだけであると仮定してよいなら、単純に
バッファリング禁止にせず、setvbuf()を使って行バッファリングモード(_IOLBF)に
するという手もあります。
完全バッファリングモード(_IOFBF)+要所要所でfflush()が一番効率よいとは
思いますが、fflush()、よく忘れるんですよね……


この投稿にコメントする

削除パスワード

No.7696

Re:親プロセス内のファイル書き込みと子プロセス内のファイル書き込み
投稿者---fork socket初心者(2007/07/20 20:42:04)


補足説明を有難うございます。

>1行に出力するのがちょうど1メッセージだけであると仮定してよいなら、単純に
>バッファリング禁止にせず、setvbuf()を使って行バッファリングモード(_IOLBF)に
>するという手もあります。

バッファするかしないかだけではないんですね。勉強になります。
1行1メッセージとは、例えば
  for(i=0;i<100;i++){
    fprintf(fp, "%d", i);
  }
  fprintf("\n");

の代わりに
  char buf[十分に大きい値], *s;
  s=buf;
  for(i=0;i<100;i++){
    buf+=sprintf(buf, "%d", i);
  }
  sprintf(s, "\n");

のように、1行分貯めてから書き込むということでしょうか。
(上のコードに意味はないのですが、
受信ログには、クライアントとのやりとりによる複数のデータを1行に書き込むつもりで、
今は各データ毎に書き込んでいるのですが、
変更した方がよいですね。)

1行の文字数がバッファサイズ超えると完全バッファリングになってしまうので注意が必要ですね。
(テキストファイル書き込みなので1行当りの文字数が多ければ
適当に改行を入れることになると思いますが)
むしろ、1行当りの文字数に合わせてバッファサイズを設定しなおすのでしょうか。
色々と試しながら勉強していきたいと思います。

>fflush()、よく忘れるんですよね……

確かに忘れそうですね。肝に銘じます。
有難うございました。


この投稿にコメントする

削除パスワード

No.7690

Re:親プロセス内のファイル書き込みと子プロセス内のファイル書き込み
投稿者---fork socket初心者(2007/07/19 20:16:37)


明快なご解答有難うございます。

>
    //省略
    fprintf(fp, "Parent\n");
    // fflush(fp); // これの有無を比べてみてください。
  //省略


fflushを呼び出すことで所望の結果が得られたものの、
今一つ理解し切れずに考え続けていて
お返事が遅くなってしまいまして申し訳ございません。

ちらと
>もっと詳しい説明が必要ですか?
に甘えてしまおうかとも思ったのですが、先ほど氷解いたしました(と思います)。

初めは
「バッファが一つなのに何で何度も"Parent"が書かれるのか?」
と悩んでいたのですが、
子プロセスは親プロセスのリソースをコピーして別に持ち、
そのリソースの中にバッファも含まれ、
まだフラッシュされずに"Parent"を保持したままのバッファが
子プロセスにコピーされるから、
3人の子が皆独立に"Parent"を保持してしまうのですね。
(もし勘違いでしたらご指摘頂ければ幸いです。)

そもそも問題はソケット通信とは無関係で、
単純にforkが分かっていないことが原因だったのですね。
forkだけを使って理解を深めたいと思います。

有難うございました。



この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    記事検索    ログ    タグ一覧





掲示板提供:(有)リアル・インテグリティ