在程序中执行新进程或倍数

时间:2015-12-01 05:14:18

标签: c++ c

我想知道从正在运行的进程执行新进程(程序)的最佳做法是什么。更具体地说,我正在实现一个C / C ++作业调度程序,它必须在与它们通信时运行多个二进制文件。 execfork是否常见?或者有没有图书馆照顾这个?

2 个答案:

答案 0 :(得分:2)

好的,让我们开始...... 从一个任务创建另一个并行任务的方法很少。虽然我不会将它们全部命名为进程。

使用fork()系统调用

现在您已经提到fork()从您的父进程创建进程。关于fork(),几乎没有什么好事和坏事。

好事

  1. fork()能够创建一个完全不同的流程&在多核CPU系统中,它可以真正实现并行性
  2. fork()还会创建一个具有不同pid&的子进程。因此,如果您想要明确地杀死该过程,那就太好了。
  3. wait()& waitpid()系统调用可以让父母等待孩子。
  4. fork生成SIGCHILD信号并使用sigaction功能,您可以让父母等待孩子而不会阻止它。
  5. 糟糕的事情

    1. fork进程不共享相同的地址空间&因此,如果一个进程说变量var,则另一个进程无法直接访问同一个var。因此,沟通是一个大问题。
    2. 要进行沟通,您需要使用某些IPC机制,例如pipenamedpipemessageQueuessharedMemory
    3. 现在,pipenamedpipemessageQueues可以使用read&系统调用write,因为read& write系统调用阻止系统调用,您的应用程序保持同步,但这些IPCs非常慢。唯一的快IPCsharedMemory,但不能使用read& write&因此,您需要使用自己的同步机制,例如semaphores。但是,为更大的应用程序实施semaphores很困难。
    4. pthread

      现在线程消除了fork所面临的所有困难。

      1. 它没有创建单独的流程。
      2. 它创建的几个轻量级子任务几乎可以并行运行。
      3. 他们共享相同的地址空间&因此不需要任何IPC
      4. mutex附带,即使对于更大的应用程序,任何同步也都很棒。
      5. 线程也不会创建任何进程,因此所有线程都是同一进程的一部分,因此将具有相同的pid。
      6. 注意:在C ++中,线程是C ++库的一部分,而不是系统调用。

        注意2: C ++中的Boost线程更加成熟。建议使用。

        主要的想法虽然是知道什么时候使用线程&何时使用流程。

        如果您需要创建一个不需要与其他任务一起工作但又必须独立工作的子任务,请使用流程;否则使用线程。

        exec家庭系统调用是不同的。它使用您的相同的pid 。因此,如果您创建一个包含500行的应用程序,并且您在第250行收到exec来电,则exec进程将粘贴到您的整个过程中,并在exec调用后,你的程序将不会从251行恢复。此外,exec调用不会刷新您的stdio缓冲区。

        但是,是的,如果您打算创建一个单独的进程,然后使用exec调用来执行该任务然后出来,那么欢迎您这样做,但请记住要存储的IPC输出否则没用

        有关fork的更多信息,请点击 here

        有关主题的更多信息,请点击 here

        要提升therad,请点击 here

        @John Zwinck 答案也很好,但我对select()系统调用知之甚少,但是有可能也是这样吗

        编辑:正如@ Jonathan Leffler指出的那样

        经过很长时间的编辑:经过几年的努力,我现在从未想过使用所有这些SPOOKY库或无意义的可怕的并行方式,或者我应该说是SEEMINGLY并行处理。输入 coroutines ,即CONCURRENT处理的未来。请查看以下Go代码。当然,这在C / C ++中也是可能的。对于数据库中的7.7密耳行,此代码几乎不会比基于C / C ++线程的实现慢几毫秒,但服务器时间更易于管理和扩展。

        package main
        
        import (
            "fmt"
            "reflect"
        
            "github.com/jinzhu/gorm"
            _ "github.com/jinzhu/gorm/dialects/sqlite"
        )
        
        type AirQuality struct {
            // gorm.Model
            // ID      uint   `gorm:"column:id"`
            Index   string `gorm:"column:index"`
            BEN     string `gorm:"column:BEN"`
            CH4     string `gorm:"column:CH4"`
            CO      string `gorm:"column:CO"`
            EBE     string `gorm:"column:EBE"`
            MXY     string `gorm:"column:MXY"`
            NMHC    string `gorm:"column:NMHC"`
            NO      string `gorm:"column:NO"`
            NO2     string `gorm:"column:NO_2"`
            NOX     string `gorm:"column:NOx"`
            OXY     string `gorm:"column:OXY"`
            O3      string `gorm:"column:O_3"`
            PM10    string `gorm:"column:PM10"`
            PM25    string `gorm:"column:PM25"`
            PXY     string `gorm:"column:PXY"`
            SO2     string `gorm:"column:SO_2"`
            TCH     string `gorm:"column:TCH"`
            TOL     string `gorm:"column:TOL"`
            Time    string `gorm:"column:date; type:timestamp"`
            Station string `gorm:"column:station"`
        }
        
        func (AirQuality) TableName() string {
            return "AQ"
        }
        
        func main() {
            c := generateRowsConcurrent("boring!!")
        
            for row := range c {
                fmt.Println(row)
            }
        }
        
        func generateRowsConcurrent(msg string) <-chan []string {
            c := make(chan []string)
            go func() {
                db, err := gorm.Open("sqlite3", "./load_testing_7.6m.db")
                if err != nil {
                    panic("failed to connect database")
                }
                defer db.Close()
                rows, err := db.Model(&AirQuality{}).Limit(20).Rows()
                defer rows.Close()
                if err != nil {
                    panic(err)
                }
                for rows.Next() {
                    var aq AirQuality
                    db.ScanRows(rows, &aq)
                    v := reflect.Indirect(reflect.ValueOf(aq))
                    var buf []string
                    for i := 0; i < v.NumField(); i++ {
                        buf = append(buf, v.Field(i).String())
                    }
                    c <- buf
                }
        
                defer close(c)
            }()
            return c
        }
        

