When I run my program:
int i = fork();
if (!i){
int id = open("shared.txt", 0600 | O_WRONLY);
if(flock(id, LOCK_EX | LOCK_NB) == -1)
perror("flock_ch");
if(write(id, "child", 5)) printf("child did it\n");
else perror("write_ch");
sleep(3);
close(id);
}
else {
int id = open("shared.txt", 0600 | O_WRONLY);
if(flock(id, LOCK_EX | LOCK_NB) == -1)
perror("flock_PR");
if(write(id, "parent", 6)) printf("parent did it\n");
else perror("write_pr");
sleep(3);
close(id);
}
wait(NULL);
I expect that only one process will write something in the file, but even if the file is locked, two of processes write in the file.
Both processes are writing to the file because you're ignoring the error return value from flock(). When I run your code, I see:
parent did it
flock_ch: Resource temporarily unavailable
child did it
That flock_ch: Resource temporarily unavailable is because flock() is returning with an error and telling you the file is locked. You're printing out an error message, but not otherwise responding to the error message. You probably want to either (a) exit or (b) loop until flock() succeeds.
Using a loop might look like:
#include <stdio.h>
#include <stdlib.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <unistd.h>
#define LOCK_FLAGS (LOCK_EX | LOCK_NB)
void parent() {
int fd = open("shared.txt", 0600 | O_WRONLY);
while (flock(fd, LOCK_FLAGS) == -1) {
printf("parent waiting for lock\n");
sleep(1);
}
printf("parent acquired lock\n");
if (write(fd, "parent", 6) >= 0) {
printf("parent wrote to file\n");
} else {
perror("parent: write failed:");
exit(1);
}
sleep(3);
close(fd);
}
void child() {
int fd = open("shared.txt", 0600 | O_WRONLY);
while (flock(fd, LOCK_FLAGS) == -1) {
printf("child waiting for lock\n");
sleep(1);
}
printf("child acquired lock\n");
if (write(fd, "child", 6) >= 0) {
printf("child wrote to file\n");
} else {
perror("child: write failed:");
exit(1);
}
sleep(3);
close(fd);
}
int main() {
// ensure "shared.txt" exists
int fd = open("shared.txt", O_CREAT | O_WRONLY, 0600);
close(fd);
pid_t pid = fork();
if (pid == 0) {
child();
exit(0);
}
parent();
wait(NULL);
return 0;
}
Running the above produces:
parent acquired lock
parent wrote to file
child waiting for lock
child waiting for lock
child waiting for lock
child acquired lock
child wrote to file
Alternately, drop the LOCK_NB flag so that flock() blocks until the lock is available. That might look like:
#include <stdio.h>
#include <stdlib.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <unistd.h>
#define LOCK_FLAGS (LOCK_EX) // | LOCK_NB)
void parent() {
int fd = open("shared.txt", 0600 | O_WRONLY);
if (flock(fd, LOCK_FLAGS) == -1) {
perror("parent: flock:");
exit(1);
}
printf("parent acquired lock\n");
if (write(fd, "parent", 6) >= 0) {
printf("parent wrote to file\n");
} else {
perror("parent: write failed:");
exit(1);
}
sleep(3);
close(fd);
}
void child() {
int fd = open("shared.txt", 0600 | O_WRONLY);
if (flock(fd, LOCK_FLAGS) == -1) {
perror("child: flock:");
exit(1);
}
printf("child acquired lock\n");
if (write(fd, "child", 6) >= 0) {
printf("child wrote to file\n");
} else {
perror("child: write failed:");
exit(1);
}
sleep(3);
close(fd);
}
int main() {
// ensure "shared.txt" exists
int fd = open("shared.txt", O_CREAT | O_WRONLY, 0600);
close(fd);
pid_t pid = fork();
if (pid == 0) {
child();
exit(0);
}
parent();
wait(NULL);
return 0;
}
And running the above produces:
parent acquired lock
parent wrote to file
child acquired lock
child wrote to file
Related
Problem - when calling ls -l | grep etc, stuck on grep (grep child process does not exit)
trying to run "ls | grep r" with "execvp()" suggests that
need to close file descriptors
wait outside of the forking loop
IMO I have performed both of above but the problem still exists.
Any opinion is welcome, thanks!
Note that below is a hard-coded version for 2 pipes only
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
int i = 0;
int pfd[2];
if (pipe(pfd) != 0)
{
printf("Error creating pipe\n");
exit(errno);
}
char **ptr = get_pipes(); // pipes as array of strings
char *command = *ptr;
while (command != NULL)
{
if (i == 2)
break; // hard code to ignore all commands after 2nd pipe
char **args = parse_cmd(command); // this parses a space-separated command as arguments
pid_t pid = fork();
if (pid == 0 && i == 0) // 1st pipe, 1st child
{
close(pfd[0]); // close pipe read end
dup2(pfd[1], 1); // set pipe write end to stdout
if (execvp(args[0], args) == -1)
{
fprintf(stderr, "%s: %s\n", args[0], strerror(errno));
exit(EXIT_FAILURE);
}
}
else if (pid == 0 && i == 1) // 2nd pipe, 2nd child
{
close(pfd[1]); // close pipe write end
dup2(pfd[0], 0); // set pipe read end to stdin
if (execvp(args[0], args) == -1)
{
fprintf(stderr, "'%s': %s\n", args[0], strerror(errno));
exit(EXIT_FAILURE);
}
// exit(EXIT_SUCCESS);
}
else if (pid > 0) // parent
{
printf("Parent pid: %d and child's pid is %d\n", (int)getpid(), (int)pid);
}
command = *++ptr;
i++;
}
pid_t zombie_pid;
int status;
do
{
zombie_pid = waitpid(-1, &status, 0);
printf("Child PID %d died with status %d\n", (int)zombie_pid, WEXITSTATUS(status));
} while (zombie_pid > 0);
}
I'm currently having problems with the following exercise:
I want to "mimic" the pipe command line ls | wc in linux bash with the following program.
What I do is:
create a pipe
create a reader child and writer child
the writer child closes the pipe's reading side and redirects his stdout to the writing side of the pipe
the reader child closes the pipe's writing side and makes the reading side of the pipe his stdin
both children do an exec, the writer executes the ls program, passes the output through the pipe to the reader that executes the wc program on that output.
When I do ls | wc in linux terminal I get the following result:
8 8 101
But if I execute my program I get the following result:
0 0 0
Here is my program:
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>
#include <libgen.h>
#include <signal.h>
#include <errno.h>
int main(void){
int mypipe[2];
pid_t pid1, pid2;
if (pipe(mypipe)<0)
perror ("pipe error"), exit(1);
if ((pid1=fork())<0)
perror ("fork error"), exit(1);
else if (pid1==0) {
//reader child
close (mypipe[1]);
if (dup2(mypipe[0], STDIN_FILENO)!=STDIN_FILENO)
perror ("dup2 error"), exit(1);
close (mypipe[0]);
if (execlp("wc", "wc", NULL)<0)
perror("execlp1 error"), exit(1);
else { //pid >0, parent
if ((pid2=fork())<0)
perror ("fork error"), exit(2);
else if (pid2==0) {
//writer child
close(mypipe[0]);
if (dup2(mypipe[1], STDOUT_FILENO) != STDOUT_FILENO)
perror("dup2 error"), exit(1);
close (mypipe[1]);
if (execlp("ls", "ls", NULL)<0)
perror ("execlp error"), exit(1);
}
else { //parent
close(mypipe[0]);
close(mypipe[1]);
waitpid(pid1, NULL, 0);
waitpid(pid2, NULL, 0);
exit(0);
}
}
}
return 0;
}
What am I doing wrong? Thanks in advance for the answers!
Your code is confused. You have many irrelevant headers, and repeat #include <errno.h>. In the main() function, you have:
int main(void)
{
int mypipe[2];
pid_t pid1, pid2;
if (pipe(mypipe) < 0)
perror("pipe error"), exit(1);
if ((pid1 = fork()) < 0)
perror("fork error"), exit(1);
else if (pid1 == 0)
{
// reader child
close(mypipe[1]);
if (dup2(mypipe[0], STDIN_FILENO) != STDIN_FILENO)
perror("dup2 error"), exit(1);
close(mypipe[0]);
if (execlp("wc", "wc", NULL) < 0)
perror("execlp1 error"), exit(1);
else // pid >0, parent
{
…
}
}
return 0;
}
The else if (pid1 == 0) clause is executed by the child. It closes the write end of the pipe, duplicates the read end to standard input and closes the read end of the pipe. It then does execlp() on wc. Only if the code fails to execute wc will the else clause be executed, and then there is only the read end of the pipe left open. Meanwhile, the original process simply exits. That closes the pipe, so the wc command gets no input, and reports 0 0 0 as a result.
You need to rewrite the code. The parent process should wait until both its children execute. Especially while debugging, you should not ignore the exit status of children, and you should report it.
Here's some code that works. Note that it avoids bushy decision trees — it is a linear sequence of if / else if / … / else code. This is easier to understand, in general, than a bushy, multi-level set of conditions.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void)
{
int fd[2];
pid_t pid1, pid2;
if (pipe(fd) < 0)
perror("pipe error"), exit(1);
else if ((pid1 = fork()) < 0)
perror("fork error"), exit(1);
else if (pid1 == 0)
{
/* ls */
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
close(fd[1]);
execlp("ls", "ls", (char *)0);
perror("exec ls");
exit(1);
}
else if ((pid2 = fork()) < 0)
perror("fork error"), exit(1);
else if (pid2 == 0)
{
/* wc */
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
close(fd[1]);
execlp("wc", "wc", (char *)0);
perror("exec wc");
exit(1);
}
else
{
close(fd[0]);
close(fd[1]);
int status1;
int status2;
int corpse1 = waitpid(pid1, &status1, 0);
int corpse2 = waitpid(pid2, &status2, 0);
printf("ls: pid = %d, corpse = %d, exit status = 0x%.4X\n", pid1, corpse1, status1);
printf("ls: pid = %d, corpse = %d, exit status = 0x%.4X\n", pid2, corpse2, status2);
}
return 0;
}
A sample run of the program pipe41 on my machine produced:
$ pipe41
175 175 1954
ls: pid = 44770, corpse = 44770, exit status = 0x0000
ls: pid = 44771, corpse = 44771, exit status = 0x0000
$ ls | wc
175 175 1954
$
So the following code is supposed to do the following:
The main process creates N_READ number of children processes and then proceeds to print some lines endlessly.
The children also print some lines endlessly.
When the main process receives a SIGINT signal (CTRL^C from the terminal in this case) he must send a SIGTERM to all his children, wait for them to finish and then delete all the semaphores used.
The use of named semaphores is justified by the fact that this is simulation of the readers-writers problem giving priority to the readers.
However when I end the program and start it again the semaphores have not been deleted, as I get an "semaphore already exists" error and they are in fact in /dev/shm.
P.D. This is college homework and they say global variables are not permitted.
Thank you in advance and here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#define SEM1 "/example_sem1"
#define SEM2 "/example_sem2"
#define SEM3 "/example_sem3"
#define N_READ 1
#define SECS 0
int valor_semaforo(sem_t *sem) {
int sval;
if (sem_getvalue(sem, &sval) == -1) {
perror("sem_getvalue");
sem_unlink(SEM1);
sem_unlink(SEM2);
sem_unlink(SEM3);
exit(EXIT_FAILURE);
}
return sval;
}
void manejador_SIGINT(int sig) {
kill(0,SIGTERM);
while(wait(NULL)>0);
/*xq no llega xq*/
sem_unlink(SEM1);
sem_unlink(SEM2);
sem_unlink(SEM3);
exit(EXIT_SUCCESS);
}
int main(void) {
sem_t *sem_write = NULL,*sem_read = NULL,*sem_count = NULL;
pid_t pid[N_READ];
int i;
struct sigaction act;
sigemptyset(&(act.sa_mask));
act.sa_flags = 0;
act.sa_handler = manejador_SIGINT;
if (sigaction(SIGINT, &act, NULL) < 0) {
perror("sigaction");
exit(EXIT_FAILURE);
}
if ((sem_write = sem_open(SEM1, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, 1)) == SEM_FAILED) {
perror("sem_open");
exit(EXIT_FAILURE);
}
if ((sem_read = sem_open(SEM2, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, 1)) == SEM_FAILED) {
perror("sem_open");
exit(EXIT_FAILURE);
}
if ((sem_count = sem_open(SEM3, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, 0)) == SEM_FAILED) {
perror("sem_open");
exit(EXIT_FAILURE);
}
for(i=0;i<N_READ;i++){
pid[i] = fork();
if (pid[i] < 0) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid[i] == 0) {
sigemptyset(&(act.sa_mask));
act.sa_flags = 0;
act.sa_handler = SIG_IGN;
if (sigaction(SIGINT, &act, NULL) < 0) {
perror("sigaction");
exit(EXIT_FAILURE);
}
while(1){
sem_wait(sem_read);
sem_post(sem_count);
if(valor_semaforo(sem_count)==1)
sem_wait(sem_write);
sem_post(sem_read);
printf("R-INI %d\n",getpid());
fflush(stdout);
sleep(SECS);
printf("R-FIN %d\n",getpid());
fflush(stdout);
sem_wait(sem_read);
sem_wait(sem_count);
if(valor_semaforo(sem_count)==0)
sem_post(sem_write);
sem_post(sem_read);
}
}
}
while(1){
sem_wait(sem_write);
printf("W-INI %d\n",getpid());
fflush(stdout);
sleep(SECS);
printf("W-FIN %d\n",getpid());
fflush(stdout);
sem_post(sem_write);
}
}
As #Jonathan said, the SIGTERM signal I use does terminate the process that should clean up the semaphores. By ignoring the signal in the parent process, everything works well.
I'm trying to make a FIFO between two programs (one being a child process of the other) so that the child can write data back to the parent. Here's what I have so far:
(Parent)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#define MAX_BUF 1024
int main(int argc, char *argv[]) {
//number of seperate processes to create
int num_processes = 4;
int i = 0;
//FIFO accross processes
int fd;
char * myfifo = "/tmp/myfifo";
char buf[MAX_BUF];
/* create the FIFO (named pipe) */
mkfifo(myfifo, 0666);
for (i; i < num_processes; i++) {
pid_t pid = fork();
if (pid < 0) {
perror("fork failed");
exit(1);
}
else if (pid == 0) {
//child now exec's
char* args[] = {"./child", "args", NULL};
execv("./child", args);
}
}
printf("Parent doing stuff\n");
//Parent wait for child
printf("Parent waiting on child\n");
/* open, read, and display the message from the FIFO */
fd = open(myfifo, O_RDONLY);
if (fcntl(fd, F_GETFD) == -1) {
perror("fd failed");
exit(1);
}
read(fd, buf, MAX_BUF);
printf("Received: %s\n", buf);
//Wait for child processes to finish
int j = 0;
for (j; j < num_processes; j++) {
wait(NULL);
}
//Close FIFO
close(fd);
return 0;
}
(Child, created 4 times)
void main() {
printf("Completed\n");
//Create FIFO
int fd;
char * myfifo = "/tmp/myfifo";
/* write "Hi" to the FIFO */
fd = open(myfifo, O_WRONLY);
if (fcntl(fd, F_GETFD) == -1) {
perror("open failed");
exit(1);
}
write(fd, "Hi", sizeof("Hi"));
//close(fd);
/* remove the FIFO */
//unlink(myfifo);
}
Right now, "Completed" is being printed 4 times, showing that there are 4 seperate processes running as there should be. However, only one "Received: Hi" is printed in the terminal. How come I am not getting a FIFO response from the other processes?
Any help would be greatly appreciated!
You need to check fd and make sure the open succeeded. And note that it can only succeed once, because the first child will unlink(myfifo).
The parent should also wait for all of the children to finish before reading from the fifo. And the parent should read the fifo in a loop until the fifo is empty.
The problem in your code is that there are multiple child writing to the same FIFO.
As pointed out also by user3386109 you have to wait each child and read the FIFO.
here is a sample code:
//Wait for child processes to finish
int child_status = 0;
while (wait(&child_status) != -1) {
if (WIFEXITED (child_status)) {
fprintf (stdout, "the child process exited normally, with exit code %d\n", WEXITSTATUS (child_status));
// Read The buffer
read(fd, buf, MAX_BUF);
printf("Received: %s\n", buf);
}
else fprintf (stderr, "the child process exited abnormally\n");
}
I also suggest to pass to the child an id (this is just a sample add checks if needed):
else if (pid == 0) {
//child now exec's
char mypid[10];
snprintf(mypid, 10, "%d", i);
char* args[] = {"./child", mypid, NULL};
execv("./child", args);
sleep(1);
That each child read in argv[1]
int mypid = atoi(argv[1]);
Please, see also this post: C Named pipe (fifo). Parent process gets stuck
Solved by putting my read statements into the loop waiting for the child processes to finish:
/* open, read, and display the message from the FIFO */
fd = open(myfifo, O_RDONLY);
if (fcntl(fd, F_GETFD) == -1) {
perror("fd failed");
exit(1);
}
//Wait for child processes to finish
int j = 0;
for (j; j < num_processes; j++) {
read(fd, buf, MAX_BUF);
printf("Received: %s\n", buf);
wait(NULL);
}
//Close
close(fd);
return 0;
This is my code.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
int file,parentID,childID;
pid_t pid;
int main(int argc, char *argv[])
{
if( argc != 2 )
{
printf("ERROR ! You have not write an argument\n");
printf("ERROR ! You give more than one argument");
return 1;
}
file = open(argv[1], O_RDONLY); //open file
if(file<0) //test the file
{
printf("Error open file\n");
printf("ERROR : %s\n", strerror(errno));
return 1;
}
// ---------------------------------------------------------------------
pid = fork();
if( pid == -1) //error fork
{
printf("Error fork\n");
return 1;
}
if(pid == 0) // child process
{
childID = getpid();
printf("Child process %d\n",childID);
// if(childID %2 == 1)
// {
// parentID = getppid();
// printf("Process of father of this child= %d\n",parentID);
// }
}
if( pid == 1)
{
parentID = getppid();
printf("ParentProcess %d\n",parentID);
}
}
I have to write a program to create a child process.Depending on the parity of the child process , the parent should transmit to child a message through a file , the message being taken over and showed by the child process( if the child process is a number that is divizible with 2 it will say -"Good morning!" else "Good night!" ).The parent should wait for the final execution of the child to terminate.
I'm trying really hard to do this exercise and i can't find anywere to explain me how or what function/structure object should i use to do this.Above i tried but i failed , and i understand somehow how fork does but... please help me with this code , or suggest me were should i go to read to make this exercise .Sorry for my bad english spelling.
What documentation are you using for the system calls?
There are a number of ways to do this, but what you probably want to do is create a pipe, and then fork the process. Since a fork copies everything, and child processes inherit the environment, each process has a copy of the file descriptors for the pipe. You can then read/write based on the return value of fork().
int main(int argc, char *argv[])
{
int fd[2];
char in[128], out[128];
if( argc != 2 )
{
printf("ERROR ! You have not write an argument\n");
printf("ERROR ! You give more than one argument");
return 1;
}
if (pipe(fd) == -1)
return 1;
// ---------------------------------------------------------------------
pid = fork();
if (!pid)
read(fd[0], in, 128);
else
write(fd[1], out, strlen(out) + 1);
pipe(2)
note, you usually want to close the file descriptor you're not using for one way communication
I think this is the code.
#include <stdio.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void sighandler (int sig)
{
if (sig == SIGUSR1)
{
FILE *f;
char line[100];
f = fopen("file","r");
fgets(line, 100, f);
printf ("Procesul copil cu pid %d a primit mesajul %s", getpid(), line);
fclose(f);
}
}
int main ()
{
pid_t pid;
FILE * f;
pid = fork();
if (pid < 0)
{
perror ("Eroare la fork()");
return (1);
}
else
if (pid == 0)
{
signal (SIGUSR1, sighandler);
pause();
return 0;
}
else
{
if (pid % 2 == 0)
{
printf ("Notificam procesul fiu cu pid %d", pid);
f = fopen ("file","w");
fprintf (f,"Good morning!");
fclose(f);
kill (pid, SIGUSR1);
}
else
{
printf ("Notificam procesul fiu cu pid %d", pid);
f = fopen ("file","w");
fprintf (f,"Good night!");
fclose(f);
kill (pid, SIGUSR1);
}
}
wait(NULL);
return 0;
}