C -> Shell - 阻止写入直到读取

标签 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() 上使用,因为我认为为每个请求打开文件会产生很多开销。

谢谢!

更新

这就是我在 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);
                    }
            }
    }
}

最佳答案

您可以执行您正在尝试的操作,但请理解您必须将 shell 端的读取限制为一个字符,因为不会写入 '\n'到管道。此外,您的写入次数可能比shell 读取次数多得多。例如,您可以按照 Pursell 先生的建议添加验证,以确保您的 C 程序正常运行并在 write 上阻塞,类似于:

#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

Shell 输出示例

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

您将写入直到fifo缓冲区已满,导致写入管道...比您已读取:a更多。

<小时/>

使用fork的粗略示例

继续这里的注释是一个使用 fork 生成子进程的粗略示例,以确保您的 C 程序在 shell 写入时始终阻塞。此示例仅限 3 次重复,但您可以仅使用 while (1) 进行连续循环。我还为 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

Shell 读取/输出示例

$ 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

(再重复 2 次)

关于C -> Shell - 阻止写入直到读取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44974699/

相关文章:

linux - 如何在Linux中使用printf解决此问题?

c - 如何删除两个索引之间的链表节点?

linux - 在 shell 脚本中使用\c

linux - 如何在echo中使用Read命令,就像填空一样

c - 如何使用通配符在目录中搜索文件

mysql - 如何 ssh 到 Linux 机器并执行包含字符串的 mysql 命令?

linux - 如何在提示用户在 shell 脚本中输入时捕获并采取行动?

导入十六进制文件后无法在 MPLab 中调试

c - 将文本文件的每一行存储到数组中

c - 如何在c中表示大于long的数字