2

假设我们有一个g=(a+b)*(c+d)-(e/f)带有硬编码任意数字变量的表达式。我想使用多个子进程来计算这个表达式,以便更好地理解 fork() 的工作原理。

我的第一次尝试是计算(a + b)pid1(c + d)pid2(e / f)pid3,然后在进程中进行求和和减法。

好吧,令我失望(a + b)的是,在子进程pid1中完成的计算并没有影响double expression1父进程中的变量。我认为这背后的原因——每个fork()都会创建一个单独的内存空间;一旦子进程退出,在该子进程中完成的所有计算都将消失。

在这种情况下,你通常会怎么做?我想也许我可以在一个子进程中嵌套 fork() 子进程来(a + b)首先计算;等待;然后(c + d); 等待(e / f); 等待第一个孩子计算整个表达式;孩子返回(0)父母终止。

但我认为这个问题有一个更简单的解决方案,对吗?

4

4 回答 4

3

如果您坚持使用fork(),那么这是我现在使用子进程和共享内存的答案

请注意,exit()此处以系统预期的方式使用它:指示孩子是否正常退出。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>


struct shared{
    int a_b;
    int c_d;
    int e_f;
};
const int a=1,b=2,c=3,d=4,e=6,f=2;
const key_t key = 1234;
pid_t pab,pcd,pef;
void* shared_mem;

int main(){
    //Parent process create the shared memory 
    int shmid = shmget(key,sizeof(struct shared), 0666|IPC_CREAT);
    if(shmid == -1) exit(EXIT_FAILURE);

    //Fork child
    pab = fork();
    if(pab == 0){  
        //Inside process ab
        //attach to shared memory
        shared_mem = shmat(shmid,(void*) 0,0);
        if(shared_mem == (void*) -1) exit (EXIT_FAILURE);
        struct shared* shared_data = (struct shared*) shared_mem;
        shared_data->a_b = a +b;

        //detach
        if(shmdt(shared_mem) == -1) exit (EXIT_FAILURE);
        exit(EXIT_SUCCESS);
    }else {
        pcd = fork();
        if(pcd == 0){
            //Inside process cd
            //attach to shared memory
            shared_mem = shmat(shmid,(void*) 0,0);
            if(shared_mem == (void*) -1) exit (EXIT_FAILURE);
            struct shared* shared_data = (struct shared*) shared_mem;
            shared_data->c_d = c+d;

            //detach
            if(shmdt(shared_mem) == -1) exit (EXIT_FAILURE);
            exit(EXIT_SUCCESS);

        }else{
            pef = fork();
            if(pef == 0){
                //Inside process ef
                //attach to shared memory
                shared_mem = shmat(shmid,(void*) 0,0);
                if(shared_mem == (void*) -1) exit (EXIT_FAILURE);
                struct shared* shared_data = (struct shared*) shared_mem;
                shared_data->e_f = e/f;

                //detach
                if(shmdt(shared_mem) == -1) exit (EXIT_FAILURE);

                exit(EXIT_SUCCESS);
            }
        }
    }

    //Wait child process termination
    int status_ab,status_cd,status_ef;
    waitpid(pab,&status_ab,0);
    waitpid(pcd,&status_cd,0);
    waitpid(pef,&status_ef,0);

    //Check if all child exited  normally
    if(!WIFEXITED(status_ab) || !WIFEXITED(status_cd)||!WIFEXITED(status_ef)){
        exit(EXIT_FAILURE);
    }

    //Parent attaches to memory 
    shared_mem = shmat(shmid,(void*) 0,0);
    if(shared_mem == (void*) -1) exit (EXIT_FAILURE);
    struct shared* shared_data = (struct shared*) shared_mem;

    //Calculate result
    int result = (shared_data->a_b)*(shared_data->c_d)-(shared_data->e_f);
    printf("Result is %d\n", result);

    //Parent detaches from shared memory and deletes
    if(shmdt(shared_mem) == -1) exit (EXIT_FAILURE);
    if(shmctl(shmid,IPC_RMID,0) == -1) exit(EXIT_FAILURE);

    return EXIT_SUCCESS;

}
于 2012-09-19T15:44:24.697 回答
2

