c -在子进程和父进程之间传递信息- linux(不带' pipe() ')



作为我任务的一部分,我必须实现子-父进程。

"父进程不应该等待子进程退出,而是应该在元素到达共享缓冲区时立即打印元素"

我知道的情况下,当父必须等待子结束,但如何实现消息的通信从子到父异步的方式?

注:确切的Q是

编写一个程序,它的主例程从用户那里获得两个参数n和d,即当它从shell调用时传递给你的程序。然后你的程序将创建一个共享内存和一个子进程。子进程应该获得n和d的值(您有多种选择如何做到这一点),并创建长度为n的等差序列,其第一个元素为0,每个后续元素的值为kd,其中k是元素编号(k= 0到n-1)。子进程将每次创建一个元素,并在生成序列元素之间等待一段随机的时间间隔(0到9.999秒)。一旦生成了一个元素,子进程就会按照第4讲的幻灯片33-37所描述的方式将元素组织到共享缓冲区中。(例如:n=5, d=2,则顺序为0,2,4,6,8)父进程不应该等待子进程退出,而是应该在元素到达共享缓冲区时立即打印元素(同样,类似于第4讲的第33-37页)。提示;使用fflush()来确保printf被立即打印到屏幕上。

我的代码到目前为止- gist

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
void printLab4(int n, int a, int *fibo)
{
int i;
for (i = 0; i < n; i++)
printf("%d -> %dn", i, fibo[i]);
}
void computeLab4(int n, int a, int *fibo)
{
int i;
for (i = 0; i < n; i++)
{
int sleepSec = rand() % 10;
printf("sleeping for %d : ", sleepSec);
sleep(sleepSec);
fibo[i] = i * a;
// randomly sleeping for 0-10 secs
printf("Generated new element %d after %d seconds n", fibo[i], sleepSec);
}
}
int main(int argc, char *argv[])
{
pid_t childPID;
int status;
int shm_fd;
int *shared_memory;
int msize; // the size (in bytes) of the shared memory segment
const char *name = "Lab_4";
int n, a;
if (argc != 3)
{
fprintf(stderr, "usage: %s <Lab4 Seq to be generated>n", argv[0]);
return -1;
}
n = atoi(argv[1]);
a = atoi(argv[2]);
printf("%d n", n);
printf("%d n", a);
if (n < 0 || a < 0)
{
fprintf(stderr, "Illegal number: %sn", argv[1]);
return -2;
}
// calculating the array size based on the number of terms being passed from child to parent
msize = (n + 2) * sizeof(int);
// open the memory
shm_fd = shm_open(name, O_CREAT | O_EXCL | O_RDWR, S_IRWXU | S_IRWXG);
if (shm_fd < 0)
{
fprintf(stderr, "Error in shm_open()");
return -3;
}
printf("Created shared memory object %sn", name);
// attach the shared memory segment
ftruncate(shm_fd, msize);
printf("shmat returnedn");
// allocating the shared memory
shared_memory = (int *)mmap(NULL, msize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (shared_memory == NULL)
{
fprintf(stderr, "Error in mmap()");
return -3;
}
printf("Shared memory segment allocated correctly (%d bytes).n", msize);
shared_memory[0] = n;
shared_memory[1] = a;
childPID = fork();
if (childPID == -1)
{
fprintf(stderr, "Cannot proceed. fork() error");
return -4;
}
if (childPID == 0)
{
// then we're the child process
computeLab4(shared_memory[0], shared_memory[1], shared_memory + 1);
exit(0);
}
else
{
// parent will wait until the child finished
wait(&status);
// print the final results in the
printLab4(shared_memory[0], shared_memory[1], shared_memory + 1);
// now detach the shared memory segment
shm_unlink(name);
}
return 0;
}

由于您是在shell调用程序时从命令行获取这些值,因此当父进程调用fork()时,子进程将有权访问相同的变量。没有必要"通过"。从父级到子级的值。

当子节点创建元素时,我假设父节点正在从共享内存中读取元素。"它一到"是一个有点可疑的要求,除非您在并行和实时系统上。但是,模拟该需求可以通过父进程检查共享内存是否获得新数据的繁忙等待循环来实现。

为了在父进程处于繁忙的等待循环时不饿死操作系统的CPU周期,父进程可以在检查迭代之间调用sched_yield(),它的行为就像一个睡眠,立即唤醒。

父进程和子进程之间的共享内存可以被看作某种队列,父进程的忙等待是检查队列是否为空,如果队列为空,则处理队列元素,直到队列为空,此时再次忙等待。

for (;;) {
if (q_is_empty(q)) {
sched_yield();
continue;
}
int v = q_dequeue(q);
process(v);
}

您可以使用值为1的usleep()nanosleep()来代替sched_yield()

子节点当然是按照赋值的规定向队列中添加元素。

for (;;) {
if (q_is_full(q)) {
sched_yield();
continue;
}
v = next_value_in_sequence_after_delay();
q_enqueue(v);
}

您可能想要添加一个指示(例如进入-1队列),表明子进程已经完成。然后,子进程就可以退出,父进程就可以知道收割子进程是安全的。

完整检查和空检查在逻辑上可以被看作是进入队列和退出队列操作本身的一部分。因此,一个可能的实现可能是:

void q_enqueue(struct queue_type *q, int v) {
while (q_is_full(q)) sched_yield();
q->q[q->tail % Q_MAX_ELEM] = v;
q->tail += 1;
}
int q_dequeue(struct queue_type *q) {
while (q_is_empty(q)) sched_yield();
int v = q->q[q->head % Q_MAX_ELEM];
q->head += 1;
return v;
}

然后父函数和子函数可能看起来像:

void parent(struct queue_type *q) {
for (;;) {
int v = q_dequeue(q);
if (v == -1) break;
printf("|%d", v);
fflush(stdout);
}
printf("|done!n");
}
void child(struct queue_type *q, int n, int d) {
int v = 0;
for (;;) {
if (v == -1) break;
useconds_t t = rand() % 10000;
usleep(t * 1000);
v = next_value(n, d, v);
q_enqueue(q, v);
}
}

下面是我测试的队列实现的其余部分,但您可能想研究无锁队列,看看如何在不需要传统临界区保护的情况下实现单个消费者/单个生产者队列。

#define Q_MAX_ELEM 1
struct queue_type {
volatile uint32_t head;
volatile uint32_t tail;
volatile int q[Q_MAX_ELEM];
};
void q_init(struct queue_type *q) {
static const struct queue_type q_zero;
*q = q_zero;
}
bool q_is_empty(struct queue_type *q) {
uint32_t tail = q->tail;
return q->head == tail;
}
bool q_is_full(struct queue_type *q) {
uint32_t head = q->head;
return (q->tail - head) == Q_MAX_ELEM;
}

上网试试!

完成此操作的典型方法是使用POSIXpipe()函数在子进程和父进程之间创建一对共享的管道。Linux手册页甚至有一个示例:https://www.man7.org/linux/man-pages/man2/pipe.2.html

最新更新