草庐IT

c - Linux进程间通信

coder 2023-06-22 原文

我必须实现一个测试程序(测验),它除了显示问题和阅读答案外,还必须显示每过一分钟还剩多少时间。考试时间结束后,做完题目或时间用完,程序必须从头返回,在开始之前,我们输入考生姓名。此实现必须使用流程来完成。以下是我到目前为止编写的代码。问题是我不确定我是否在流程和子流程之间进行了良好的沟通,特别是因为我没有使用管道。一些意见?

#include<stdio.h>
#include<sys/types.h>
#include<signal.h>
#include<unistd.h>
#include<sys/wait.h>

#define T 180

void firstChildAction(){
    static const char filename[] = "/home/osystems01/laura/text";
    char question[100];
    char answer[100];

    FILE *file = fopen(filename,"r");
    if(file != NULL){
        while(fgets(question,sizeof question,file) != NULL){
            fputs(question, stdout);
            scanf("%s",&answer);
        }
        fclose(file);
    }
    else{
        perror(filename);
    }
}

void secondChildAction(){
    int i;
    for(i = T; i >= 0; i-=60){
        if( i/60 != 0){
            printf("You have %d %s left.\n", i/60,(i/60 >   1)?"minutes":"minute");
            sleep(60);
        }
        else{
            printf("The time is over\n");
            break;
        }
    }
}

int main() {
    pid_t pidA;
    pid_t pidB;
    pid_t wPid;
    char name[20];
    while(1){
        printf("Enter the candidate name or Quit to exit: \n");
        scanf("%s",&name);
        if(strcmp(name,"Quit") == 0 || strcmp(name,"quit") == 0){
            printf("The program is terminating.....\n");
            break;
        }
        else{
            pidA = fork();
            if(pidA == 0){
                firstChildAction();
                exit(0);
            }
            else{
                pidB = fork();
                if(pidB == 0){
                    secondChildAction();
                    exit(0);
                }
            }
            int status;
            while(wPid = wait(&status)) > 0 ){
                if(WIFEXITED(status)){
                    int result = WEXITSTATUS(status);
                    printf("Exit status of %d is %d\n", wPid, result);
                    if(wPid == pidA){
                        kill(pidB,SIGTERM);
                        kill(pidA,SIGTERM);
                    }
                    else if(wPid == pidB){
                        kill(pidA,SIGTERM);
                        kill(pidB,SIGTERM);
                    }
                }
            }
        }
    }
    return 0;
}

最佳答案

管道本身不需要您提供常规文件,但它们可以有一个唯一的、全局可见的名称,该名称由您必须指定的(未使用的)文件名提供。文件的内容(如果有)由库处理。

有(简单的)管道用于相关进程(例如同一进程层次结构中的子进程和父进程)之间的通信,其中管道句柄可以很容易地传递给其他进程。

