Linuxでは、含まれているプログラムは次のselect
場所から返され、終了します。
$ gcc -Wall -Wextra select_test.c -o select_test
$ ./select_test
reading from read end
closing write end
first read returned 0
second read returned 0
selecting with read fd in fdset
select returned
OS Xではselect
永久にブロックされ、プログラムは決して終了しません。 Linuxは期待どおりに機能し、POSIXのマニュアルページの次の選択セクションに従うようです。
O_NONBLOCK がクリアされた入力関数への呼び出しがブロックされない場合、記述子は関数がデータを正常に送信したかどうかにかかわらず読み取る準備ができたと見なされます。 (関数は、データ、ファイルの終わりの表示、またはブロックされたことを示すエラーを返すことがあります。それぞれの場合、記述子を読み取る準備ができたと見なす必要があります。)
fifoの読み取り側のread(2)は常にを返すので、EOF
私の読み取りによると、常に選択によって準備されたと見なされるべきです。
macOSの動作はよく知られているか予想されますか?この例では、行動の違いに寄与する他の要因がありますか?
read
これらの呼び出しを削除すると、macOSがselect
置き換えられます。この実験とは異なる実験では、ファイルからEOFを読み取ると、後で呼び出しても準備ができているとマークされなくなるselect
ことを示しているようです。
サンプルプログラム
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define FILENAME "select_test_tmp.fifo"
int main()
{
pid_t pid;
int r_fd, w_fd;
unsigned char buffer[10];
fd_set readfds;
mkfifo(FILENAME, S_IRWXU);
pid = fork();
if (pid == -1)
{
perror("fork");
exit(1);
}
if (pid == 0)
{
w_fd = open(FILENAME, O_WRONLY);
if (w_fd == -1)
{
perror("open");
exit(1);
}
printf("closing write end\n");
close(w_fd);
exit(0);
}
r_fd = open(FILENAME, O_RDONLY);
if (r_fd == -1)
{
perror("open");
exit(1);
}
printf("reading from read end\n");
if (read(r_fd, &buffer, 10) == 0)
{
printf("first read returned 0\n");
}
else
{
printf("first read returned non-zero\n");
}
if (read(r_fd, &buffer, 10) == 0)
{
printf("second read returned 0\n");
}
else
{
printf("second read returned non-zero\n");
}
FD_ZERO(&readfds);
FD_SET(r_fd, &readfds);
printf("selecting with read fd in fdset\n");
if (select(r_fd + 1, &readfds, NULL, NULL, NULL) == -1)
{
perror("select");
exit(1);
}
printf("select returned\n");
unlink(FILENAME);
exit(0);
}
答え1
Macintoshでは、この関数はパイプをソケットと同じように扱いますread
。この動作は、read
ファイルにアクセスしようselect_test_tmp.fifo
とし、無料の入力がある限りブロックされるために発生します。デフォルトでは、EOFは各書き込み操作の後にパイプに書き込まれます。
これを確認する1つの方法は、cat select_test_tmp.fifo
コマンドラインから実行することです。最初に終了しない限り、返す前に入力を受け取るのを待ちます。
答え2
あなたの家が間違っています。
予想される動作は、すべての作成者が保持しているFIFOの書き込み側を閉じると、読み取り側でread
0バイトを返す呼び出しを提供することです。その後はextraを呼び出すべきではありませんread
。これを行うには、読み取り側を閉じて再度開く必要があり、パイプの場合は再作成する必要があります。
この規則によれば、プラットフォーム全体で一貫した動作を得ることができます。
以下は、両方のプラットフォームで正しく実行されるプログラムに触発されたサンプルプログラムです。
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FILENAME "select_test_tmp.fifo"
int main()
{
pid_t pid;
int r_fd, w_fd;
unsigned char buffer[8192];
fd_set master_set, working_set;
char const * message = "Hello there\n";
mkfifo(FILENAME, S_IRWXU);
pid = fork();
if (pid < 0) exit(1);
if (pid == 0) {
w_fd = open(FILENAME, O_WRONLY);
if (w_fd == -1) exit(1);
write(w_fd, message, strlen(message));
close(w_fd);
exit(0);
}
r_fd = open(FILENAME, O_RDONLY);
if (r_fd < 0) exit(1);
FD_ZERO(&master_set);
FD_SET(r_fd, &master_set);
int finished = 0;
while (!finished) {
memcpy(&working_set, &master_set, sizeof(master_set));
int rc = select(r_fd + 1, &working_set, NULL, NULL, NULL);
if (rc < 1) exit(1); // No timeout so rc == 0 is also an error here
for (int fd = 0; fd < r_fd +1; ++fd) {
if (FD_ISSET(fd, &working_set)) {
if (fd == r_fd) { // Our fifo
// Read data and print it in stdout
ssize_t nb_bytes = read(r_fd, buffer, 8192);
if (nb_bytes < 0) {
exit(1);
}
else if (0 == nb_bytes) {
finished = 1;
}
else {
write(1,buffer,nb_bytes);
}
}
}
}
}
unlink(FILENAME);
exit(0);
}