C程序多次要求用户输入
C program asking for user input multiple times
正在编写一个非常基本的 C 程序来演示共享内存。该程序需要不断计算两个输入的总和,直到用户执行 ^C
(此处未显示)。
到目前为止,我的 parent 捕获用户输入,然后告诉 child 计算总和,然后 parent 打印总和(作为分配建议)。
附上我的代码。
// Fork the process
while(1) {
// Declare a shared memory integer array
int *shared_ints = mmap(NULL, shared_seg_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
int x;
int y;
shared_ints[3] = 0;
if((pid = fork()) < 0) {
printf("Fork error \n");
exit(-1);
} else if(pid > 0 && shared_ints[3] == 0) { // Parent code
// Ask user for first input
printf("Enter your first number:");
scanf("%d", &x);
printf("\n");
// Ask user for second input
printf("Enter your second number:");
scanf("%d", &y);
printf("\n");
// Assign first value to shared memory
shared_ints[0] = x;
// Assign second value to shared memory
shared_ints[1] = y;
// Tell child that it can compute the sum now
shared_ints[3] = 1;
// Trap parent in a while-loop while the child
// computes the sum
while(shared_ints[3] == 1);
// Child has completed calculating the sum and
// the parent can print the sum
if(shared_ints[3] == 2) {
printf("The sum is: %d", shared_ints[2]);
printf("\n");
}
} else { // Child code
// Wait for parent to accept input values
while(shared_ints[3] == 0);
// Calculate the sum
shared_ints[2] = shared_ints[0] + shared_ints[1];
// Tell parent sum has been calculated
shared_ints[3] = 2;
}
}
总和计算一直有效,直到我转到总和计算的 fourth-iteration(这是输出):
Created shared memory object /my_shared_memory
Shared memory segment allocated correctly (16 bytes).
Enter your first number:1
Enter your second number:2
The sum is: 3
Enter your first number:Enter your first number:3
Enter your second number:4
The sum is: 7
Enter your first number:Enter your first number:5
Enter your second number:6
The sum is: 11
Enter your first number:Enter your first number:7
Enter your second number:8
Enter your second number:9
Enter your second number:
我看到的一个有趣的错误是,在我打印总和后,程序会按照此处的建议要求输入两次第一个输入:Enter your first number:Enter your first number:3
,然后在第四次求和迭代中,它会要求输入第二个数字在计算总和之前多次。
问题是你在外 while
循环的每次迭代中分叉,所以第一次,你有一个 child,然后你有一个 child认为它已经长大并且是 parent(以及知道它是 parent 的 parent)。随着您的继续,情况会变得更糟。
另一个问题是您在外部 while
循环的每次迭代中分配共享内存。我不确定这到底是不是内存泄漏,但这可能不是个好主意。
修复:
- 将
mmap()
和 fork()
移到 while (1)
循环之外。
- parent 进程将有一个循环 — 在它自己的函数中,请不要全部在
main()
内 — 从用户读取,转移到 child,读取child 的结果,并继续。
- child 进程将有一个循环 — 请也在它自己的函数中 — 从 parent 读取、计算并继续。
- 确保 child 知道如何在 parent 退出时退出。
- 理想情况下,使用忙等待以外的同步机制。几个互斥锁或几个信号量会很好。进程将等待被告知有工作要做,而不是在等待下一个任务时旋转它们的轮子(非常快)。
或多或少的工作代码
这使用了我编写的代码中的 header "stderr.h"
和函数 err_setarg0()
和 err_syserr()
,因为错误报告很重要,这些使它变得容易。您应该能够从我这里找到有关 header 和那些函数的工作版本的代码。
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "stderr.h"
static void be_childish(int *shared_ints);
static void be_parental(int *shared_ints);
int main(int argc, char **argv)
{
const char *file = "fidget";
int shared_seg_size = 4 * sizeof(int);
err_setarg0(argv[0]);
if (argc > 1)
file = argv[1];
int fd = open(file, O_RDWR|O_CREAT, 0600);
if (fd < 0)
err_syserr("Failed to open file %s for read/write\n", file);
/* Assume sizeof(int) == 4 */
if (write(fd, "abcdefghijklmnop", shared_seg_size) != shared_seg_size)
err_syserr("Failed to write %d bytes to %s\n", shared_seg_size, file);
int *shared_ints = mmap(NULL, shared_seg_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shared_ints == 0)
err_syserr("Failed to mmap file %s\n", file);
shared_ints[3] = 0;
int pid;
if ((pid = fork()) == -1)
err_syserr("Failed to fork\n");
else if (pid == 0)
be_childish(shared_ints);
else
be_parental(shared_ints);
return 0;
}
static void be_childish(int *shared_ints)
{
while (1)
{
// Wait for parent to generate input values
while (shared_ints[3] == 0 || shared_ints[3] == 2)
{
printf("Child: %d\n", shared_ints[3]);
usleep(500000);
}
if (shared_ints[3] != 1)
{
printf("Child: exiting\n");
return;
}
// Calculate the sum
shared_ints[2] = shared_ints[0] + shared_ints[1];
printf("Child: calculated %d + %d = %d\n", shared_ints[0], shared_ints[1], shared_ints[2]);
// Tell parent sum has been calculated
shared_ints[3] = 2;
}
}
static void be_parental(int *shared_ints)
{
while (1)
{
int x;
int y;
// Ask user for first input
printf("Enter your first number:");
if (scanf("%d", &x) != 1)
{
printf("Parent: exiting\n");
shared_ints[3] = -1; /* Tell child to exit */
return;
}
printf("\n");
// Ask user for second input
printf("Enter your second number:");
if (scanf("%d", &y) != 1)
{
printf("Parent: exiting\n");
shared_ints[3] = -1; /* Tell child to exit */
return;
}
printf("\n");
// Assign first value to shared memory
shared_ints[0] = x;
// Assign second value to shared memory
shared_ints[1] = y;
// Tell child that it can compute the sum now
shared_ints[3] = 1;
// Trap parent in a while-loop while the child
// computes the sum
while (shared_ints[3] == 1)
{
printf("Parent: %d\n", shared_ints[3]);
usleep(500000);
}
// Child has completed calculating the sum and
// the parent can print the sum
if (shared_ints[3] == 2)
{
printf("The sum is: %d", shared_ints[2]);
printf("\n");
}
else
{
printf("Parent: unexpected control %d - exiting\n", shared_ints[2]);
shared_ints[3] = -1; /* Tell child to exit */
return;
}
}
}
此代码在 busy-wait 循环中进行了调试,每个读取周期有 0.5 秒的延迟。你可以调整它以适合你自己,一旦你确定它也适用于你就失去输出。请注意,除非您将替代名称指定为 created/destroyed,否则代码会破坏文件 fidget
。它在退出时不会删除文件;它可能应该。
样本运行
$ ./dualproc
Enter your first number:Child: 0
1
Enter your second number:Child: 0
2
Parent: 1
Child: calculated 1 + 2 = 3
Child: 2
The sum is: 3
Enter your first number:Child: 2
Child: 2
3
Enter your second number:Child: 2
4
Parent: 1
Child: calculated 3 + 4 = 7
Child: 2
The sum is: 7
Enter your first number:Child: 2
q
Parent: exiting
$ Child: exiting
$
parent 在 child 之前退出;没有什么大惊喜。如果您愿意,可以升级代码以使用 wait()
等待 child 退出。
注意:在 parent 退出并且 child 仍在等待 parents 输入值的极端情况下存在问题 - 如果用户当 shared_ints[3] != 0
时中断程序
为了证明@Jonathan Leffler 的出色回答,这是我的修复建议:
/* These are better declared outside the while loop */
// Declare a shared memory integer array
int *shared_ints = mmap(NULL, shared_seg_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
int x;
int y;
// Fork the process
while(1) {
shared_ints[3] = 0;
if((pid = fork()) < 0) {
printf("Fork error \n");
exit(-1);
} else if(pid > 0 && shared_ints[3] == 0) { // Parent code
// Ask user for first input
printf("Enter your first number:");
scanf("%d", &x);
printf("\n");
// Ask user for second input
printf("Enter your second number:");
scanf("%d", &y);
printf("\n");
// Assign first value to shared memory
shared_ints[0] = x;
// Assign second value to shared memory
shared_ints[1] = y;
// Tell child that it can compute the sum now
shared_ints[3] = 1;
// Trap parent in a while-loop while the child
// computes the sum
while(shared_ints[3] == 1);
// Child has completed calculating the sum and
// the parent can print the sum
if(shared_ints[3] == 2) {
printf("The sum is: %d", shared_ints[2]);
printf("\n");
}
} else { // Child code
// Wait for parent to accept input values
while(shared_ints[3] == 0);
// Calculate the sum
shared_ints[2] = shared_ints[0] + shared_ints[1];
// Tell parent sum has been calculated
shared_ints[3] = 2;
/* Child process should finish, so it doesn't fork
and in the upper while loop. this is a fast way to do it */
exit(0);
}
}
在大 while 循环的每次迭代中,父进程和子进程都在这一行中调用 fork()
:
if((pid = fork()) < 0) {
您在每次迭代后重复生成更多子进程,并且可能会产生意想不到的交互。
正在编写一个非常基本的 C 程序来演示共享内存。该程序需要不断计算两个输入的总和,直到用户执行 ^C
(此处未显示)。
到目前为止,我的 parent 捕获用户输入,然后告诉 child 计算总和,然后 parent 打印总和(作为分配建议)。
附上我的代码。
// Fork the process
while(1) {
// Declare a shared memory integer array
int *shared_ints = mmap(NULL, shared_seg_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
int x;
int y;
shared_ints[3] = 0;
if((pid = fork()) < 0) {
printf("Fork error \n");
exit(-1);
} else if(pid > 0 && shared_ints[3] == 0) { // Parent code
// Ask user for first input
printf("Enter your first number:");
scanf("%d", &x);
printf("\n");
// Ask user for second input
printf("Enter your second number:");
scanf("%d", &y);
printf("\n");
// Assign first value to shared memory
shared_ints[0] = x;
// Assign second value to shared memory
shared_ints[1] = y;
// Tell child that it can compute the sum now
shared_ints[3] = 1;
// Trap parent in a while-loop while the child
// computes the sum
while(shared_ints[3] == 1);
// Child has completed calculating the sum and
// the parent can print the sum
if(shared_ints[3] == 2) {
printf("The sum is: %d", shared_ints[2]);
printf("\n");
}
} else { // Child code
// Wait for parent to accept input values
while(shared_ints[3] == 0);
// Calculate the sum
shared_ints[2] = shared_ints[0] + shared_ints[1];
// Tell parent sum has been calculated
shared_ints[3] = 2;
}
}
总和计算一直有效,直到我转到总和计算的 fourth-iteration(这是输出):
Created shared memory object /my_shared_memory
Shared memory segment allocated correctly (16 bytes).
Enter your first number:1
Enter your second number:2
The sum is: 3
Enter your first number:Enter your first number:3
Enter your second number:4
The sum is: 7
Enter your first number:Enter your first number:5
Enter your second number:6
The sum is: 11
Enter your first number:Enter your first number:7
Enter your second number:8
Enter your second number:9
Enter your second number:
我看到的一个有趣的错误是,在我打印总和后,程序会按照此处的建议要求输入两次第一个输入:Enter your first number:Enter your first number:3
,然后在第四次求和迭代中,它会要求输入第二个数字在计算总和之前多次。
问题是你在外 while
循环的每次迭代中分叉,所以第一次,你有一个 child,然后你有一个 child认为它已经长大并且是 parent(以及知道它是 parent 的 parent)。随着您的继续,情况会变得更糟。
另一个问题是您在外部 while
循环的每次迭代中分配共享内存。我不确定这到底是不是内存泄漏,但这可能不是个好主意。
修复:
- 将
mmap()
和fork()
移到while (1)
循环之外。 - parent 进程将有一个循环 — 在它自己的函数中,请不要全部在
main()
内 — 从用户读取,转移到 child,读取child 的结果,并继续。 - child 进程将有一个循环 — 请也在它自己的函数中 — 从 parent 读取、计算并继续。
- 确保 child 知道如何在 parent 退出时退出。
- 理想情况下,使用忙等待以外的同步机制。几个互斥锁或几个信号量会很好。进程将等待被告知有工作要做,而不是在等待下一个任务时旋转它们的轮子(非常快)。
或多或少的工作代码
这使用了我编写的代码中的 header "stderr.h"
和函数 err_setarg0()
和 err_syserr()
,因为错误报告很重要,这些使它变得容易。您应该能够从我这里找到有关 header 和那些函数的工作版本的代码。
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "stderr.h"
static void be_childish(int *shared_ints);
static void be_parental(int *shared_ints);
int main(int argc, char **argv)
{
const char *file = "fidget";
int shared_seg_size = 4 * sizeof(int);
err_setarg0(argv[0]);
if (argc > 1)
file = argv[1];
int fd = open(file, O_RDWR|O_CREAT, 0600);
if (fd < 0)
err_syserr("Failed to open file %s for read/write\n", file);
/* Assume sizeof(int) == 4 */
if (write(fd, "abcdefghijklmnop", shared_seg_size) != shared_seg_size)
err_syserr("Failed to write %d bytes to %s\n", shared_seg_size, file);
int *shared_ints = mmap(NULL, shared_seg_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shared_ints == 0)
err_syserr("Failed to mmap file %s\n", file);
shared_ints[3] = 0;
int pid;
if ((pid = fork()) == -1)
err_syserr("Failed to fork\n");
else if (pid == 0)
be_childish(shared_ints);
else
be_parental(shared_ints);
return 0;
}
static void be_childish(int *shared_ints)
{
while (1)
{
// Wait for parent to generate input values
while (shared_ints[3] == 0 || shared_ints[3] == 2)
{
printf("Child: %d\n", shared_ints[3]);
usleep(500000);
}
if (shared_ints[3] != 1)
{
printf("Child: exiting\n");
return;
}
// Calculate the sum
shared_ints[2] = shared_ints[0] + shared_ints[1];
printf("Child: calculated %d + %d = %d\n", shared_ints[0], shared_ints[1], shared_ints[2]);
// Tell parent sum has been calculated
shared_ints[3] = 2;
}
}
static void be_parental(int *shared_ints)
{
while (1)
{
int x;
int y;
// Ask user for first input
printf("Enter your first number:");
if (scanf("%d", &x) != 1)
{
printf("Parent: exiting\n");
shared_ints[3] = -1; /* Tell child to exit */
return;
}
printf("\n");
// Ask user for second input
printf("Enter your second number:");
if (scanf("%d", &y) != 1)
{
printf("Parent: exiting\n");
shared_ints[3] = -1; /* Tell child to exit */
return;
}
printf("\n");
// Assign first value to shared memory
shared_ints[0] = x;
// Assign second value to shared memory
shared_ints[1] = y;
// Tell child that it can compute the sum now
shared_ints[3] = 1;
// Trap parent in a while-loop while the child
// computes the sum
while (shared_ints[3] == 1)
{
printf("Parent: %d\n", shared_ints[3]);
usleep(500000);
}
// Child has completed calculating the sum and
// the parent can print the sum
if (shared_ints[3] == 2)
{
printf("The sum is: %d", shared_ints[2]);
printf("\n");
}
else
{
printf("Parent: unexpected control %d - exiting\n", shared_ints[2]);
shared_ints[3] = -1; /* Tell child to exit */
return;
}
}
}
此代码在 busy-wait 循环中进行了调试,每个读取周期有 0.5 秒的延迟。你可以调整它以适合你自己,一旦你确定它也适用于你就失去输出。请注意,除非您将替代名称指定为 created/destroyed,否则代码会破坏文件 fidget
。它在退出时不会删除文件;它可能应该。
样本运行
$ ./dualproc
Enter your first number:Child: 0
1
Enter your second number:Child: 0
2
Parent: 1
Child: calculated 1 + 2 = 3
Child: 2
The sum is: 3
Enter your first number:Child: 2
Child: 2
3
Enter your second number:Child: 2
4
Parent: 1
Child: calculated 3 + 4 = 7
Child: 2
The sum is: 7
Enter your first number:Child: 2
q
Parent: exiting
$ Child: exiting
$
parent 在 child 之前退出;没有什么大惊喜。如果您愿意,可以升级代码以使用 wait()
等待 child 退出。
注意:在 parent 退出并且 child 仍在等待 parents 输入值的极端情况下存在问题 - 如果用户当 shared_ints[3] != 0
时中断程序为了证明@Jonathan Leffler 的出色回答,这是我的修复建议:
/* These are better declared outside the while loop */
// Declare a shared memory integer array
int *shared_ints = mmap(NULL, shared_seg_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
int x;
int y;
// Fork the process
while(1) {
shared_ints[3] = 0;
if((pid = fork()) < 0) {
printf("Fork error \n");
exit(-1);
} else if(pid > 0 && shared_ints[3] == 0) { // Parent code
// Ask user for first input
printf("Enter your first number:");
scanf("%d", &x);
printf("\n");
// Ask user for second input
printf("Enter your second number:");
scanf("%d", &y);
printf("\n");
// Assign first value to shared memory
shared_ints[0] = x;
// Assign second value to shared memory
shared_ints[1] = y;
// Tell child that it can compute the sum now
shared_ints[3] = 1;
// Trap parent in a while-loop while the child
// computes the sum
while(shared_ints[3] == 1);
// Child has completed calculating the sum and
// the parent can print the sum
if(shared_ints[3] == 2) {
printf("The sum is: %d", shared_ints[2]);
printf("\n");
}
} else { // Child code
// Wait for parent to accept input values
while(shared_ints[3] == 0);
// Calculate the sum
shared_ints[2] = shared_ints[0] + shared_ints[1];
// Tell parent sum has been calculated
shared_ints[3] = 2;
/* Child process should finish, so it doesn't fork
and in the upper while loop. this is a fast way to do it */
exit(0);
}
}
在大 while 循环的每次迭代中,父进程和子进程都在这一行中调用 fork()
:
if((pid = fork()) < 0) {
您在每次迭代后重复生成更多子进程,并且可能会产生意想不到的交互。