另一种风格称为“命名管道”,用于具有任何关系的进程,其中可以使用全局名称查找管道句柄(如我链接的问题的答案中所述)。您可以将管道想象成一个直接连接的传声管,允许两个进程使用读写功能随意聊天。在 Linux 上,管道是一个单工(一次,一个人说,另一个人听)。在这种情况下,双向异步 IO 需要两个管道 (https://unix.stackexchange.com/questions/53641/how-to-make-bidirectional-pipe-between-two-programs)。输入和输出的直接缓冲区是抽象的。就像网络套接字一样。

我建议在接受的答案中编译这个很好的例子来玩:https://stackoverflow.com/a/2789967/1175253

编辑

带有错误处理的示例代码。将 pipe.h 和 pipe.c 视为一个库(针对它链接 NamedPipeReader 和 NamedPipeWriter)。 此代码需要进一步测试,但是,该代码能够以任何顺序(重新)打开命名管道。


pipe.h

#ifndef PIPE_H_
#define PIPE_H_

//C headers
#include <errno.h>
#include <assert.h>

//Linux headers
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#ifdef __cplusplus
extern "C"
{
#endif

int open_named_pipe(const char* const name, const int permissions, const int mode, int* pipe_created);

#ifdef __cplusplus
}
#endif

#endif /* PIPE_H_ */


管道.c

#include "pipe.h"

#include <stdio.h>

int open_named_pipe(const char* const name, const int permissions, const int mode, int* pipe_created)
{
    int fd;

    assert(name);
    assert(permissions);
    assert(pipe_created);

    //Create or use an existing pipe special file
    if (0 == mkfifo(name, permissions))
    {
        *pipe_created = 1;
        printf("Successfully created named pipe '%s'\n", name);
    }
    else
    {
        switch (errno)
        {
        case EEXIST:
            //this is OK, as the other process might already has created the special file
            printf("Opened existing named pipe '%s'\n", name);
            break;
        default:
            fprintf(stderr, "Failed to create or access named pipe '%s'\n", name);
            perror("    ");
            return -1;
        };
    }

    fd = open(name, mode);
    if (fd < 0)
    {
        perror("Could not open pipe for writing");
        if (*pipe_created)
        {
            if (0 == unlink(name))
            {
                *pipe_created = 0;
            }
            else
            {
                perror("Failed to unlink named pipe");
            }
        }
    }

    return fd;
}

NamedPipeReader.c

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>

#include "pipe.h"

//Globals
const char* const pipe_name = "/tmp/myfifo";
const int pipe_permissions = 0600;
const size_t read_buffer_size = 1024; //[bytes]
const size_t read_retry_delay = 25000; //[us]

int fd = -1;
int pipe_created = 0;
char* read_buffer = NULL;

//Handles EPIPE signal
void signal_handler(int signal)
{
    fprintf(stderr, "cought signal %d\n", signal);
}

//Handles cleanup on exit
void exit_handler(void)
{
    if (read_buffer)
        free(read_buffer);

    if (fd >= 0)
        close(fd);

    //if this process created the FIFO, we unlink it
    if (pipe_created == 0)
        unlink(pipe_name);
}

int main()
{
    //Locals
    int run = 1;
    int received = 0;

    //Install the exit handler
    atexit(&exit_handler);
    signal(EPIPE, signal_handler);
    signal(EACCES, signal_handler);

    //Allocate the buffer
    read_buffer = (char*) malloc(read_buffer_size);
    if (!read_buffer)
    {
        perror("Failed to allocate buffer");
        return EXIT_FAILURE;
    }

    restart: ;

    //Close if already open
    if(fd >= 0)
        close(fd);
    //Create or use an existing pipe special file
    fd = open_named_pipe(pipe_name, pipe_permissions, O_RDONLY, &pipe_created);
    if (fd < 0)
    {
        return EXIT_FAILURE;
    }

    while (run)
    {
        assert(fd >= 0);
        assert(read_buffer_size > 1);

        received = read(fd, read_buffer, read_buffer_size - 1);

        if (received > 0)
        {
            //add a NUL char for string termination
            read_buffer[received] = '0';
            printf("local process %llu received: %s\n", (unsigned long long) getpid(), read_buffer);
        }
        else if (received == 0)
        {
            //EOF reached, this happens in case the writer has closed its handle.
            //Perform a delayed restart and recreate the named pipe
            usleep(read_retry_delay);
            printf("Restarting...\n");
            goto restart;
        }
        else
        {
            switch (errno)
            {
            case EAGAIN:
                //Wait, if the pipe is empty,
                //happens when opened with the O_NONBLOCK flag
                usleep(read_retry_delay);
                break;
            case EPIPE:
            case EBADF:
            case EBADFD:
                perror("Pipe error");
                printf("Restarting...\n");
                goto restart;
            default:
                perror("Pipe error");
                return EXIT_FAILURE;
            };
        }
    }

    return EXIT_SUCCESS;
}

NamedPipeWriter.c

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>

#include "pipe.h"

//Globals
const char* const pipe_name = "/tmp/myfifo";
const int pipe_permissions = 0600;
const size_t write_buffer_size = 1024; //[bytes]
const size_t write_retry_delay = 25000; //[us]
const size_t write_interval = 1000000;

int fd = -1;
int pipe_created = 0;
char* write_buffer = NULL;

//Handles EPIPE signal
void signal_handler(int signal)
{
    fprintf(stderr, "cought signal %d\n", signal);
}

//Handles cleanup on exit
void exit_handler(void)
{
    if (write_buffer)
        free(write_buffer);

    if (fd >= 0)
        close(fd);

    //if this process created the FIFO, we unlink it
    if (pipe_created == 0)
        unlink(pipe_name);
}

//Main Function
int main()
{
    //Locals
    int run = 1;
    int sent = 0;
    int msg_len = 0;

    //Install the exit handler
    atexit(&exit_handler);
    signal(EPIPE, signal_handler);
    signal(EACCES, signal_handler);

    //Allocate the buffer
    write_buffer = (char*) malloc(write_buffer_size);
    if (!write_buffer)
    {
        perror("Failed to allocate buffer");
        return EXIT_FAILURE;
    }

    restart: ;

    //Close if already open
    if(fd >= 0)
        close(fd);
    //Create or use an existing pipe special file
    fd = open_named_pipe(pipe_name, pipe_permissions, O_WRONLY, &pipe_created);
    if (fd < 0)
    {
        return EXIT_FAILURE;
    }

    while (run)
    {
        //Print message into the buffer
        msg_len = snprintf(write_buffer, write_buffer_size, "Greetings from process %llu\n", (unsigned long long) getpid());

        {
            char* msg_ptr = write_buffer;
            char* msg_end = write_buffer + msg_len;
            while (msg_ptr != msg_end)
            {
                assert(fd >= 0);
                assert(msg_ptr < msg_end);
                sent = write(fd, msg_ptr, msg_end - msg_ptr);
                if (sent > 0)
                {
                    msg_ptr += sent;
                }
                else if (sent == 0)
                {
                    //retry delay for nonblocking writes
                    usleep(write_retry_delay);
                }
                else
                {
                    switch (errno)
                    {
                    case EAGAIN:
                        //Wait, if the pipe is full,
                        //happens when opened with the O_NONBLOCK flag
                        usleep(write_retry_delay);
                        break;
                    case EPIPE:
                    case EBADF:
                    case EBADFD:
                        perror("Pipe error");
                        printf("Restarting...\n");
                        goto restart;
                    default:
                        perror("Pipe error");
                        return EXIT_FAILURE;
                    };
                }
            }

            printf("Written: %s\n", write_buffer);
            usleep(write_interval);
        }
    }

    return EXIT_SUCCESS;
}

关于c - Linux进程间通信,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19746483/

有关c - Linux进程间通信的更多相关文章

  1. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  2. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

  3. ruby - 无法在 Ruby 中将 ffmpeg 作为子进程运行 - 2

    我正在尝试使用以下代码通过将ffmpeg实用程序作为子进程运行并获取其输出并解析它来确定视频分辨率:IO.popen'ffmpeg-i'+path_to_filedo|ffmpegIO|#myparsegoeshereend...但是ffmpeg输出仍然连接到标准输出并且ffmepgIO.readlines是空的。ffmpeg实用程序是否需要一些特殊处理?或者还有其他方法可以获得ffmpeg输出吗?我在WinXP和FedoraLinux下测试了这段代码-结果是一样的。 最佳答案 要跟进mouviciel的评论,您需要使用类似pope

  4. Ruby 守护进程导致 ActiveRecord 记录器 IOError - 2

    我目前正在用Ruby编写一个项目,它使用ActiveRecordgem进行数据库交互,我正在尝试使用ActiveRecord::Base.logger记录所有数据库事件具有以下代码的属性ActiveRecord::Base.logger=Logger.new(File.open('logs/database.log','a'))这适用于迁移等(出于某种原因似乎需要启用日志记录,因为它在禁用时会出现NilClass错误)但是当我尝试运行包含调用ActiveRecord对象的线程守护程序的项目时脚本失败并出现以下错误/System/Library/Frameworks/Ruby.frame

  5. ruby - 在 ruby​​ 中生成一个进程,捕获 stdout,stderr,获取退出状态 - 2

    我想从ruby​​rake脚本运行一个可执行文件,比如foo.exe我希望将foo.exe的STDOUT和STDERR输出直接写入我正在运行rake任务的控制台.当进程完成时,我想将退出代码捕获到一个变量中。我如何实现这一目标?我一直在玩backticks、process.spawn、system但我无法获得我想要的所有行为,只有部分更新:我在Windows上,在标准命令提示符下,而不是cygwin 最佳答案 system获取您想要的STDOUT行为。它还返回true作为零退出代码,这可能很有用。$?填充了有关最后一次system调

  6. ruby-on-rails - 如何用不同的用户运行nginx主进程 - 2

    A/ctohttp://wiki.nginx.org/CoreModule#usermaster进程曾经以root用户运行,是否可以以不同的用户运行nginxmaster进程? 最佳答案 只需以非root身份运行init脚本(即/etc/init.d/nginxstart),就可以用不同的用户运行nginxmaster进程。如果这真的是你想要做的,你将需要确保日志和pid目录(通常是/var/log/nginx&/var/run/nginx.pid)对该用户是可写的,并且您所有的listen调用都是针对大于1024的端口(因为绑定(

  7. Ruby 守护进程和 JRuby - 备选方案 - 2

    我有一个应用程序正在从Ruby迁移到JRuby(由于需要通过Java提供更好的Web服务安全支持)。我使用的gem之一是daemons创建后台作业。问题在于它使用fork+exec来创建后台进程,但这对JRuby来说是禁忌。那么-是否有用于创建后台作业的替代gem/wrapper?我目前的想法是只从shell脚本调用rake并让rake任务永远运行......提前致谢,克里斯。更新我们目前正在使用几个与Java线程相关的包装器,即https://github.com/jmettraux/rufus-scheduler和https://github.com/philostler/acts

  8. ruby-on-rails - Rails - Carrierwave 进程抛出 ArgumentError : no images in this image list - 2

    在尝试实现应用auto_orient的过程之后!对于我的图片,我收到此错误:ArgumentError(noimagesinthisimagelist):app/uploaders/image_uploader.rb:36:in`fix_exif_rotation'app/controllers/posts_controller.rb:12:in`create'Carrierwave在没有进程的情况下工作正常,但在添加进程后尝试上传图像时抛出错误。流程如下:process:fix_exif_rotationdeffix_exif_rotationmanipulate!do|image|

  9. ruby-on-rails - Ruby 长时间运行的进程对队列事件使用react - 2

    我有一个将某些事件写入队列的Rails3应用。现在我想在服务器上创建一个服务,每x秒轮询一次队列,并按计划执行其他任务。除了创建ruby​​脚本并通过cron作业运行它之外,还有其他稳定的替代方案吗? 最佳答案 尽管启动基于Rails的持久任务是一种选择,但您可能希望查看更有序的系统,例如delayed_job或Starling管理您的工作量。我建议不要在cron中运行某些东西,因为启动整个Rails堆栈的开销可能很大。每隔几秒运行一次它是不切实际的,因为Rails上的启动时间通常为5-15秒,具体取决于您的硬件。不过,每天这样做几

  10. ruby - 在一个进程多个数据库连接 sinatra 应用程序中使用什么 ORM? - 2

    已检查ActiveRecord、DataMapper、Sequel:有些使用全局变量(静态变量)有些需要在使用模型加载源文件之前打开数据库连接。在使用不同数据库的sinatra应用程序中使用哪种ORM更好。 最佳答案 DataMapper专为多数据库使用而设计。你可以通过像DataMapper.setup(:repository_one,"mysql://localhost/my_db_name")这样的方式设置多个存储库。DataMapper随后会跟踪所有已在哈希中设置的存储库,您可以引用该哈希并将其用于范围界定:DataMapp

随机推荐