fork() 进程,然后 waitpid() 处理它们的返回值:

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

int main()
{
    //whatever values you like:
    int a = 1;
    int b = 2;
    int c = 3;
    int d = 4;
    int e = 15;
    int f = 6;

    int a_plus_b_pid;
    int c_plus_d_pid;
    int e_div_f_pid;

    int a_plus_b;
    int c_plus_d;
    int e_div_f;

    a_plus_b_pid = fork();
    if(a_plus_b_pid)
    {
        c_plus_d_pid = fork();
        if(c_plus_d_pid)
        {
            e_div_f_pid = fork();
            if (e_div_f_pid)
            {
                //wait for our processes to exit, with our results, and stash the computed values.
                waitpid(a_plus_b_pid, &a_plus_b, 0);
                waitpid(c_plus_d_pid, &c_plus_d, 0);
                waitpid(e_div_f_pid, &e_div_f, 0);

                //The 8 least-significant bits carry info that we're not interested in here, so shift them out:
                a_plus_b >>= 8;
                c_plus_d >>= 8;
                e_div_f >>= 8;

                printf("%d %d %d %d\n", a_plus_b, c_plus_d, e_div_f, a_plus_b * c_plus_d - e_div_f);
            }
            else
            {
                exit (e/f);
            }
        }
        else
        {
            exit (c+d);
        }
    }
    else
    {
        exit (a+b);
    }
}
于 2012-09-19T00:59:37.887 回答
1

这是使用 pthread 的版本:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>


volatile int a_b;
volatile int c_d;
volatile int e_f;
const int a=1,b=2,c=3,d=4,e=6,f=2;

void* calc_ab(void*);
void* calc_cd(void*);
void* calc_ef(void*);

int main(){
        pthread_t ab_thread, cd_thread, ef_thread;

        pthread_create(&ab_thread,NULL,calc_ab,NULL);
        pthread_create(&cd_thread,NULL,calc_cd,NULL);
        pthread_create(&ef_thread,NULL,calc_ef,NULL);
        pthread_join(ab_thread, NULL);
        pthread_join(cd_thread, NULL);
        pthread_join(ef_thread,NULL);
        int result = a_b*c_d-e_f;
        printf("Result is %d\n", result);

    return EXIT_SUCCESS;
}

void* calc_ab(void* arg){ a_b = a+b;pthread_exit(NULL);}
void* calc_cd(void* arg){ c_d = c+d;pthread_exit(NULL);}
void* calc_ef(void* arg){ e_f = e/f;pthread_exit(NULL);}

要编译,您必须链接到 pthread: gcc pthread.c -lpthread -o teste

笔记

  • 请注意,声明了在主线程和子线程之间共享的变量volatile。这可以防止编译器进行一些内存优化,从而防止在一个线程中完成的写入操作不会被其他线程看到。
  • 每个子线程写入不同的共享变量。我想保持代码简单,不必显式处理同步。
  • 主线程只有在它从pthread_join更改它的线程返回后才读取共享变量。同样,我想保持代码简单,不必显式处理同步。
于 2012-09-19T02:04:53.800 回答
1

首先,您根本不需要进程来进行任意计算。嵌入像lua这样的解释器可能更简单。

当然,每个进程都有自己的地址空间。键入cat /proc/self/maps以获取有关运行该cat命令的进程的信息。

如果您坚持使用进程来了解它们如何通过管道进行通信,您可能会使用诸如popen(3) 之类的东西,它将使用一些系统调用来启动和管道命令。

char cmd[80];
int a, b, sum;
/// fill a & b
snprintf (cmd, sizeof(cmd), "echo $[%d + %d]", a, b);
FILE* pcmd = popen(cmd, "r");
if (fscanf (pcmd, "%d", &sum)>0) {
    // do something with sum
};
pclose(pcmd);

你应该读一本好书,比如Advanced Unix ProgrammingAdvanced Linux Programming。真正的事情是理解系统调用,如fork(2)waitpid(2)execve(2)pipe(2)dup(2)等。了解系统调用 (2)是由某些命令完成的或程序,使用strace

于 2012-09-19T05:49:19.833 回答