I have this basic code about parent/child relation:
#define _DEFAULT_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/types.h>
int main(){
int sum = 6;
int *p = (int*) mmap(NULL, sizeof (int) , PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
*p = 7;
printf("filled %d in memory\n", *p);
int pID = fork();
if (pID == 0){
printf(" found %d\n", *p);
printf(" sum inside %d\n",sum + (*p));
exit(0);
}
else if (pID> 0){
wait(NULL);
printf("sum %d\n",sum+ *p);
printf("exit\n");
exit(0);
}
return 0;
}
when i compile it in terminal it gives me this result:
filled 7 in memory
found 7
sum inside 13
sum 13
exit
but when i want to keep that output in a .txt file with ./a.out > output.txt command, my code seems to run filling block twice and my output.txt file is filled by this:
filled 7 in memory
found 7
sum inside 13
filled 7 in memory //**printed twice
sum 13
exit
How can I solve it?
This has nothing to do with mmap, it's purely about your use of fork.
fork doesn't flush your buffers for you, so if you fork with unflushed data in your output buffers, the child will contain the same data and will eventually flush it. An easy solution in this case is to just manually flush stdout just before the fork:
// ...
printf("filled %d in memory\n", *p);
fflush(stdout); // To flush all output streams in more complicated cases: fflush(NULL)
int pID = fork();
// ...
The reason you only saw it when writing to a file is that, when stdout is connected to a terminal (on most systems) it's line-buffered, and the newline in your printf ensured it flushed. When it's not connected to a terminal, it's typically block buffered (so the data was still in the buffer when the fork occurred).
Related
I want to write something in process A and read it in process B which is forked by A. But I find that B cannot read the content unless A is terminated. How can I write in process A while read in B without A quitting? My code is as follows.
#include <stdio.h>
#include <unistd.h>
int mypipe[2];
int main(){
FILE* f;
pid_t pid = 0;
int num = 0, temp;
pipe(mypipe);
pid = fork();
if (pid == (pid_t)0){
f = fdopen(mypipe[0], "r");
while (1){
fscanf(f, "%d", &temp);
printf("from child: %d\n", temp);
}
fclose(f);
}
else{
f = fdopen(mypipe[1], "w");
while (1){
scanf("%d", &num);
fprintf(f, "%d\n", num);
//break;
}
fclose(f);
}
return 0;
}
B cannot read the content unless A is terminated
This is because A doesn't actually write something. stdio streams (represented by FILE *) are buffered. You can set the buffering mode using setvbuf(). The stream you open on your pipe with fdopen() will be fully buffered by default, so no actual writes to the pipe occur until the buffer is full.
The easiest solution is to put a call to fflush() in your code anywhere you want writes to occur, e.g. in your code directly after the fprintf():
while (1){
scanf("%d", &num);
fprintf(f, "%d\n", num);
fflush(f);
//break;
}
First of all there are many unnecessary lines in your code. Instead of doing fdopen() you can directly use read() and write() system calls. Secondly fork() system call return 0 to the child and pid of the child process to the parent. Finally parent should wait for child to finish and clear it from memory to stop it becoming zombie.
This is the corrected version of your code works perfectly without break.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
int status; // To get return status of file
// No need of FILE pointer
int mypipe[2]; // No need to make it global
pid_t pid = 0;
int num = 0, temp;
pipe(mypipe); // Create pipe
if((pid = fork()) == 0) // Child block
{
close(mypipe[1]); // Child will close the writing end
while (1)
{
read(mypipe[0],&temp,sizeof(int));
printf("from child: %d\n",temp);
}
exit(0); // Exit the child
}
else // Parent block
{
close(mypipe[0]); // Parent will close the reading end of pipe
while (1)
{
scanf("%d", &num);
write(mypipe[1],&num,sizeof(int));
}
}
// Parent should wait for child
wait(&status);
return 0;
}
Read the system calls man pages
man 2 read, man 2 write etc.
I'm supposed to create two programs (main and aux), where main forks a child to execute aux. The parent takes input from the user, until blank line '\n', and the child executes aux, which is supposed to print the input back out. I'm able to get it to work in main with the commented code instead of execlp(), but cannot get execlp(aux) to work correctly. Any help is appreciated.
"main.c"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main() {
int fd[2], i;
char line[100], buffer[100];
pipe(fd);
pid_t pid = fork();
if (pid < 0) {
printf("Fork Failed\n");
exit(-1);
}
else if (pid > 0) {
close(fd[0]);
while(fgets(line, sizeof(line), stdin) && line[0] != '\n') {
write(fd[1], line, sizeof(line));
}
close(fd[1]);
}
else {
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
//while(read(fd[0], buffer, sizeof(buffer)))
// printf("> %s", buffer);
execlp("./aux", "aux", (char *)0);
}
return 0;
}
"aux.c"
#include <stdio.h>
#include <stdlib.h>
int main() {
char data[100];
while(fgets(data, sizeof(data), stdin))
printf(">%s\n", data);
return 0;
}
sample input/output
this
>this
is a test
>
> test
only prints larger text with random \n
>
>ts larger text with random \n
Your call to write(2) is wrong (you always write 100 bytes even for shorter line-s):
write(fd[1], line, sizeof(line)); // WRONG
should probably be using strlen(3)
size_t ll = strlen(line);
ssize_t wc = write(fd[1], line, ll);
if (wc != ll)
fprintf(stderr, "write was wrong (only %d, wanted %d) - %s\n",
(int) wc, (int) ll, strerror(errno));
Since you want to write only the filled bytes of the line buffer, not always 100 bytes each time (some of them not being initialized).
In your case sizeof(data) is 100 since you declared char data[100];
Please read carefully the documentation of every used function (and also ALP or some other book on Unix/POSIX/Linux programming). The documentation of strerror(3) and of errno(3) tells that you need to add:
#include <string.h>
#include <errno.h>
Actually, if you want to use read(2) and write(2) directly (without stdio(3)) you should prefer using larger buffers (e.g. 4Kbytes each at least for efficiency) and you need to manage partial read-s and write-s and do your buffering by yourself.
BTW, compile with all warnings and debug info: gcc -Wall -Wextra -g and learn to use the gdb debugger and strace(1) (and valgrind). In general, be scared of undefined behavior (however, at a first glance, your program don't seem to have UB).
Notice that execlp(3) could fail. Consider adding some call to perror(3) after it.
I have an assignment requiring me to write a multi-processed program that works with a memory-mapped file containing a string of characters. After the parent process maps the file to memory, it spawns 2 children processes to modify the file. Child 1 outputs the contents of the file, converts the file's contents to their upper case equivalent, then outputs the file's new contents. Child 2 waits 1 second to let child 1 finish, outputs the file's contents, removes any hyphen " - " characters, then outputs the file's new contents. My problem with both child processes is that after first displaying the file's contents, the processes attempt to modify the contents of the file, but neither child outputs the file's new contents. I get no errors when running or compiling so I can't find out what the problem is. And of course, I'm new to memory mapping so feel free to let me know what I'm doing wrong. Here is my source code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <signal.h>
#include <string.h>
int main (int argc, char *argv[]) {
struct stat buf;
int fd, length, status, i, j, k;
char *mm_file;
char *string = "this is a lowercase-sentence.";
length = strlen(string);
fd = open(argv[1], O_CREAT | O_RDWR, 0666); //Creates file with name given at command line
write(fd, string, strlen(string)); //Writes the string to be modified to the file
fstat(fd, &buf); //used to determine the size of the file
//Establishes the mapping
if ((mm_file = mmap(0, (size_t) buf.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == (caddr_t) - 1) {
fprintf(stderr, "mmap call fails\n");
}
//Initializes child processes
pid_t MC0;
pid_t MC1;
//Creates series of child processes which share the same parent ID
if((MC0 = fork()) == 0) {
printf("Child 1 %d reads: \n %s\n", getpid(), mm_file);
//{convert file content to uppercase string};
for (i = 0; i < length; i++) {
string[i] = toupper(string[i]);
}
//sync the new contents to the file
msync(0, (size_t) buf.st_size, MS_SYNC);
printf("Child 1 %d reads again: \n %s\n", getpid(), mm_file);
exit(EXIT_SUCCESS); //Exits process
} else if ((MC1 = fork()) == 0) {
sleep(1); //so that child 2 will perform its task after child 1 finishes
("Child 2 %d reads: \n %s\n", getpid(), mm_file);
//{remove hyphens}
for (j = 0; j < length; i++) {
if (string[i] == '-') {
string[i] = ' ';
}
}
//sync the new contents to the file
msync(0, (size_t) buf.st_size, MS_SYNC);
printf("Child 2 %d reads again: \n %s\n", getpid(), mm_file);
exit(EXIT_SUCCESS); //Exits process
}
// Waits for all child processes to finish before continuing.
waitpid(MC0, &status, 0);
waitpid(MC1, &status, 0);
return 0;
}
Then my output is as follows:
**virtual-machine:~$** ./testt file
Child 1 3404 reads:
this is a lowercase-sentence.
Child 2 3405 reads:
this is a lowercase-sentence.
All child processes have finished. Now exiting program.
**virtual-machine:~$**
But my desired result would be:
**virtual-machine:~$** ./testt file
Child 1 3404 reads:
this is a lowercase-sentence.
Child 1 3404 reads again:
THIS IS A LOWERCASE-SENTENCE.
Child 2 3405 reads:
THIS IS A LOWERCASE-SENTENCE.
Child 2 3405 reads:
THIS IS A LOWERCASE SENTENCE.
All child processes have finished. Now exiting program.
**virtual-machine:~$**
Any help is greatly appreciated.
There are a few errors here. Firstly, you write into the file and then map it into the memory. The mapping is correct, but the writing not. If the string has n characters, you have to write n+1 characters, since strings in C are null-terminated. Now you only have n, so all C string functions will try to access at least one more byte, which is not good. And if that one extra byte is not null (zero), the functions will go even further. In debug more they might be zeroed, but in optimized code usually not. So you have to use
write(fd, string, strlen(string)+1); //Writes the string to be modified to the file
Then you do this:
for (i = 0; i < length; i++) {
string[i] = toupper(string[i]);
}
This only changes the data that is referred by the pointer string, which has nothing to do with the memory mapped file. You should have:
for (i = 0; i < length; i++) {
mm_file[i] = toupper(mm_file[i]);
}
The same is with the second child process.
Also your msync() call is a bit suspect. You give the memory address as 0, which is not within your memory mapped file, so it will not sync the contents. You need to call msync(mm_file, (size_t) buf.st_size, MS_SYNC);
Also, many compilers will put the constant strings into read-only memory, so you might not even be allowed to change the data referred to by string. In this case it seems you are allowed.
Do also remember, that the length of the file is one byte larger than the length of the string, so use the variables correctly. Currently you do, since you sync the file with file length and handle the string with string length.
You have let mem map get in the way of the logic.
To get this working comment out all mem map stuff and just work on the file.
This will show you that neither child reads from the input file, never mind writing new contents to it.
Under some operating systems, Linux being one if you are mixing reads and writes you need a seek between to keep the write and read pointers in the same position. This may have to be fseek(stream, 0,SEEK_CUR);
Child one should be something like
// Lock file here
rewind(file);
printf ("child 1 reads ");
int ch;
while(1){
ch = fgetc(file);
if(ch == EOF) break;
fputc (Ch,stdout );
}
fputc('\n',stdout );
Rewind(file);
while(1){
ch=fgetc (file);
if(Ch == EOF) break;
fseek(file,-1,SEEK_CUR);
fputc (toupper (Ch),file);
fseek(file,0,SEEK_CUR);
}
Rewind ( file);
Printf (" child 1 reads ");
while(1){
ch=fgetc(file);
if(Ch == EOF) break;
fputc (Ch,file) ;
}
// Unlock file here
Because you have multiple processes acting on the same object you have to implement write locking ( exclusive locks).
Read man pages flock(2), fcntl (2) and lockf(3).
These cooperative locks could be implemented as a semaphore.
With out locking both children may try to write to the same character simultaneously, in this example it shouldn't matter as one child does hyphens and the other letters.
Now it's working uncomment your mem map stuff.
I'm trying to send a bi-dimensional array from a child process to a parent process and failing terribly. Not exactly sure how this should be done but here's what I tried.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
int fd[2];
int matrix[2][2];
int main () {
pipe (fd);
if (0 == fork()) {
printf ("Start child process with pid: %d\n", getpid());
for (int i = 0; i < 2; i++)
matrix[i][i] = 1;
write (fd[1], matrix, 4);
exit (0);
}
printf ("Start parent process with pid: %d\n", getpid());
read (fd[0], matrix, 4);
printf ("Received %d\n", matrix[1][1]);
return 0;
}
Compiles correctly but the received value is always 0 instead of 1.
What am I doing wrong ? (lots of things, I expect)
It's because you are misunderstanding the third argument of read and write
to be number of elements. read and write system calls require their third argument to be count of bytes.
Change your code
write (fd[1], matrix, 4);
...
read (fd[0], matrix, 4);
to
write (fd[1], matrix, 4*sizeof(int));
...
read (fd[0], matrix, 4*sizeof(int));
I want to do the following:
Parent process creates a child process. Then the child process reads n int's from the user and store them in a shared memory. The parent process then displays them.
I reached the following:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#define SHMSIZE 27
int main() {
int shmid;
int *shm;
int *n;
if(fork() == 0) {
shmid = shmget(2009, SHMSIZE, 0);
shm = shmat(shmid, 0, 0);
n = shm;
int i;
for(i=0; i<5; i++) {
printf("Enter number<%i>: ", i);
scanf("%d", n++);
}
printf ("Child wrote <%d>\n",shm);
shmdt(shm);
}
else {
wait();
int *s;
shmid = shmget(2009, SHMSIZE, 0666 | IPC_CREAT);
shm = shmat(shmid, 0, 0);
s = shm;
wait(NULL);
printf ("Parent reads <%d>\n",shm) ;
shmdt(shm);
shmctl(shmid, IPC_RMID, NULL);
}
return 0;
}
And the output is just this line:
Enter number<1>:
And if I entered a number, let's say 25, it outputs this:
Parent reads <r>
r: random -ve number changes every time I execute the code
It never went through the child process code ! Am I doing this in a wrong way ?
Ok, better collect in an answer instead...
There are several problems with you program. If you enable warnings when building (I use -Wall -Wextra) a lot of them will be quite evident.
The first two problems I already mentioned in my comments, but I explain them here:
The first is the call to wait(). There is no wait function in C or POSIX that takes no argument.
The second problem is the scanf call, you are calling it with *++, where *n takes the value of the memory pointed to by n which most likely can result in a crash. Remove the asterisk.
The third problem is that you treat the shared memory as both an array of integers (with n) and as a string. You cant really do both, pick one or the other.
You create the shared memory in the parent process, but wait for the child process to finish before you create the memory.
There is a race condition between the parent and child process, since the share memory might be created after the child tries to access it.
Edit I came up with this instead, which seems to work for me. I added comments on the things I changed.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <sys/wait.h> /* Needed for the wait function */
#include <unistd.h> /* needed for the fork function */
#include <string.h> /* needed for the strcat function */
#define SHMSIZE 27
int main() {
int shmid;
char *shm;
if(fork() == 0) {
shmid = shmget(2009, SHMSIZE, 0);
shm = shmat(shmid, 0, 0);
char *s = (char *) shm;
*s = '\0'; /* Set first location to string terminator, for later append */
int i;
for(i=0; i<5; i++) {
int n; /* Variable to get the number into */
printf("Enter number<%i>: ", i);
scanf("%d", &n);
char number[20];
sprintf(number, "%d", n); /* Convert the number to string */
strcat(s, number); /* Append the number to the string */
}
strcat(s, "\n"); /* Append newline */
printf ("Child wrote <%s>\n",shm);
shmdt(shm);
}
else {
/* Variable s removed, it wasn't used */
/* Removed first call to wait as it held up parent process */
shmid = shmget(2009, SHMSIZE, 0666 | IPC_CREAT);
shm = shmat(shmid, 0, 0);
wait(NULL);
printf ("Parent reads <%s>\n",shm) ;
shmdt(shm);
shmctl(shmid, IPC_RMID, NULL);
}
return 0;
}
Do note that point 5 in the list above have not been resolved.
My problem was so stupid. I need to provide the Child process with the ability to write into the SHM. This line in the if-block :
shmid = shmget(2009, SHMSIZE, 0);
Will become like this:
shmid = shmget(2009, SHMSIZE, 0666 | IPC_CREAT);
Thanks to you all and especially to #JoachimPileborg :)
Your description seems to not be correct since there is no code that outputs "Parent Wrote <>".
You are reading numbers and storing them as int in *n++, but then you are appending a '\n' character to the n-int array and you are treating shm as a string?
It seems to me that in your child you are creating a shared memory, writing to it and then closing (discarding) the shared memory. Then your second part opens a new shared memory with the same segment, but yet it is a new shared memory. Normally one process creates a shared memory, then the second opens it and when the last process closes the shared memory, then it is freed by the OS.
One problem is that the child process is attempting to use get the shared memory before it has been created by the parent. The parent has a wait() call before creating the shared memory, so it won't exist when the client tries to retrieve the id. Even if the wait() call is moved, it may not work because there is a race condition. The call to shmget may need to precede the fork call (or use some synchronization to make sure it actually exists before retrieving it in the child process).
And (as others have already pointed out), the child is attempting to write integers to the memory while the reading (printing of it) tries to treat it as a character string.