システムインタフェース

付録 A 完全なコーディング例

次のプログラムは、priocntl(1) -l ユーティリティの呼び出しと同じです。これは、タイムシェアリングと実時間のスケジューラクラスに対して有効な優先順位の範囲の取得と印刷を行います。


例 A-1 priocntl(2) を使用した有効な優先順位の表示

 /*
 * スケジューラクラス ID と優先順位範囲を取得する
 */

#include <sys/types.h>
#include <sys/priocntl.h>
#include <sys/rtpriocntl.h>
#include <sys/tspriocntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
main ()
{
	pcinfo_t			pcinfo;
	tsinfo_t			*tsinfop;
	rtinfo_t			*rtinfop;
	short			maxtsupri, maxrtpri;

	/* タイムシェアリング */
		(void) strcpy (pcinfo.pc_clname, "TS");
		if (priocntl (0L, 0L, PC_GETCID, &pcinfo) == -1L) {
			perror ("PC_GETCID failed for time-sharing class");
			exit (1);
		}
		tsinfop = (struct tsinfo *) pcinfo.pc_clinfo;
		maxtsupri = tsinfop->ts_maxupri;
		(void) printf("Time sharing: ID %ld, priority range -%d
					 through %d¥n",
			pcinfo.pc_cid, maxtsupri, maxtsupri);

	/* 実時間 */
		(void) strcpy(pcinfo.pc_clname, "RT");
		if (priocntl (0L, 0L, PC_GETCID, &pcinfo) == -1L) {
			perror ("PC_GETCID failed for realtime class");
			exit (2);
		}
	rtinfop = (struct rtinfo *) pcinfo.pc_clinfo;
	maxrtpri = rtinfop->rt_maxpri;
	(void) printf("Real time: ID %ld, priority range 0 through %d¥n",
			pcinfo.pc_cid, maxrtpri);
	return (0);
}

次に、getcid というこのプログラムの出力例を示します。


$ getcid
Time sharing: ID 1, priority range -20 through 20
Real time: ID 2, priority range 0 through 59 

次の例は、PC_GETCLINFO を使用して、プロセス ID を元にしてプロセスのクラス名を取得します。


例 A-2 priocntl(2) を使用したクラス名の取得

/* プロセス ID を指定してスケジューラクラス名を取得する */

#include <sys/types.h>
#include <sys/priocntl.h>
#include <sys/rtpriocntl.h>
#include <sys/tspriocntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

main (argc, argv)
	int argc;
	char *argv[];
{
	pcinfo_t			pcinfo;
	id_t			pid, classID;
	id_t			getclassID();

	if ((pid = atoi(argv[1])) <= 0) {
		perror ("bad pid");
		exit (1);
	}
	if ((classID = getclassID(pid)) == -1) {
		perror ("unknown class ID");
		exit (2);
	}
	pcinfo.pc_cid = classID;
	if (priocntl (0L, 0L, PC_GETCLINFO, &pcinfo) == -1L) {
		perror ("PC_GETCLINFO failed");
		exit (3);
	}
	(void) printf("process ID %d, class %s¥n", pid,
	 pcinfo.pc_clname);
}

/*
 * ID が pid のプロセスのスケジューリングクラス ID を戻す
 */

getclassID (pid)
	id_t pid;
{
	pcparms_t			pcparms;

	pcparms.pc_cid = PC_CLNULL;
	if (priocntl(P_PID, pid, PC_GETPARMS, &pcparms) == -1) {
		return (-1);
	}
	return (pcparms.pc_cid);
}

次のプログラムは、プロセス ID を入力として受け取り、プロセスを「最高有効優先順位 - 1」の実時間プロセスにし、その優先順位のデフォルトのタイムスライスをプロセスに与えます。プログラムは schedinfo 関数を呼び出して、実時間クラス ID と最高優先順位を取得します。


例 A-3 priocntl(2) を使用した指定プロセスの実時間への変換

/*
 * 入力引数はプロセス ID。
 * プロセスを最高優先順位 - 1 の実時間プロセスにする
 */
#include <sys/types.h>
#include <sys/priocntl.h>
#include <sys/rtpriocntl.h>
#include <sys/tspriocntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

main (argc, argv)
	int argc;
	char *argv[];
{
	pcparms_t		pcparms;
	rtparms_t		*rtparmsp;
	id_t		pid, rtID;
	id_t		schedinfo();
	short		maxrtpri;
	if ((pid = atoi(argv[1])) <= 0) {
		perror ("bad pid");
		exit (1);
	}

	/* 実時間プロセスの優先順位の最大を取得する */
	if ((rtID = schedinfo ("RT", &maxrtpri)) == -1) {
		perror ("schedinfo failed for RT");
		exit (2);
	}

	/* プロセスを最高優先順位 - 1、デフォルトのタイムスライスの RT に変更する */
	pcparms.pc_cid = rtID;
	rtparmsp = (struct rtparms *) pcparms.pc_clparms;
	rtparmsp->rt_pri = maxrtpri - 1;
	rtparmsp->rt_tqnsecs = RT_TQDEF;

	if (priocntl(P_PID, pid, PC_SETPARMS, &pcparms) == -1) {
		perror ("PC_SETPARMS failed");
		exit (3);
	}
}

/*
 * クラス ID と最高優先順位を戻す。
 * 入力引数名はクラス名。
 * 最高優先順位は *maxpri に戻される
 */

id_t
schedinfo (name, maxpri)
	char *name;
	short *maxpri;
{
	pcinfo_t			info;
	tsinfo_t			*tsinfop;
	rtinfo_			*rtinfop;

	(void) strcpy(info.pc_clname, name);
	if (priocntl (0L, 0L, PC_GETCID, &info) == -1L) {
		return (-1);
	}
	if (strcmp(name, "TS") == 0) {
		tsinfop = (struct tsinfo *) info.pc_clinfo;
		*maxpri = tsinfop->ts_maxupri;
	} else if (strcmp(name, "RT") == 0) {
		rtinfop = (struct rtinfo *) info.pc_clinfo;
		*maxpri = rtinfop->rt_maxpri;
	} else {
		return (-1);
	}
	return (info.pc_cid);
}

次に、priocntlset(2) が有用な例を示します。プログラムが 1 つのユーザ ID だけで実行する実時間プロセスとタイムシェアリングプロセスの両方を持っていると想定します。タイムシェアリングプロセスを実時間プロセスに変えずに、実時間プロセスだけの優先順位を変更したい場合、プログラムは次のようになります。


例 A-4 priocntlset(2) を使用したプロセス優先順位の変更

/*
 * この uid の実時間優先順位を
 * 実時間プロセスの最高優先順位 - 1 に変更する
 */
#include <sys/types.h>
#include <sys/priocntl.h>
#include <sys/rtpriocntl.h>
#include <sys/tspriocntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

main (argc, argv)
	int argc;
	char *argv[];
{
	procset_t				procset;
	pcparms_t				pcparms;
	struct rtparms				*rtparmsp;
	id_t				rtclassID;
	id_t				schedinfo();
	short				maxrtpri;

	/* このプロセスと同じ uid のプロセスを選択する */
	procset.p_lidtype = P_UID;
	procset.p_lid = getuid();

	/* 実時間クラスに関する情報を取得する */
	if ((rtclassID = schedinfo ("RT", &maxrtpri)) == -1) {
		perror ("schedinfo failed");
		exit (1);
	}

...
}

/*
 * クラス ID と最高優先順位を戻す。
 * 入力引数名はクラス名。
 * 最高優先順位は *maxpri に戻される
 */

id_t
schedinfo (name, maxpri)
	char *name;
	short *maxpri;
{
	pcinfo_t			info;
	tsinfo_t			*tsinfop;
	rtinfo_			*rtinfop;

	(void) strcpy(info.pc_clname, name);
	if (priocntl (0L, 0L, PC_GETCID, &info) == -1L) {
		return (-1);
	}
	if (strcmp(name, "TS") == 0) {
		tsinfop = (struct tsinfo *) info.pc_clinfo;
		*maxpri = tsinfop->ts_maxupri;
	} else if (strcmp(name, "RT") == 0) {
		rtinfop = (struct rtinfo *) info.pc_clinfo;
		*maxpri = rtinfop->rt_maxpri;
	} else {
		return (-1);
	}
	return (info.pc_cid);
}


例 A-5 msgget(2) を使用したプログラム

