我想将一个字符从 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/