HP OpenVMS Systems Documentation |
| 前へ | 次へ | 目次 | 索引 |
レコード・モードで固定長レコード・ファイルにアクセスする場合, I/O 関数は一般に, 第 1.8.2.2.1 項 で説明したように動作します。
省略可能な引数 "ctx=xplct" を指定してファイルをオープンした場合を除き,指定したレコード・サイズが最大レコード・サイズの整数倍でないと, write関数, fwrite関数, decc$record_write関数はエラーになります。他のすべての出力関数は n バイトごとにレコードを生成します。ただし,n は最大レコード・サイズです。
fflushによって新しいレコードが生成される場合,最大レコード・サイズになるようにバッファ内のデータにヌル文字が付加されます。
ファイルの終端 (EOF) を検索するプログラムの場合,このヌル文字の付加が問題になることがあります。たとえば,プログラムでファイルの終端にデータを追加した後,ファイルを逆向きにシークし ( fflushが実行されます),その後で再びファイルの終端を検索すると,元のファイルの終端がレコード境界上になかった場合,元のファイルの終端と新しいファイルの終端の間に, 0 が埋め込まれた「穴」が作成されています。 |
例 1-1 は,ストリーム・モードとレコード・モードのアクセスの相違点を示しています。
| 例 1-1 ストリーム・モードとレコード・モードのアクセスの相違点 |
|---|
/* CHAP_1_STREAM_RECORD.C */
/* This program demonstrates the difference between */
/* record mode and stream mode input/output. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void process_records(const char *fspec, FILE * fp);
main()
{
FILE *fp;
fp = fopen("example-fixed.dat", "w", "rfm=fix", "mrs=40", "rat=none");
if (fp == NULL) {
perror("example-fixed");
exit(EXIT_FAILURE);
}
printf("Record mode\n");
process_records("example-fixed.dat", fp);
fclose(fp);
printf("\nStream mode\n");
fp = fopen("example-streamlf.dat", "w");
if (fp == NULL) {
perror("example-streamlf");
exit(EXIT_FAILURE);
}
process_records("example-streamlf.dat", fp);
fclose(fp);
}
void process_records(const char *fspec, FILE * fp)
{
int i,
sts;
char buffer[40];
/* Write records of all 1's, all 2's and all 3's */
for (i = 0; i < 3; i++) {
memset(buffer, '1' + i, 40);
sts = fwrite(buffer, 40, 1, fp);
if (sts != 1) {
perror("fwrite");
exit(EXIT_FAILURE);
}
}
/* Rewind the file and write 10 characters of A's, then 10 B's, */
/* then 10 C's. */
/* */
/* For stream mode, each fwrite call outputs 10 characters */
/* and advances the file position 10 characters */
/* characters. */
/* */
/* For record mode, each fwrite merges the 10 characters into */
/* the existing 40-character record, updates the record and */
/* advances the file position 40 characters to the next record. */
rewind(fp);
for (i = 0; i < 3; i++) {
memset(buffer, 'A' + i, 10);
sts = fwrite(buffer, 10, 1, fp);
if (sts != 1) {
perror("fwrite2");
exit(EXIT_FAILURE);
}
}
/* Now reopen the file and output the records. */
fclose(fp);
fp = fopen(fspec, "r");
for (i = 0; i < 3; i++) {
sts = fread(buffer, 40, 1, fp);
if (sts != 1)
perror("fread");
printf("%.40s\n", buffer);
}
return;
}
|
このプログラムを実行すると,次の出力が生成されます。
Record Mode AAAAAAAAAA111111111111111111111111111111 BBBBBBBBBB222222222222222222222222222222 CCCCCCCCCC333333333333333333333333333333 Stream mode AAAAAAAAAABBBBBBBBBBCCCCCCCCCC1111111111 2222222222222222222222222222222222222222 3333333333333333333333333333333333333333 |
複数のシステム間でソース・プログラムを移植する予定がある場合, HP C RTL を使用するための最後の準備作業の 1 つとして, HP C RTL と,C 言語の他の実装のランタイム・ライブラリの相違点を認識することが必要です。ここでは,OpenVMS システムとの間でプログラムを移植するときに発生する可能性のある問題の一部について説明します。移植性は HP C RTL の実装に密接に関係していますが,ここでは,他の HP C for OpenVMS 構造体の移植性についても説明します。
HP C RTL では, ANSI C で定義されているライブラリ関数をはじめ,一般に使用されている多くの API や,若干の OpenVMS 拡張機能も提供されます。特定の標準のうち, HP C RTL で実装されている部分については, 第 1.5 節 を参照してください。可能な限り機能面で完全な移植性を維持するようになっています。 HP C RTL で提供される多くの標準 I/O および UNIX I/O の関数およびマクロは,他の実装の関数やマクロに機能的に対応します。
RTL 関数およびマクロの説明では,ここに示した問題の他に,ここに示していない問題についても詳しく説明しています。
次の一覧は,C プログラムを OpenVMS 環境に移植するときに考慮しなければならない問題点を示しています。
int foo 123; |
この初期化形式を使用するプログラムは変更する必要があります。
int a, b, c; |
a[i] = i++; |
x = func_y() + func_z(); |
f(p++, p++) |
HP C でも他の C コンパイラでも,このような式がすべての C コンパイラで同じ順序で評価されるという保証はありません。
HP C RTL では,リエントラントのサポートが向上し,強化されました。次の種類のリエントラントがサポートされます。
デフォルトのリエントラント・タイプは TOLERANT です。
リエントラント・タイプを設定するには, /REENTRANCY コマンド・ライン修飾子を指定してコンパイルするか, decc$set_reentrancy関数を呼び出します。 この関数は非 AST レベルから排他的に呼び出さなければなりません。
複数のスレッドまたは AST を使用するアプリケーションをプログラミングする場合は,次の 3 つのクラスの関数について考慮してください。
ほとんどの関数は,まったく内部データのない関数です。このような関数の場合,同期化が必要になるのは,パラメータがマルチスレッドのアプリケーションで使用されるか,または AST コンテキストと非 AST コンテキストの両方でパラメータが使用される場合だけです。たとえば, strcat関数は一般にはスレッド・セーフですが,次の例は安全でない使い方を示しています。
extern char buffer[100];
void routine1(char *data) {
strcat( buffer, data );
}
|
routine1が複数のスレッドで並列に実行されるか,または routine1がそのルーチンを呼び出す AST ルーチンによって割り込まれると, strcat呼び出しの結果は予測不能になります。
2 番目のクラスの関数は,スレッドだけで有効な静的データを含む関数です。通常,これらの関数は,アプリケーションが文字列の格納領域を解放することを許可されていない状況で,文字列を返すライブラリ内のルーチンです。これらのルーチンはスレッド・セーフですが,AST リエントラントではありません。つまり,並列に呼び出しても安全ですが,各スレッドはそれぞれ独自のデータのコピーを保有します。同じルーチンが非 AST コンテキストで実行される可能性がある場合は,これらのルーチンを AST ルーチンから呼び出すことはできません。このクラスのルーチンは次のとおりです。
asctime stat ctermid strerror ctime strtok cuserid VAXC$ESTABLISH gmtime the errno variable localtime wcstok perror |
使用している TCP/IP 製品がスレッド・セーフの場合,すべてのソケット関数もこのリストに含まれます。
3 番目のクラスの関数は,プロセス単位のデータに影響する関数です。これらの関数はスレッド・セーフではなく,AST リエントラントでもありません。たとえば, sigsetmaskはプロセス単位のシグナル・マスクを確立します。次のようなルーチンについて考えてみましょう。
void update_data
base()
{
int old_mask;
old_mask = sigsetmask( 1 << (SIGINT - 1));
/* Do work here that should not be aborted. */
sigsetmask( old_mask );
}
|
update_databaseが複数のスレッドで並列して呼び出された場合,スレッド 2 が強制終了されない作業をまだ実行している間に,スレッド 1 は SIGINT のブロックを解除する可能性があります。
このクラスのルーチンは次のとおりです。
一般に, UTC ベースの時刻関数はメモリ内のタイム・ゾーン情報に影響を与える可能性があり,これはプロセス単位のデータです。しかし,アプリケーションが実行されている間,システム・タイム・ゾーンが変化せず ( これは一般的な場合です ),タイム・ゾーン・ファイルのキャッシュが許可されている場合 ( これはデフォルトです ),時刻関数 asctime_r, ctime_r, gmtime_r, localtime_rの _rバリアントはスレッド・セーフであり,かつ AST リエントラントです。 しかし,アプリケーションの実行中にシステム・タイム・ゾーンが変化する可能性がある場合や,タイム・ゾーン・ファイルのキャッシュが許可されていない場合は, UTC ベースの時刻関数のバリアントはどちらも 3 番目のクラスの関数に属し,スレッド・セーフでも AST リエントラントでもありません。 |
次に示す一部の関数は,リエントラントであるかどうかに関係なく,本質的にスレッド・セーフではありません。
execl exit execle _exit execlp nice execv system execve vfork execvp |
| 前へ | 次へ | 目次 | 索引 |