/*
 * msgget.c: msgget() 関数の例を示す。
 * このプログラムは msgget() 関数の簡単な例である。
 * 引数を要求し、呼び出しを行い、結果を報告する
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

extern void	 exit();
extern void	 perror();

main()
{
	key_t		key;		/* msgget() に渡すキー */
	int		msgflg,		/* msgget() に渡す msgflg */
			msqid;		/* msgget() からの戻り値 */

	(void) fprintf(stderr,
		"All numeric input is expected to follow C conventions:¥n");
	(void) fprintf(stderr,
		"¥t0x... is interpreted as hexadecimal,¥n");
	(void) fprintf(stderr, "¥t0... is interpreted as octal,¥n");
	(void) fprintf(stderr, "¥totherwise, decimal.¥n");
	(void) fprintf(stderr, "IPC_PRIVATE == %#lx¥n", IPC_PRIVATE);
	(void) fprintf(stderr, "Enter key: ");
	(void) scanf("%li", &key);
	(void) fprintf(stderr, "¥nExpected flags for msgflg argument
are:¥n");
	(void) fprintf(stderr, "¥tIPC_EXCL =¥t%#8.8o¥n", IPC_EXCL);
	(void) fprintf(stderr, "¥tIPC_CREAT =¥t%#8.8o¥n", IPC_CREAT);
	(void) fprintf(stderr, "¥towner read =¥t%#8.8o¥n", 0400);
	(void) fprintf(stderr, "¥towner write =¥t%#8.8o¥n", 0200);
	(void) fprintf(stderr, "¥tgroup read =¥t%#8.8o¥n", 040);
	(void) fprintf(stderr, "¥tgroup write =¥t%#8.8o¥n", 020);
	(void) fprintf(stderr, "¥tother read =¥t%#8.8o¥n", 04);
	(void) fprintf(stderr, "¥tother write =¥t%#8.8o¥n", 02);
	(void) fprintf(stderr, "Enter msgflg value: ");
	(void) scanf("%i", &msgflg);

	(void) fprintf(stderr, "¥nmsgget: Calling msgget(%#lx,
%#o)¥n",
	key, msgflg);
	if ((msqid = msgget(key, msgflg)) == -1)
	{
		perror("msgget: msgget failed");
		exit(1);
	} else {
		(void) fprintf(stderr,
			"msgget: msgget succeeded: msqid = %d¥n", msqid);
		exit(0);
	}
} 


例 A-6 msgctl(2) を使用したプログラム

/*
 * msgctl.c:  msgctl() 関数の例を示す。
 *
 * これは msgctl() 関数の簡単な例である。
 * このプログラムは、1 つのメッセージ待ち行列に
 * 1 つの制御操作を実行できるようにする。
 * 制御操作が失敗するとただちに処理を中止するので、
 * 読み取り権のないアクセス権の設定にしないように
 * 注意が必要である。
 * このコードでアクセス権の再設定はできない
 */
#include			<stdio.h>
#include			<sys/types.h>
#include			<sys/ipc.h>
#include			<sys/msg.h>
#include			<time.h>

static void do_msgctl();
extern void exit();
extern void perror();
static char warning_message[] = "If you remove read permission for ¥
					yourself, this program will fail frequently!";

main()
{
	struct msqid_ds	buf;					/* IPC_STAT コマンドと IP_SET コマンドのための
						待ち行列記述子バッファ */
	int				cmd,		/* msgctl() に指定するコマンド */
				msqid;		/* msgctl() に指定する待ち行列 ID */

	(void fprintf(stderr,
		"All numeric input is expected to follow C conventions:¥n");
	(void) fprintf(stderr,
		"¥t0x... is interpreted as hexadecimal,¥n");
	(void) fprintf(stderr, "¥t0... is interpreted as octal,¥n");
	(void) fprintf(stderr, "¥totherwise, decimal.¥n");

	/* msgctl() 呼び出しの msgid 引数と cmd 引数を取得する */
	(void) fprintf(stderr,
		"Please enter arguments for msgctls() as requested.");
	(void) fprintf(stderr, "¥nEnter the msqid: ");
	(void) scanf("%i", &msqid);
	(void) fprintf(stderr, "¥tIPC_RMID = %d¥n", IPC_RMID);
	(void) fprintf(stderr, "¥tIPC_SET = %d¥n", IPC_SET);
	(void) fprintf(stderr, "¥tIPC_STAT = %d¥n", IPC_STAT);
	(void) fprintf(stderr, "¥nEnter the value for the command: ");
	(void) scanf("%i", &cmd);

	switch (cmd) {
		case IPC_SET:
			/* メッセージ待ち行列制御構造体にある設定を修正する */
			(void) fprintf(stderr, "Before IPC_SET, get current values:");
			/* IPC_STAT 処理にそのまま進む */
		case IPC_STAT:
			/* 現在のメッセージ待ち行列制御構造体のコピーを取得して
			 * 表示する */
			do_msgctl(msqid, IPC_STAT, &buf);
			(void) fprintf(stderr, ]
			"msg_perm.uid = %d¥n", buf.msg_perm.uid);
			(void) fprintf(stderr,
			"msg_perm.gid = %d¥n", buf.msg_perm.gid);
			(void) fprintf(stderr,
			"msg_perm.cuid = %d¥n", buf.msg_perm.cuid);
			(void) fprintf(stderr,
			"msg_perm.cgid = %d¥n", buf.msg_perm.cgid);
			(void) fprintf(stderr, "msg_perm.mode = %#o, ",
			buf.msg_perm.mode);
			(void) fprintf(stderr, "access permissions = %#o¥n",
			buf.msg_perm.mode & 0777);
			(void) fprintf(stderr, "msg_cbytes = %d¥n",
							buf.msg_cbytes);
			(void) fprintf(stderr, "msg_qbytes = %d¥n",
							buf.msg_qbytes);
			(void) fprintf(stderr, "msg_qnum = %d¥n", buf.msg_qnum);
			(void) fprintf(stderr, "msg_lspid = %d¥n",
							buf.msg_lspid);
			(void) fprintf(stderr, "msg_lrpid = %d¥n",
							buf.msg_lrpid);
			(void) fprintf(stderr, "msg_stime = %s", buf.msg_stime ?
			ctime(&buf.msg_stime) : "Not Set¥n");
			(void) fprintf(stderr, "msg_rtime = %s", buf.msg_rtime ?
			ctime(&buf.msg_rtime) : "Not Set¥n");
			(void) fprintf(stderr, "msg_ctime = %s",
							ctime(&buf.msg_ctime));
			if (cmd == IPC_STAT)
				break;
			/* 今度は IPC_SET で続ける */
			(void) fprintf(stderr, "Enter msg_perm.uid: ");
			(void) scanf ("%hi", &buf.msg_perm.uid);
			(void) fprintf(stderr, "Enter msg_perm.gid: ");
			(void) scanf("%hi", &buf.msg_perm.gid);
			(void) fprintf(stderr, "%s¥n", warning_message);
			(void) fprintf(stderr, "Enter msg_perm.mode: ");
			(void) scanf("%hi", &buf.msg_perm.mode);
			(void) fprintf(stderr, "Enter msg_qbytes: ");
			(void) scanf("%hi", &buf.msg_qbytes);
			do_msgctl(msqid, IPC_SET, &buf);
			break;
		case IPC_RMID:
		default:
			/* メッセージ待ち行列を削除したか、未知のコマンドが指定された */
			do_msgctl(msqid, cmd, (struct msqid_ds *)NULL);
			break;
	}
	exit(0);
}

/*
 * msgctl() に渡す引数の指示を表示し、msgctl() を呼び出し、
 * 結果を報告する。msgctl() は失敗すると復帰しない。
 * この例では、エラーを処理せずに報告するだけである
 */
static void
do_msgctl(msqid, cmd, buf)
struct msqid_ds				*buf;		/* 待ち行列記述子バッファへのポインタ */
int				cmd,		/* コマンドコード */
				msqid;		/* 待ち行列 ID */
{
	register int	rtrn;		/* msgctl() からの戻り値の保持領域 */

	(void) fprintf(stderr, "¥nmsgctl: Calling msgctl(%d, %d, %s)¥n",
			msqid, cmd, buf ? "&buf" : "(struct msqid_ds *)NULL");
	rtrn = msgctl(msqid, cmd, buf);
	if (rtrn == -1) {
		perror("msgctl: msgctl failed");
		exit(1);
	} else {
		(void) fprintf(stderr, "msgctl: msgctl returned %d¥n",
						rtrn);
	}
}


例 A-7 msgsnd(2)msgrcv(2) を使用したプログラム

