我想知道从正在运行的进程执行新进程(程序)的最佳做法是什么。更具体地说,我正在实现一个C / C ++作业调度程序,它必须在与它们通信时运行多个二进制文件。 exec
或fork
是否常见?或者有没有图书馆照顾这个?
答案 0 :(得分:2)
好的,让我们开始...... 从一个任务创建另一个并行任务的方法很少。虽然我不会将它们全部命名为进程。
使用fork()
系统调用
现在您已经提到fork()
从您的父进程创建进程。关于fork()
,几乎没有什么好事和坏事。
好事
fork()
能够创建一个完全不同的流程&在多核CPU系统中,它可以真正实现并行性fork()
还会创建一个具有不同pid
&的子进程。因此,如果您想要明确地杀死该过程,那就太好了。 wait()
& waitpid()
系统调用可以让父母等待孩子。fork
生成SIGCHILD
信号并使用sigaction
功能,您可以让父母等待孩子而不会阻止它。糟糕的事情
fork
进程不共享相同的地址空间&因此,如果一个进程说变量var
,则另一个进程无法直接访问同一个var
。因此,沟通是一个大问题。pipe
,namedpipe
,messageQueues
或sharedMemory
pipe
,namedpipe
和messageQueues
可以使用read
&系统调用write
,因为read
& write
系统调用阻止系统调用,您的应用程序保持同步,但这些IPCs
非常慢。唯一的快IPC
是sharedMemory
,但不能使用read
& write
&因此,您需要使用自己的同步机制,例如semaphores
。但是,为更大的应用程序实施semaphores
很困难。pthread
现在线程消除了fork
所面临的所有困难。
IPC
。mutex
附带,即使对于更大的应用程序,任何同步也都很棒。注意:在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);
}
的三个副本以打印当前目录的内容。它们都是异步运行的。