C - > Shell - 写入直到读取时阻塞

时间:2017-07-07 15:34:59

标签: c shell

我想将一个c程序中的字符发送到shell程序。我正在使用命名管道在需要时发送字母'a'。我只需要打开一次管道。这是一个例子:

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main(){
    int fd;
    mkfifo("/tmp/test", 0666);
    fd = open("/tmp/test", O_WRONLY);
    printf("Opened\n");
    char * a = "a";
    while(1){
            printf("Writing to pipe...\n");
            write(fd,a,1);
            sleep(1);
    }
}

shell会根据需要多次执行此命令...

head -c 1 /tmp/test

问题是在一个头之后,即使没有人在那里,c也会无休止地流入管道。

我注意到open()会阻塞,直到有人在另一端。在有人正在阅读之前,如何告诉write()阻止?

我宁愿在write()上使用read()而不是read(),因为我认为为每个请求打开文件会有很多开销。

谢谢!

更新

这就是我在Java中处理它的方式,它一直等到有人在继续之前监听这个管道。也许只是因为它是一种更高级别的语言。

public static void writeToPipe(int val, String pipename){
    try{
        pipe_out = new PrintStream("/tmp/" + pipename);
    }catch(Exception e){
        System.out.println("Could not open a pipe for output!");
        e.printStackTrace();
    }
    try{
        pipe_out.println(val);
    }catch(Exception e){
        System.out.println("Could not write to pipe!");
        e.printStackTrace();
    }

    try{
        pipe_out.close();
    }catch(Exception e){
        System.out.println("Could not close the output pipe!");
        e.printStackTrace();
    }
}

更新#2 - 这是解决方案

这是我的代码基于David的想法,它很粗糙,但它有效。我没有检查命名管道是否存在,只是在戒烟时压制它。

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char **argv){
    mkfifo("/tmp/test", 0666);
    while(1){
      int fd, status;
            if ((fd = open ("/tmp/test", O_WRONLY)) == -1) {
         perror ("open failed");
         return 1;
            }
     printf("Opened Pipe\n");
     char a = 'a';
            int f  = fork();
            if(f == -1){
                    perror("fork");
                    exit(1);
            }else if(f == 0){
                    //This is by the child process
                    if (write (fd, &a, 1) == -1) {
                            close(fd);
                    perror ("open failed");
                            return 1;
         }

            }else{
                    //This is the parent process
                    int w = waitpid(f, &status, WUNTRACED | WCONTINUED);
                    if (w == -1){
                            perror("waitpid");
                            exit(EXIT_FAILURE);
                    }
            }
    }
}

2 个答案:

答案 0 :(得分:0)