答案 1 :(得分:1)

您可以使用popen()生成流程并与流程进行通信。为了处理来自单个父进程的许多进程的通信,请使用select()poll()来复用popen()给您的文件描述符的读/写(您可以使用{ {1}}将fileno()转换为整数文件描述符。)

如果你想让一个库为你提取大部分内容,我建议使用libuv。这是一个完整的示例程序,主要是关注https://nikhilm.github.io/uvbook/processes.html#spawning-child-processes的文档:

FILE*

以上将生成#include <cstdio> #include <cstdlib> #include <inttypes.h> #include <uv.h> static void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { *buf = uv_buf_init((char*)malloc(suggested_size), suggested_size); } void echo_read(uv_stream_t *server, ssize_t nread, const uv_buf_t* buf) { if (nread == -1) { fprintf(stderr, "error echo_read"); return; } puts(buf->base); } static void on_exit(uv_process_t *req, int64_t exit_status, int term_signal) { fprintf(stderr, "Process %d exited with status %" PRId64 ", signal %d\n", req->pid, exit_status, term_signal); uv_close((uv_handle_t*)req, NULL); } int main() { uv_loop_t* loop = uv_default_loop(); const int N = 3; uv_pipe_t channel[N]; uv_process_t child_req[N]; for (int ii = 0; ii < N; ++ii) { char* args[3]; args[0] = const_cast<char*>("ls"); args[1] = const_cast<char*>("."); args[2] = NULL; uv_pipe_init(loop, &channel[ii], 1); uv_stdio_container_t child_stdio[3]; // {stdin, stdout, stderr} child_stdio[STDIN_FILENO].flags = UV_IGNORE; child_stdio[STDOUT_FILENO].flags = uv_stdio_flags(UV_CREATE_PIPE | UV_WRITABLE_PIPE); child_stdio[STDOUT_FILENO].data.stream = (uv_stream_t*)&channel[ii]; child_stdio[STDERR_FILENO].flags = UV_IGNORE; uv_process_options_t options = {}; options.exit_cb = on_exit; options.file = "ls"; options.args = args; options.stdio = child_stdio; options.stdio_count = sizeof(child_stdio) / sizeof(child_stdio[0]); int r; if ((r = uv_spawn(loop, &child_req[ii], &options))) { fprintf(stderr, "%s\n", uv_strerror(r)); return EXIT_FAILURE; } else { fprintf(stderr, "Launched process with ID %d\n", child_req[ii].pid); uv_read_start((uv_stream_t*)&channel[ii], alloc_buffer, echo_read); } } return uv_run(loop, UV_RUN_DEFAULT); } 的三个副本以打印当前目录的内容。它们都是异步运行的。