/*
 * msgop.c: msgsnd () 関数と msgrcv() 関数の例を示す。
 *
 * これは、メッセージの送信ルーチンと受信ルーチンの簡単な例である。
 * このプログラムを使用して、1 つの待ち行列との間で必要な数の
 * メッセージを送受信できる
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

static int	ask();
extern void	exit();
extern char	*malloc();
extern void	perror();

char	first_on_queue[] = "-> first message on queue",
	full_buf[] = "Message buffer overflow. Extra message text¥
				  discarded.";

main()
{
	register int				c;		/* メッセージテキスト入力 */
	int				choice;	/* ユーザが入力した操作コード */
	register int				i;		/* mtext のためのループ制御 */
	int				msgflg;	/* 操作のためのメッセージフラグ */
	struct msgbuf 				*msgp;		/* メッセージバッファへのポインタ */
	int				msgsz;		/* メッセージの大きさ */
	long				msgtyp;		/* 希望のメッセージタイプ */
	int				msqid,		/* 使用するメッセージ待ち行列 ID */
					maxmsgsz,	/* 割り当てるメッセージバッファの大きさ */
					rtrn;		/* msgrcv または msgsnd からの戻り値 */
	(void) fprintf(stderr,
		"All numeric input is expected to follow C conventions:¥n");
	(void) fprintf(stderr,
		"¥t0x... is interpreted as hexadecimal,¥n");
	(void) fprintf(stderr, "¥t0... is interpreted as octal,¥n");
	(void) fprintf(stderr, "¥totherwise, decimal.¥n");
	/* メッセージ待ち行列 ID を取得してメッセージバッファを設定する */
	(void) fprintf(stderr, "Enter msqid: ");
	(void) scanf("%i", &msqid);
	/*
	 * <sys/msg.h> は、char mtext[1] として定義された
	 * メッセージテキストの msgbuf 構造体の定義を含んでいることに
	 * 注意。
	 * したがって、この定義はテンプレートだけであり、
	 * 0 バイトまたは 1 バイトのメッセージだけを送受信したい場合
	 * 以外は、直接使用できる構造体定義ではない。
	 * これを扱うには、テンプレートを含む十分大きい領域、
	 * つまりメッセージテキストのテンプレートフィールドの大きさに、
	 * 必要なメッセージテキストのフィールドの大きさを加えたものを
	 * malloc する。
	 * その後、malloc によって戻されるポインタを、
	 * 必要な大きさのメッセージテキストフィールドの msgbuf 構造体
	 * として使用できる。
	 * msgp が何も指していなくても、
	 * sizeof (msgp->mtext) は有効であることにも注意
	 */
	(void) fprintf(stderr,
		"Enter the message buffer size you want:");
	(void) scanf("%i", &maxmsgsz);
	if (maxmsgsz < 0) {
		(void) fprintf(stderr, "msgop: %s¥n",
				"The message buffer size must be >= 0.");
		exit(1);
	}
	msgp = (struct msgbuf *)malloc((unsigned)(sizeof(struct msgbuf)
				- sizeof msgp->mtext + maxmsgsz));
	if (msgp == NULL) {
		(void) fprintf(stderr, "msgop: %s %d byte messages.¥n",
				"could not allocate message buffer for", maxmsgsz);
		exit(1);
	}
	/* ユーザが終了を望むまで、
		メッセージ操作をループする */
	while (choice = ask()) {
		switch (choice) {
		case 1: /*  msgsnd(): 引数を取得して呼び出しを行い、
				結果を報告する */
			(void) fprintf(stderr, "Valid msgsnd message %s¥n",
				"types are positive integers.");
			(void) fprintf(stderr, "Enter msgp->mtype: ");
			(void) scanf("%li", &msgp->mtype);
			if (maxmsgsz) {
				/* scanf を使用しているので、
				   メッセージテキストの読み取りを開始する前に、
			      入力されたメッセージタイプ行の入力の残りを捨てるため	
				   下のループが必要 */
				while ((c = getchar()) != '¥n' && c != EOF);
				(void) fprintf(stderr, "Enter a %s:¥n",
								"one line message");
				for (i = 0; ((c = getchar()) != '¥n'); i++) {
					if (i >= maxmsgsz) {
						(void) fprintf(stderr, "¥n%s¥n", full_buf);
						while ((c = getchar()) != '¥n');
						break;
					}
					msgp->mtext[i] = c;
				}
				msgsz = i;
			} else
				msgsz = 0;
			(void) fprintf(stderr,"¥nMeaningful msgsnd flag is:¥n");
			(void) fprintf(stderr, "¥tIPC_NOWAIT =¥t%#8.8o¥n",
				IPC_NOWAIT);
			(void) fprintf(stderr, "Enter msgflg: ");
			(void) scanf("%i", &msgflg);
			(void) fprintf(stderr, "%s(%d, msgp, %d, %#o)¥n",
				"msgop: Calling msgsnd", msqid, msgsz, msgflg);
			(void) fprintf(stderr, "msgp->mtype = %ld¥n",
							msgp->mtype);
			(void) fprintf(stderr, "msgp->mtext = ¥"");
			for (i = 0; i < msgsz; i++)
				(void) fputc(msgp->mtext[i], stderr);
				(void) fprintf(stderr, "¥"¥n");
				rtrn = msgsnd(msqid, msgp, msgsz, msgflg);
				if (rtrn == -1)
					perror("msgop: msgsnd failed");
				else
					(void) fprintf(stderr,
								"msgop: msgsnd returned %d¥n", rtrn);
				break;
		case 2: /* msgrcv(): 引数を取得して呼び出しを行い、
				     結果を報告する */
			for (msgsz = -1; msgsz < 0 || msgsz > maxmsgsz;
						(void) scanf("%i", &msgsz))
				(void) fprintf(stderr, "%s (0 <= msgsz <= %d): ",
								"Enter msgsz", maxmsgsz);
			(void) fprintf(stderr, "msgtyp meanings:¥n");
			(void) fprintf(stderr, "¥t 0 %s¥n", first_on_queue);
			(void) fprintf(stderr, "¥t>0 %s of given type¥n",
							first_on_queue);
			(void) fprintf(stderr, "¥t<0 %s with type <= |msgtyp|¥n",
							first_on_queue);
			(void) fprintf(stderr, "Enter msgtyp: ");
			(void) scanf("%li", &msgtyp);
			(void) fprintf(stderr,
							"Meaningful msgrcv flags are:¥n");
			(void) fprintf(stderr, "¥tMSG_NOERROR =¥t%#8.8o¥n",
							MSG_NOERROR);
			(void) fprintf(stderr, "¥tIPC_NOWAIT =¥t%#8.8o¥n",
							IPC_NOWAIT);
			(void) fprintf(stderr, "Enter msgflg: ");
			(void) scanf("%i", &msgflg);
			(void) fprintf(stderr, "%s(%d, msgp, %d, %ld, %#o);¥n",
							"msgop: Calling msgrcv", msqid, msgsz,
							msgtyp, msgflg);
			rtrn = msgrcv(msqid, msgp, msgsz, msgtyp, msgflg);
			if (rtrn == -1)
				perror("msgop: msgrcv failed");
			else {
				(void) fprintf(stderr, "msgop: %s %d¥n",
								"msgrcv returned", rtrn);
				(void) fprintf(stderr, "msgp->mtype = %ld¥n",
								msgp->mtype);
				(void) fprintf(stderr, "msgp->mtext is: ¥"");
				for (i = 0; i < rtrn; i++)
					(void) fputc(msgp->mtext[i], stderr);
				(void) fprintf(stderr, "¥"¥n");
			}
			break;
		default:
			(void) fprintf(stderr, "msgop: operation unknown¥n");
			break;
		}
	}
	exit(0);
}

/*
 *	次に何を行うかをユーザに尋ねる。ユーザの選択コードを戻す。
 *	ユーザが有効な選択肢を選択するまで復帰しない
 */
static
ask()
{
	int	response;	/* ユーザの応答 */

	do {
		(void) fprintf(stderr, "Your options are:¥n");
		(void) fprintf(stderr, "¥tExit =¥t0 or Control-D¥n");
		(void) fprintf(stderr, "¥tmsgsnd =¥t1¥n");
		(void) fprintf(stderr, "¥tmsgrcv =¥t2¥n");
		(void) fprintf(stderr, "Enter your choice: ");

		/* 応答を事前に設定するので "^D" は終了と解釈される */
		response = 0;
		(void) scanf("%i", &response);
	} while (response < 0 || response > 2);

	return(response);
} 


例 A-8 semget(2) を使用したプログラム

/*
 * semget.c: semget() 関数の例を示す。
 *
 * これは semget() 関数の簡単な例である。このプログラムは、
 * 引数を要求し、呼び出しを行い、結果を報告する
*/

#include			<stdio.h>
#include			<sys/types.h>
#include			<sys/ipc.h>
#include			<sys/sem.h>

extern void				exit();
extern void				perror();

main()
{
	key_t		key;			/* semget() に渡すキー */
	int		semflg;			/* semget() に渡す semflg */
	int		nsems;			/* semget() に渡す nsems */
	int		semid;			/* semget() からの戻り値 */

	(void) fprintf(stderr,
		"All numeric input must follow C conventions:¥n");
	(void) fprintf(stderr,
		"¥t0x... is interpreted as hexadecimal,¥n");
	(void) fprintf(stderr, "¥t0... is interpreted as octal,¥n");
	(void) fprintf(stderr, "¥totherwise, decimal.¥n");
	(void) fprintf(stderr, "IPC_PRIVATE == %#lx¥n", IPC_PRIVATE);
	(void) fprintf(stderr, "Enter key: ");
	(void) scanf("%li", &key);

	(void) fprintf(stderr, "Enter nsems value: ");
	(void) scanf("%i", &nsems);
	(void) fprintf(stderr, "¥nExpected flags for semflg are:¥n");
	(void) fprintf(stderr, "¥tIPC_EXCL = ¥t%#8.8o¥n", IPC_EXCL);
	(void) fprintf(stderr, "¥tIPC_CREAT = ¥t%#8.8o¥n", IPC_CREAT);
	(void) fprintf(stderr, "¥towner read = ¥t%#8.8o¥n", 0400);
	(void) fprintf(stderr, "¥towner alter = ¥t%#8.8o¥n", 0200);
	(void) fprintf(stderr, "¥tgroup read = ¥t%#8.8o¥n", 040);
	(void) fprintf(stderr, "¥tgroup alter = ¥t%#8.8o¥n", 020);
	(void) fprintf(stderr, "¥tother read = ¥t%#8.8o¥n", 04);
	(void) fprintf(stderr, "¥tother alter = ¥t%#8.8o¥n", 02);
	(void) fprintf(stderr, "Enter semflg value: ");
	(void) scanf("%i", &semflg);
	(void) fprintf(stderr, "¥nsemget: Calling semget(%#lx, %
				 %#o)¥n",key, nsems, semflg);
	if ((semid = semget(key, nsems, semflg)) == -1) {
		perror("semget: semget failed");
		exit(1);
	} else {
		(void) fprintf(stderr, "semget: semget succeeded: semid = %d¥n",
			semid);
		exit(0);
	}
}


例 A-9 semctl(2) を使用したプログラム

/*
 * semctl.c: semctl() 関数の例を示す。
 *
 * これは semctl() 関数の簡単な例である。
 * このプログラムによって、1 つの制御操作を 1 つのセマフォのセットに
 * 実行できる。制御操作が失敗するとただちに処理を中止するため、
 * アクセス権を読み取り権なしの設定にしないように注意が必要である。
 * このコードでアクセス権を再設定できない
 */

#include				<stdio.h>
#include				<sys/types.h>
#include				<sys/ipc.h>
#include				<sys/sem.h>
#include				<time.h>

struct semid_ds semid_ds;

static void		 do_semctl();
static void 		do_stat();
extern char		 *malloc();
extern void	 	exit();
extern void	 	perror();

char				warning_message[] = "If you remove read permission¥
				for yourself, this program will fail frequently!";

main()
{
	union semun				arg;				/* semctl() に渡す共用体 */
	int				cmd,				/* semctl() に指定するコマンド */
					i,				/* 作業領域 */
					semid,				/* semctl() に渡す semid */
					semnum;				/* semctl() に渡す semnum */

	(void) fprintf(stderr,
				"All numeric input must follow C conventions:¥n");
	(void) fprintf(stderr,
				"¥t0x... is interpreted as hexadecimal,¥n");
	(void) fprintf(stderr, "¥t0... is interpreted as octal,¥n");
	(void) fprintf(stderr, "¥totherwise, decimal.¥n");
	(void) fprintf(stderr, "Enter semid value: ");
	(void) scanf("%i", &semid);

	(void) fprintf(stderr, "Valid semctl cmd values are:¥n");
	(void) fprintf(stderr, "¥tGETALL = %d¥n", GETALL);
	(void) fprintf(stderr, "¥tGETNCNT = %d¥n", GETNCNT);
	(void) fprintf(stderr, "¥tGETPID = %d¥n", GETPID);
	(void) fprintf(stderr, "¥tGETVAL = %d¥n", GETVAL);
	(void) fprintf(stderr, "¥tGETZCNT = %d¥n", GETZCNT);
	(void) fprintf(stderr, "¥tIPC_RMID = %d¥n", IPC_RMID);
	(void) fprintf(stderr, "¥tIPC_SET = %d¥n", IPC_SET);
	(void) fprintf(stderr, "¥tIPC_STAT = %d¥n", IPC_STAT);
	(void) fprintf(stderr, "¥tSETALL = %d¥n", SETALL);
	(void) fprintf(stderr, "¥tSETVAL = %d¥n", SETVAL);
	(void) fprintf(stderr, "¥nEnter cmd: ");
	(void) scanf("%i", &cmd);
	
	/* 複数のコマンドで必要な設定操作を行う */
	switch (cmd) {
		case GETVAL:
		case SETVAL:
		case GETNCNT:
		case GETZCNT:
			/* これらのコマンドにセマフォ番号を取得する */
			(void) fprintf(stderr, "¥nEnter semnum value: ");
			(void) scanf("%i", &semnum);
			break;
		case GETALL:
		case SETALL:
			/* セマフォ値のためのバッファを割り当てる */
			(void) fprintf(stderr,
				"Get number of semaphores in the set.¥n");
			arg.buf = &semid_ds;
			do_semctl(semid, 0, IPC_STAT, arg);
			if (arg.array =
				(ushort *)malloc((unsigned)
					(semid_ds.sem_nsems * sizeof(ushort)))) {
				/* 必要なものを得た場合は switch からぬける */
				break;
			}
			(void) fprintf(stderr,
				"semctl: unable to allocate space for %d values¥n",
				semid_ds.sem_nsems);
			exit(2);
	}

	/* 指定されたコマンドに必要な引数の残りを取得する */
	switch (cmd) {
		case SETVAL:
			/* 1 つのセマフォの値を設定する */
			(void) fprintf(stderr, "¥nEnter semaphore value: ");
			(void) scanf("%i", &arg.val);
			do_semctl(semid, semnum, SETVAL, arg);
			/* そのまま進んで結果を検証する */
			(void) fprintf(stderr,
				"Do semctl GETVAL command to verify results.¥n");
		case GETVAL:
			/* 1 つのセマフォの値を取得する */
			arg.val = 0;
			do_semctl(semid, semnum, GETVAL, arg);
			break;
		case GETPID:
			/* セマフォに対して semctl(SETVAL)、semctl(SETALL)、
			   または semop() の呼び出しが正常に終了した最後の
			   プロセスの PID を取得する */
			arg.val = 0;
			do_semctl(semid, 0, GETPID, arg);
			break;
		case GETNCNT:
			/* セマフォ値が増加するのを待っている
			   プロセス数を取得する */
			arg.val = 0;
			do_semctl(semid, semnum, GETNCNT, arg);
			break;
		case GETZCNT:
			/* セマフォ値がゼロになるのを待っている
			   プロセス数を取得する */
			arg.val = 0;
			do_semctl(semid, semnum, GETZCNT, arg);
			break;
		case SETALL:
			/* セットにあるすべてのセマフォの値を設定する */
			(void) fprintf(stderr,
							"There are %d semaphores in the set.¥n",
							semid_ds.sem_nsems);
			(void) fprintf(stderr, "Enter semaphore values:¥n");
			for (i = 0; i < semid_ds.sem_nsems; i++) {
				(void) fprintf(stderr, "Semaphore %d: ", i);
				(void) scanf("%hi", &arg.array[i]);
			}
			do_semctl(semid, 0, SETALL, arg);
			/* そのまま進んで結果を検証する */
			(void) fprintf(stderr,
				"Do semctl GETALL command to verify results.¥n");
		case GETALL:
			/* セットにあるすべてのセマフォの値を取得して
			   表示する */
			do_semctl(semid, 0, GETALL, arg);
			(void) fprintf(stderr,
							"The values of the %d semaphores are:¥n",
							semid_ds.sem_nsems);
			for (i = 0; i < semid_ds.sem_nsems; i++)
				(void) fprintf(stderr, "%d ", arg.array[i]);
			(void) fprintf(stderr, "¥n");
			break;
		case IPC_SET:
			/* モードや所有権を修正する */
			arg.buf = &semid_ds;
			do_semctl(semid, 0, IPC_STAT, arg);
			(void) fprintf(stderr, "Status before IPC_SET:¥n");
			do_stat();
			(void) fprintf(stderr, "Enter sem_perm.uid value: ");
			(void) scanf("%hi", &semid_ds.sem_perm.uid);
			(void) fprintf(stderr, "Enter sem_perm.gid value: ");
			(void) scanf("%hi", &semid_ds.sem_perm.gid);
			(void) fprintf(stderr, "%s¥n", warning_message);
			(void) fprintf(stderr, "Enter sem_perm.mode value: ");
			(void) scanf("%hi", &semid_ds.sem_perm.mode);
			do_semctl(semid, 0, IPC_SET, arg);
			/* そのまま進んで変更を検証する */
			(void) fprintf(stderr, "Status after IPC_SET:¥n");
		case IPC_STAT:
			/* 現在のステータスを取得して表示する */
			arg.buf = &semid_ds;
			do_semctl(semid, 0, IPC_STAT, arg);
			do_stat();
			break;
		case IPC_RMID:
			/* セマフォのセットを削除する */
			arg.val = 0;
			do_semctl(semid, 0, IPC_RMID, arg);
			break;
		default:
			/* 未知のコマンドを semctl に渡す */
			arg.val = 0;
			do_semctl(semid, 0, cmd, arg);
			break;
	}
	exit(0);
}

/*
 * semctl() に渡す引数の指示を表示し、semctl() を呼び出し、
 * 結果を報告する。semctl() は失敗すると復帰しない。
 * この例は、エラーを処理しないで
 * 報告するだけである
 */
static void
do_semctl(semid, semnum, cmd, arg)
union semun	 arg;
int		cmd,
		semid,
		semnum;
{
	register int						i;			/* 作業領域 */

	void) fprintf(stderr, "¥nsemctl: Calling semctl(%d, %d, %d,",
				semid, semnum, cmd);
	switch (cmd) {
		case GETALL:
			(void) fprintf(stderr, "arg.array = %#x)¥n",
							arg.array);
			break;
		case IPC_STAT:
		case IPC_SET:
			(void) fprintf(stderr, "arg.buf = %#x)¥n", arg.buf);
			break;
		case SETALL:
			(void) fprintf(stderr, "arg.array = [", arg.buf);
			for (i = 0;i < semid_ds.sem_nsems;) {
				(void) fprintf(stderr, "%d", arg.array[i++]);
				if (i < semid_ds.sem_nsems)
						(void) fprintf(stderr, ", ");
			}
			(void) fprintf(stderr, "])¥n");
			break;
		case SETVAL:
		default:
			(void) fprintf(stderr, "arg.val = %d)¥n", arg.val);
			break;
	}
	i = semctl(semid, semnum, cmd, arg);
	if (i == -1) {
		perror("semctl: semctl failed");
		exit(1);
	}
	(void) fprintf(stderr, "semctl: semctl returned %d¥n", i);
	return;
}

/*
 *	ステータス構造体の一般的に使用される部分の内容を表示する
 */
static void
do_stat()
{
	(void) fprintf(stderr, "sem_perm.uid = %d¥n",
							semid_ds.sem_perm.uid);
	(void) fprintf(stderr, "sem_perm.gid = %d¥n",
							semid_ds.sem_perm.gid);
	(void) fprintf(stderr, "sem_perm.cuid = %d¥n",
							semid_ds.sem_perm.cuid);
	(void) fprintf(stderr, "sem_perm.cgid = %d¥n",
							semid_ds.sem_perm.cgid);
	(void) fprintf(stderr, "sem_perm.mode = %#o, ",
							semid_ds.sem_perm.mode);
	(void) fprintf(stderr, "access permissions = %#o¥n",
							semid_ds.sem_perm.mode & 0777);
	(void) fprintf(stderr, "sem_nsems = %d¥n",
							semid_ds.sem_nsems);
	(void) fprintf(stderr, "sem_otime = %s", semid_ds.sem_otime ?
							ctime(&semid_ds.sem_otime) : "Not Set¥n");
	(void) fprintf(stderr, "sem_ctime = %s",
							ctime(&semid_ds.sem_ctime));
}


例 A-10 semop(2) を使用したプログラム

/*
 * semop.c: semop() 関数の例を示す。
 *
 * これは、semop() 関数の簡単な例である。
 * このプログラムによって、
 * semop() の引数の設定と呼び出しを行うことができる。
 * その後、1 つのセマフォのセットに関して繰り返して結果を報告する。
 * セマフォのセットに読み取り権を持っていなければならない。
 * 持っていないと失敗する。
 * (セットにあるセマフォ数を得るため、および semop() の呼び出しの
 * 前後に値を報告するため、読み取り権が必要)
 */

#include				<stdio.h>
#include				<sys/types.h>
#include				<sys/ipc.h>
#include				<sys/sem.h>

static int					 ask();
extern void		 		exit();
extern void				 free();
extern char				 *malloc();
extern void		 		perror();

static struct semid_ds		semid_ds;				/* セマフォのセットの状態 */

static char				error_mesg1[] = "semop: Can't allocate space for %d¥
								semaphore values. Giving up.¥n";
static char			error_mesg2[] = "semop: Can't allocate space for %d¥
								sembuf structures. Giving up.¥n";

main()
{
	register int				i;			/* 作業領域 */
	int				nsops;			/* 操作数 */
	int				semid;			/* セマフォのセットの semid */
	struct sembuf				*sops;			/* 実行する操作へのポインタ */

	(void) fprintf(stderr,
				"All numeric input must follow C conventions:¥n");
	(void) fprintf(stderr,
				"¥t0x... is interpreted as hexadecimal,¥n");
	(void) fprintf(stderr, "¥t0... is interpreted as octal,¥n");
	(void) fprintf(stderr, "¥totherwise, decimal.¥n");
	/* 呼び出し側が何もしなくなるまでループする */
	while (nsops = ask(&semid, &sops)) {
		/* 実行する操作の配列を初期化する */
		for (i = 0; i < nsops; i++) {
			(void) fprintf(stderr,
					"¥nEnter values for operation %d of %d.¥n",
							i + 1, nsops);
			(void) fprintf(stderr,
					"sem_num(valid values are 0 <= sem_num < %d): ",
					semid_ds.sem_nsems);
			(void) scanf("%hi", &sops[i].sem_num);
			(void) fprintf(stderr, "sem_op: ");
			(void) scanf("%hi", &sops[i].sem_op);
			(void) fprintf(stderr,
					"Expected flags in sem_flg are:¥n");
			(void) fprintf(stderr, "¥tIPC_NOWAIT =¥t%#6.6o¥n",
					IPC_NOWAIT);
			(void) fprintf(stderr, "¥tSEM_UNDO =¥t%#6.6o¥n",
					SEM_UNDO);
			(void) fprintf(stderr, "sem_flg: ");
			(void) scanf("%hi", &sops[i].sem_flg);
		}

		/* 呼び出し時の内容を表示する */
		(void) fprintf(stderr,
					"¥nsemop: Calling semop(%d, &sops, %d) with:",
					semid, nsops);
		for (i = 0; i < nsops; i++)
		{
			(void) fprintf(stderr, "¥nsops[%d].sem_num = %d, ", i,
						sops[i].sem_num);
			(void) fprintf(stderr, "sem_op = %d, ", sops[i].sem_op);
			(void) fprintf(stderr, "sem_flg = %#o¥n",
						sops[i].sem_flg);
		}

		/* semop() 呼び出しを実行して結果を報告する */
		if ((i = semop(semid, sops, nsops)) == -1) {
			perror("semop: semop failed");
		} else {
			(void) fprintf(stderr, "semop: semop returned %d¥n", i);
		}
	}
}

/*
 * ユーザに続けたいかを尋ねる。
 *
 * 最初の呼び出しで
 *	処理する semid を取得して呼び出し側に返す。
 * 各呼び出しで
 * 	1. 現在のセマフォ値を表示する。
 * 	2. 次の semop の呼び出しで何回操作を実行するかをユーザに尋ねる。
 * 	   ジョブに十分な sembuf 構造体の配列を割り当て、
 * 	   呼び出し側が指定したポインタをその配列に設定する。
 * 	   (配列が十分に大きい場合は、後続の呼び出しで再使用する。
 * 	   十分に大きくない場合は配列を解放して、より大きい配列
 * 	   を割り当てる)
 */
static
ask(semidp, sopsp)
int			*semidp;		/* semid へのポインタ (最初だけ使用する) */
struct sembuf			**sopsp;
{
	static union semun					arg;		/* semctl への引数 */
	int					i;		/* 作業領域 */
	static int					nsops = 0;		/* 現在割り当てられている
									  sembuf 配列の大きさ */
	static int					semid = -1;			/* ユーザが指定した semid */
	static struct sembuf				*sops;	 			/* 割り当てられている配列へのポインタ */
	
	if (semid < 0) {
		/* 最初の呼び出し: ユーザからの semid と
		   セマフォのセットの現在の状態を取得する */
		(void) fprintf(stderr,
				"Enter semid of the semaphore set you want to use: ");
		(void) scanf("%i", &semid);
		*semidp = semid;
		arg.buf = &semid_ds;
		if (semctl(semid, 0, IPC_STAT, arg) == -1) {
			perror("semop: semctl(IPC_STAT) failed");
			/* semctl が失敗すると、semid_ds はゼロのままであるため、
			   セマフォ数の後での検査はゼロになることに注意 */
			(void) fprintf(stderr,
					"Before and after values are not printed.¥n");
		} else {
			if ((arg.array = (ushort *)malloc(
				(unsigned)(sizeof(ushort) * semid_ds.sem_nsems)))
							== NULL) {
				(void) fprintf(stderr, error_mesg1,
							semid_ds.sem_nsems);
				exit(1);
			}
		}
	}
	/* 現在のセマフォ値を表示する */
	if (semid_ds.sem_nsems) {
		(void) fprintf(stderr,
						"There are %d semaphores in the set.¥n",
						semid_ds.sem_nsems);
		if (semctl(semid, 0, GETALL, arg) == -1) {
			perror("semop: semctl(GETALL) failed");
		} else {
			(void) fprintf(stderr, "Current semaphore values are:");
			for (i = 0; i < semid_ds.sem_nsems;
				(void) fprintf(stderr, " %d", arg.array[i++]));
			(void) fprintf(stderr, "¥n");
		}
	}
	/* 次の呼び出しで行われる操作がいくつになるかを見つけ出し、
	   十分なスペースを割り当てる */
	(void) fprintf(stderr,
					"How many semaphore operations do you want %s¥n",
					"on the next call to semop()?");
	(void) fprintf(stderr, "Enter 0 or control-D to quit: ");
	i = 0;
	if (scanf("%i", &i) == EOF || i == 0)
		exit(0);
	if (i > nsops) {
		if (nsops)
			free((char *)sops);
		nsops = i;
		if ((sops = (struct sembuf *)malloc((unsigned)(nsops *
			sizeof(struct sembuf)))) == NULL) {
			(void) fprintf(stderr, error_mesg2, nsops);
			exit(2);
		}
	}
	*sopsp = sops;
	return (i);
}


例 A-11 shmget(2) を使用したプログラム

/*
 * shmget.c: shmget() 関数の例を示す。
 *
 *	これは shmget() 関数の簡単な例である。
 * このプログラムは、引数を要求し、呼び出しを行い、結果を報告する
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

extern void				exit();
extern void				perror();

main()
{
	key_t		key;			/* shmget() に渡すキー */
	int		shmflg;			/* shmget() に渡す shmflg */
	int		shmid;			/* shmget() からの戻り値 */
	int		size;			/* shmget() に渡す大きさ */

	(void) fprintf(stderr,
		"All numeric input is expected to follow C conventions:¥n");
	(void) fprintf(stderr,
				"¥t0x... is interpreted as hexadecimal,¥n");
	(void) fprintf(stderr, "¥t0... is interpreted as octal,¥n");
	(void) fprintf(stderr, "¥totherwise, decimal.¥n");

	/* キーを取得する */
	(void) fprintf(stderr, "IPC_PRIVATE == %#lx¥n", IPC_PRIVATE);
	(void) fprintf(stderr, "Enter key: ");
	(void) scanf("%li", &key);

	/* セグメントの大きさを取得する */
	(void) fprintf(stderr, "Enter size: ");
	(void) scanf("%i", &size);

	/* shmflg 値を取得する */
	(void) fprintf(stderr,
				"Expected flags for the shmflg argument are:¥n");
	(void) fprintf(stderr, "¥tIPC_CREAT = ¥t%#8.8o¥n", IPC_CREAT);
	(void) fprintf(stderr, "¥tIPC_EXCL = ¥t%#8.8o¥n", IPC_EXCL);
	(void) fprintf(stderr, "¥towner read =¥t%#8.8o¥n", 0400);
	(void) fprintf(stderr, "¥towner write =¥t%#8.8o¥n", 0200);
	(void) fprintf(stderr, "¥tgroup read =¥t%#8.8o¥n", 040);
	(void) fprintf(stderr, "¥tgroup write =¥t%#8.8o¥n", 020);
	(void) fprintf(stderr, "¥tother read =¥t%#8.8o¥n", 04);
	(void) fprintf(stderr, "¥tother write =¥t%#8.8o¥n", 02);
	(void) fprintf(stderr, "Enter shmflg: ");
	(void) scanf("%i", &shmflg);

	/* 呼び出しを行い、結果を報告する */
	(void) fprintf(stderr,
					"shmget: Calling shmget(%#lx, %d, %#o)¥n",
					key, size, shmflg);
	if ((shmid = shmget (key, size, shmflg)) == -1) {
		perror("shmget: shmget failed");
		exit(1);
	} else {
		(void) fprintf(stderr,
					"shmget: shmget returned %d¥n", shmid);
		exit(0);
	}
}


例 A-12 shmctl(2) を使用したプログラム

/*
 * shmctl.c: shmctl() 関数の例を示す。
 *
 * これは shmctl() 関数の簡単な例である。
 * このプログラムによって、1 つの制御操作を
 * 1 つの共用メモリセグメントに実行できる。
 * (一部の操作はユーザの要求に関係なく行われる。
 * 制御操作が失敗すると、すぐに処理を中止する。
 * 読み取り権のないアクセス権を設定しないように注意。
 * このコードでアクセス権の再設定はできない)
*/

#include			<stdio.h>
#include			<sys/types.h>
#include			<sys/ipc.h>
#include			<sys/shm.h>
#include			<time.h>
static void			do_shmctl();
extern void			exit();
extern void			perror();

main()
{
	int		cmd;		/* shmctl() のコマンドコード */
	int		shmid;		/* セグメント ID */
	struct shmid_ds		shmid_ds;					/* 結果を保持するための
									共用メモリのデータ構造体 */

	(void) fprintf(stderr,
		"All numeric input is expected to follow C conventions:¥n");
	(void) fprintf(stderr,
					"¥t0x... is interpreted as hexadecimal,¥n");
	(void) fprintf(stderr, "¥t0... is interpreted as octal,¥n");
	(void) fprintf(stderr, "¥totherwise, decimal.¥n");

	/* shmid と cmd を取得する */
	(void) fprintf(stderr,
					"Enter the shmid for the desired segment: ");
	(void) scanf("%i", &shmid);
	(void) fprintf(stderr, "Valid shmctl cmd values are:¥n");
	(void) fprintf(stderr, "¥tIPC_RMID =¥t%d¥n", IPC_RMID);
	(void) fprintf(stderr, "¥tIPC_SET =¥t%d¥n", IPC_SET);
	(void) fprintf(stderr, "¥tIPC_STAT =¥t%d¥n", IPC_STAT);
	(void) fprintf(stderr, "¥tSHM_LOCK =¥t%d¥n", SHM_LOCK);
	(void) fprintf(stderr, "¥tSHM_UNLOCK =¥t%d¥n", SHM_UNLOCK);
	(void) fprintf(stderr, "Enter the desired cmd value: ");
	(void) scanf("%i", &cmd);

	switch (cmd) {
		case IPC_STAT:
			/* 共用メモリセグメントの状態を取得する */
			break;
		case IPC_SET:
			/* 所有者の UID および GID とアクセス権を設定する */
			/* 現在値を取得して表示する */
			do_shmctl(shmid, IPC_STAT, &shmid_ds);
			/* 読み込む UID、GID、およびアクセス権を設定する */
			(void) fprintf(stderr, "¥nEnter shm_perm.uid: ");
			(void) scanf("%hi", &shmid_ds.shm_perm.uid);
			(void) fprintf(stderr, "Enter shm_perm.gid: ");
			(void) scanf("%hi", &shmid_ds.shm_perm.gid);
			(void) fprintf(stderr,
				"Note: Keep read permission for yourself.¥n");
			(void) fprintf(stderr, "Enter shm_perm.mode: ");
			(void) scanf("%hi", &shmid_ds.shm_perm.mode);
			break;
		case IPC_RMID:
			/* 最後の接続点が切り離されたらセグメントを削除する */
			break;
		case SHM_LOCK:
			/* 共用メモリセグメントをロッキングする */
			break;
		case SHM_UNLOCK:
			/* 共用メモリセグメントのロッキングを解除する */
			break;
		default:
			/* 未知のコマンドが shmctl に渡される */
			break;
	}
	do_shmctl(shmid, cmd, &shmid_ds);
	exit(0);
}

/*
 * shmctl() に渡す引数を表示し、shmctl() を呼び出し、
 * 結果を報告する。shmctl() は、失敗すると復帰しない。
 * この例は、エラーを処理しないで報告するだけである
 */
static void
do_shmctl(shmid, cmd, buf)
int			shmid,			/* 接続点 */
			cmd;			/* コマンドコード */
struct shmid_ds			*buf;		/* 共用メモリのデータ構造体へのポインタ */
{
	register int				rtrn;		/* 保持領域 */

	(void) fprintf(stderr, "shmctl: Calling shmctl(%d, %d, buf)¥n",
		shmid, cmd);
	if (cmd == IPC_SET) {
		(void) fprintf(stderr, "¥tbuf->shm_perm.uid == %d¥n",
					buf->shm_perm.uid);
		(void) fprintf(stderr, "¥tbuf->shm_perm.gid == %d¥n",
					buf->shm_perm.gid);
		(void) fprintf(stderr, "¥tbuf->shm_perm.mode == %#o¥n",
					buf->shm_perm.mode);
	}
	if ((rtrn = shmctl(shmid, cmd, buf)) == -1) {
		perror("shmctl: shmctl failed");
		exit(1);
	} else {
		(void) fprintf(stderr,
						"shmctl: shmctl returned %d¥n", rtrn);
	}
	if (cmd != IPC_STAT && cmd != IPC_SET)
		return;

	/* 現在の状態を表示する */
	(void) fprintf(stderr, "¥nCurrent status:¥n");
	(void) fprintf(stderr, "¥tshm_perm.uid = %d¥n",
						buf->shm_perm.uid);
	(void) fprintf(stderr, "¥tshm_perm.gid = %d¥n",
						buf->shm_perm.gid);
	(void) fprintf(stderr, "¥tshm_perm.cuid = %d¥n",
						buf->shm_perm.cuid);
	(void) fprintf(stderr, "¥tshm_perm.cgid = %d¥n",
						buf->shm_perm.cgid);
	(void) fprintf(stderr, "¥tshm_perm.mode = %#o¥n",
						buf->shm_perm.mode);
	(void) fprintf(stderr, "¥tshm_perm.key = %#x¥n",
						buf->shm_perm.key);
	(void) fprintf(stderr, "¥tshm_segsz = %d¥n", buf->shm_segsz);
	(void) fprintf(stderr, "¥tshm_lpid = %d¥n", buf->shm_lpid);
	(void) fprintf(stderr, "¥tshm_cpid = %d¥n", buf->shm_cpid);
	(void) fprintf(stderr, "¥tshm_nattch = %d¥n", buf->shm_nattch);
	(void) fprintf(stderr, "¥tshm_atime = %s",
						buf->shm_atime ? ctime(&buf->shm_atime) : "Not Set¥n");
	(void) fprintf(stderr, "¥tshm_dtime = %s",
						buf->shm_dtime ? ctime(&buf->shm_dtime) : "Not Set¥n");
	(void) fprintf(stderr, "¥tshm_ctime = %s",
						ctime(&buf->shm_ctime));
} 


例 A-13 shmat(2)shmdt(2) を使用したプログラム

/*
 * shmop.c: shmat() 関数と shmdt() 関数の例を示す。
 *
 * これは、shmat() および shmdt() システムコールの簡単な例である。
 * このプログラムによって、セグメントの接続と切り離しを行い、
 * 接続セグメントとの間で文字列の書き込みと読み取りを行うことができる
 */

#include			<stdio.h>
#include			<setjmp.h>
#include			<signal.h>
#include			<sys/types.h>
#include			<sys/ipc.h>
#include			<sys/shm.h>

#define			MAXnap		4	/* 並列接続の最大数 */

static			ask();
static void		catcher();
extern void		exit();
static			good_addr();
extern void		perror();
extern char		*shmat();

static struct state					{	/* 現在接続されているセグメントの
									内部レコード */
	int		shmid;			/* 接続されているセグメントの shmid */
	char		*shmaddr;			/* 接続点 */
	int		shmflg;			/* 接続で使用されるフラグ */
}	ap[MAXnap];					/* 現在接続されているセグメントの状態 */

static int				nap;		/* 現在接続されているセグメント数 */
static jmp_buf			segvbuf;			/* SIGSEGV キャッチングのための
						   プロセス状態の保存領域 */

main()
{
	register int				action;			/* 実行するアクション */
	char				*addr;			/* アドレス作業領域 */
	register int				i;			/* 作業領域 */
	register struct state			*p;				/* 現在の状態エントリへのポインタ */
	void			(*savefunc)();		/* SIGSEGV 状態の保持領域 */
	(void) fprintf(stderr,
		"All numeric input is expected to follow C conventions:¥n");
	(void) fprintf(stderr,
		"¥t0x... is interpreted as hexadecimal,¥n");
	(void) fprintf(stderr, "¥t0... is interpreted as octal,¥n");
	(void) fprintf(stderr, "¥totherwise, decimal.¥n");
	while (action = ask()) {
		if (nap) {
			(void) fprintf(stderr,
						"¥nCurrently attached segment(s):¥n");
			(void) fprintf(stderr, " shmid address¥n");
			(void) fprintf(stderr, "------ ----------¥n");
			p = &ap[nap];
			while (p-- != ap) {
				(void) fprintf(stderr, "%6d", p->shmid);
				(void) fprintf(stderr, "%#11x", p->shmaddr);
				(void) fprintf(stderr, " Read%s¥n",
					(p->shmflg & SHM_RDONLY) ?
					"-Only" : "/Write");
			}
		} else
			(void) fprintf(stderr,
				"¥nNo segments are currently attached.¥n");
		switch (action) {
		case 1:			/* 要求する shmat */
			/* 別の接続のための余地があることを検証する */
			if (nap == MAXnap) {
				(void) fprintf(stderr, "%s %d %s¥n",
							"This simple example will only allow",
							MAXnap, "attached segments.");
				break;
			}
			p = &ap[nap++];
			/* 引数を取得し、呼び出しを行い、結果を報告し、
			 *	現在の状態の配列を更新する */
			(void) fprintf(stderr,
				"Enter shmid of segment to attach: ");
			(void) scanf("%i", &p->shmid);

			(void) fprintf(stderr, "Enter shmaddr: ");
			(void) scanf("%i", &p->shmaddr);
			(void) fprintf(stderr,
				"Meaningful shmflg values are:¥n");
			(void) fprintf(stderr, "¥tSHM_RDONLY = ¥t%#8.8o¥n",
				SHM_RDONLY);
			(void) fprintf(stderr, "¥tSHM_RND = ¥t%#8.8o¥n",
				SHM_RND);
			(void) fprintf(stderr, "Enter shmflg value: ");
			(void) scanf("%i", &p->shmflg);

			(void) fprintf(stderr,
				"shmop: Calling shmat(%d, %#x, %#o)¥n",
				p->shmid, p->shmaddr, p->shmflg);
				p->shmaddr = shmat(p->shmid, p->shmaddr, p->shmflg);
			if(p->shmaddr == (char *)-1) {
				perror("shmop: shmat failed");
				nap--;
			} else {
				(void) fprintf(stderr,
					"shmop: shmat returned %#8.8x¥n", p->shmaddr);
			}
			break;

		case 2:			/* 要求する shmdt */
			/* アドレスを取得し、呼び出しを行い、結果を報告し、
			 *	内部状態の一致を行う */
			(void) fprintf(stderr,
				"Enter detach shmaddr: ");
			(void) scanf("%i", &addr);

			i = shmdt(addr);
			if(i == -1) {
				perror("shmop: shmdt failed");
			} else {
				(void) fprintf(stderr,
					"shmop: shmdt returned %d¥n", i);
				for (p = ap, i = nap; i--; p++) {
					if (p->shmaddr == addr)
						*p = ap[--nap];
				}
			}
			break;
		case 3:	/* 要求されたセグメントからの読み取り */
			if (nap == 0)
				break;

			(void) fprintf(stderr, "Enter address of an %s",
				"attached segment: ");
			(void) scanf("%i", &addr);

			if (good_addr(addr))
				(void) fprintf(stderr, "String @ %#x is `%s'¥n",
					addr, addr);
			break;

		case 4:	/* 要求されたセグメントへの書き込み */
			if (nap == 0)
				break;

			(void) fprintf(stderr, "Enter address of an %s",
				"attached segment: ");
			(void) scanf("%i", &addr);

			/* 読み取り専用の接続セグメントへの書き込みの試みをトラップする 
			 *	SIGSEGV キャッチルーチンを設定する */
			savefunc = signal(SIGSEGV, catcher);

			if (setjmp(segvbuf)) {
				(void) fprintf(stderr, "shmop: %s: %s¥n",
					"SIGSEGV signal caught",
					"Write aborted.");
			} else {
				if (good_addr(addr)) {
					(void) fflush(stdin);
					(void) fprintf(stderr, "%s %s %#x:¥n",
						"Enter one line to be copied",
						"to shared segment attached @",
						addr);
					(void) gets(addr);
				}
			}
			(void) fflush(stdin);

			/* SIGSEGV を前の状態に復元する */
			(void) signal(SIGSEGV, savefunc);
			break;
		}
	}
	exit(0);
	/* 達していない */
}
/* 次のアクションを求める */
static
ask()
{
	int		response;			/* ユーザの応答 */
	do {
			(void) fprintf(stderr, "Your options are:¥n");
			(void) fprintf(stderr, "¥t^D = exit¥n");
			(void) fprintf(stderr, "¥t 0 = exit¥n");
			(void) fprintf(stderr, "¥t 1 = shmat¥n");
			(void) fprintf(stderr, "¥t 2 = shmdt¥n");
			(void) fprintf(stderr, "¥t 3 = read from segment¥n");
			(void) fprintf(stderr, "¥t 4 = write to segment¥n");
			(void) fprintf(stderr,
				"Enter the number corresponding to your choice: ");

			/* 応答を事前設定するので "^D" は終了と解釈される */
			response = 0;
			(void) scanf("%i", &response);
	} while (response < 0 || response > 4);
	return (response);
}
/*
 * SHM_RDONLY フラグセットに接続された共用メモリセグメントへの
 * 書き込みの試みによって発生するキャッチシグナル
 */
/* 使用されている引数 */
static void
catcher(sig)
{
	longjmp(segvbuf, 1);
	/* 達していない */
}
/*
 * 指定されたアドレスが接続されているセグメントの
 * アドレスであることを検証する。
 * アドレスが有効な場合は 1 を戻し、無効な場合は 0 を戻す
 */
static
good_addr(address)
char	*address;
{
	register struct state	*p;		/* 接続されているセグメントへのポインタ */

	for (p = ap; p != &ap[nap]; p++)
			if (p->shmaddr == address)
				return(1);
	return(0);
}

例 A-14 は、リスト要素レコードとしてファイルに格納される二重リンクトリストへのエントリの挿入例を示しています。たとえば、その後に新しいレコードが挿入されるレコードがすでに読み取りロッキングを持っていると想定します。このレコードへのロッキングは、そのレコードを編集できるようにロッキングを変更するか、書き込みロッキングに強化しなければなりません。

ロッキングの強化 (通常は読み取りロッキングから書き込みロッキング) は、他のプロセスがファイルの同じセクションに読み取りロッキングを保持していない場合に認められます。保留状態の書き込みロッキングを持つプロセスがファイルの同じセクションで休眠しているときは、ロッキングの強化は成功し、他の (休眠している) ロッキングは待ちます。書き込みロッキングから読み取りロッキングへの変更には制約はありません。いずれの場合にも、ロッキングは新しいロッキングタイプで再設定されるだけです。lockf(3C) 関数は読み取りロッキングを持っていないので、ロッキングの強化はその呼び出しには適用されません。

これら 3 つのレコードへのロッキングは、他のプロセスがその設定をブロッキングしている場合に待ち (休眠) に設定されます。これは、F_SETLKW コマンドで行います。代わりに F_SETLK コマンドを使用すると、fcntl(2) 関数はブロッキングされていると失敗します。その後プログラムは、各エラー復帰セクションでブロッキングされた状態を処理するように変更しなければなりません。


例 A-14 ロッキング強化したレコードのロッキング

	struct record {
...				/* レコードのデータ部分 */
	off_t prev;				/* リスト内の前のレコードへのインデックス */
	off_t next;				/* リスト内の次のレコードへのインデックス */
};

/* fcntl(2) を使用したロッキングの強化
 * このルーチンに入るときは、
 *	    "here" と "next" に読み取りロッキングがあると想定されている。
 *	    "here" と "next" に書き込みロッキングが得られると、
 *          書き込みロッキングを "this" に設定する。
 *          インデックスを "this" レコードに戻す。
 * 書き込みロッキングが得られない場合は、
 *	    読み取りロッキングを "here" と "next" に復元する。
 *	    その他のすべてのロッキングを解除する。
 *	    -1 を戻す
 */

off_t
set3lock (this, here, next)
off_t this, here, next;
{
	struct flock lck;
	lck.l_type = F_WRLCK;	/* 書き込みロッキングの設定 */
	lck.l_whence = 0;	/* ファイルの先頭からのオフセット l_start */
	lck.l_start = here;
	lck.l_len = sizeof(struct record);

	/* "here" のロッキングを書き込みロッキングに強化する */
	if (fcntl(fd, F_SETLKW, &lck) < 0) {
		    return (-1);
	}
	/* "this" を書き込みロッキングでロッキングする */
	lck.l_start = this;
	if (fcntl(fd, F_SETLKW, &lck) < 0) {
		/* "this" のロッキングの失敗で "here" のロッキングを読み取りロッキングに下げる */
		lck.l_type = F_RDLCK;
		lck.l_start = here;
		(void) fcntl(fd, F_SETLKW, &lck);
		return (-1);
	/* "next" のロッキングを書き込みロッキングに強化する */
	lck.l_start = next;
	if (fcntl(fd, F_SETLKW, &lck) < 0) {
		/* "next" のロッキングの失敗で "here" のロッキングを読み取りロッキングに下げ、*/
		lck.l_type = F_RDLCK;
		lck.l_start = here;
		(void) fcntl(fd, F_SETLK, &lck);
		/* "this" のロッキングを解除する */
		lck.l_type = F_UNLCK;
		lck.l_start = this;
		(void) fcntl(fd, F_SETLK, &lck);
		return (-1);	/* ロッキングを設定できず、再試行するか終了する */
	}

	return (this);
}


例 A-15 lockf(3C) を使用したレコードの書き込みロッキング

/* lockf(3C)
 * このルーチンに入るときは、"here" と "next" にロッキングはないと
 * 想定されている。ロッキングが得られると、"this" にロッキングを設定し、
 * インデックスを "this" レコードに戻す。ロッキングが得られないと、
 * 他のすべてのロッキングを解除し、-1 を戻す
 */
#include <unistd.h>

long
set3lock (this, here, next)
long this, here, next;
{
	/* "here" をロッキングする */
	(void) lseek(fd, here, 0);
	if (lockf(fd, F_LOCK, sizeof(struct record)) < 0) {
		return (-1);
	}
	/* "this" をロッキングする */
	(void) lseek(fd, this, SEEK_SET);
	if (lockf(fd, F_LOCK, sizeof(struct record)) < 0) {
		/* "this" のロッキングが失敗した。"here" のロッキングを解除する */
		(void) lseek(fd, here, 0);
		(void) lockf(fd, F_ULOCK, sizeof(struct record));
		return (-1);
	}
	/* "next" をロッキングする */
	(void) lseek(fd, next, 0);
	if (lockf(fd, F_LOCK, sizeof(struct record)) < 0) {
		/* "next" のロッキングが失敗した。"here" のロッキングを解除する */
		(void) lseek(fd, here, 0);
		(void) lockf(fd, F_ULOCK, sizeof(struct record));
		/* "this" のロッキングを解除する */
		(void) lseek(fd, this, 0);
		(void) lockf(fd, F_ULOCK, sizeof(struct record));
		return (-1);				/* ロッキングを設定できず、再試行するか終了する */
	}
	return (this);
}