由于您不想查看write的回复,您甚至不会注意到它失败了。 (也就是说,它无休止地流入管道;它只是骗你并打印&#34;写入管道......&#34;失败写入管道。)

阻止写入的唯一方法是填充管道。但这不是你的问题。问题是管道已经终止。如果你想保持那个管道打开,你需要让一些进程继续读取它。例如,您可以在其他shell中执行sleep 500 < /tmp/test。或者只是打开fifo阅读程序中的写作。 (例如,添加行open("/tmp/fifo", O_RDONLY);

更奇怪的问题是;为什么你的程序没有从SIGPIPE终止?

答案 1 :(得分:0)

您可以执行您正在尝试的操作,但了解您必须将读取限制在shell端的 one-char ,因为没有'\n'写入管道。此外,您write可能比readwrite次。例如,您可以添加验证,因为Pursell先生建议确保您的C程序在#include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <unistd.h> #include <errno.h> int main (int argc, char **argv) { int fd; errno = 0; if (mkfifo (argc > 1 ? argv[1] : "/tmp/test", 0666)) { perror ("mkfifo failed"); return 1; } if ((fd = open ("/tmp/test", O_WRONLY)) == -1) { perror ("open failed"); return 1; } printf ("Opened\n"); char a = 'a'; while (1) { printf ("Writing to pipe...\n"); if (write (fd, &a, 1) == -1) { perror ("open failed"); return 1; } } return 0; } 上运行并阻止类似于:

$ declare -i c=0; while test "$c" -lt 10 && read -n 1 ch; do 
    echo "read: $ch"
    ((c++))
done </tmp/test

您可以使用简单的测试:

read: a
read: a
read: a
read: a
read: a
read: a
read: a
read: a
read: a
read: a

示例Shell输出

write

您将Writing to pipe...直到fifo缓冲区已满,导致read: afork更多。

fork

的粗略示例

继续这里的评论是一个粗略的例子,使用while (1)来生成子进程,以确保您的C程序始终阻止写入shell。此示例仅限于3次重复,但您可以使用write进行连续循环。我还为#include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> int crt_fifo_write (char *fname); int file_exists (char *f); int main (int argc, char **argv) { int n = 0; errno = 0; while (n < 3) { /* limit example to 3 child processes */ pid_t cpid, w; int status; cpid = fork(); if (cpid == -1) { perror("fork"); exit(EXIT_FAILURE); } if (cpid == 0) { /* Code executed by child */ if (!crt_fifo_write (argc > 1 ? argv[1] : "/tmp/test")) fprintf (stderr, "crt_fifo_write() failed.\n"); } else { /* Code executed by parent */ do { w = waitpid (cpid, &status, WUNTRACED | WCONTINUED); if (w == -1) { perror("waitpid"); exit(EXIT_FAILURE); } if (WIFSIGNALED(status)) /* signal on close of read end */ printf("shell read complete. %s\n", n < 2 ? "restarting" : "done"); } while (!WIFEXITED(status) && !WIFSIGNALED(status)); } n++; } return 0; } /** your write 'a' to the fifo with check for existence & unlink */ int crt_fifo_write (char *fname) { int fd, n = 0; errno = 0; if (!fname || !*fname) return 0; if (file_exists (fname)) if (unlink (fname) == -1) { perror ("fifo exists unlink failed"); return 0; } if (mkfifo (fname, 0666)) { perror ("mkfifo failed"); return 1; } if ((fd = open (fname, O_WRONLY)) == -1) { perror ("open failed"); return 1; } printf ("Opened\n"); char a = 'a'; while (write (fd, &a, 1) != -1) { printf ("%3d - Writing to pipe...\n", n++); } return 0; } /** atomic test that file exists (1 success, 0 otherwise) */ int file_exists (char *f) { errno = 0; int flags = O_CREAT | O_WRONLY | O_EXCL; int mode = S_IRUSR | S_IWUSR; int fd = open (f, flags, mode); if (fd < 0 && errno == EEXIST) return 1; else if (fd) { /* created, like bash touch */ close (fd); unlink (f); } return 0; } 添加了一个快速计数器(仅仅是为了我的好奇心),例如

$ ./bin/pipe_write_shell_fork
Opened
  0 - Writing to pipe...
  1 - Writing to pipe...
  2 - Writing to pipe...
  3 - Writing to pipe...
  4 - Writing to pipe...
    ...
138 - Writing to pipe...
139 - Writing to pipe...
140 - Writing to pipe...
shell read complete. restarting
Opened
  0 - Writing to pipe...
  1 - Writing to pipe...
  2 - Writing to pipe...
  3 - Writing to pipe...
  4 - Writing to pipe...
    ...
    130 - Writing to pipe...
131 - Writing to pipe...
shell read complete. restarting
Opened
  0 - Writing to pipe...
  1 - Writing to pipe...
  2 - Writing to pipe...
  3 - Writing to pipe...
  4 - Writing to pipe...
    ...
144 - Writing to pipe...
145 - Writing to pipe...
shell read complete. done

示例程序使用/输出

$ declare -i c=0; while test "$c" -lt 10 && read -n 8 ch; do \
echo "read: $ch"; ((c++)); done </tmp/test
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa
read: aaaaaaaa

示例Shell读取/输出

{
  "sendSmsRequest": {
  "to": "5511982694404",
  "msg": "funcionou"
  }
}   

(重复2次)