[CNet-02] Forking for concurrent access
In order to answer our previous challenge at previous article (https://medium.com/@habibiefaried/cnet-01-basic-c-socket-programming-a818b0da5ae0), we can try something simple like this.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main() {
int pid;
pid = fork();
while (1){
printf("Hi from PID %d\n",pid);
sleep(1);
}
return 0;
}
It will fork, create another process and each of those processes will be printing their PID on infinite loop basis
After compilation and running, this is the output
$ gcc weirdfork.c$ ./a.outHi from PID 24602
Hi from PID 0
Hi from PID 0
Hi from PID 24602
Hi from PID 0
Hi from PID 24602
There are 2 separate process on it, we can check using ps
$ ps aux | grep a.out
habibiefaried 24610 0.0 0.0 4268932 368 s000 S+ 11:40PM 0:00.00 ./a.out
habibiefaried 24609 0.0 0.0 4277124 788 s000 S+ 11:40PM 0:00.00 ./a.outAnd now, let’s try to add fork here
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main() {
int pid;
pid = fork();
pid = fork(); pid = fork();
while (1){
printf("Hi from PID %d\n",pid);
sleep(1);
}
return 0;
}
And this is the output
$ ./a.outHi from PID 24636
Hi from PID 0
Hi from PID 24638
Hi from PID 0
Hi from PID 24639
Hi from PID 0
Hi from PID 24640
And this is the output process
$ ps aux | grep a.out
habibiefaried 24640 0.0 0.0 4285316 328 s000 S+ 11:42PM 0:00.00 ./a.out
habibiefaried 24639 0.0 0.0 4277124 332 s000 S+ 11:42PM 0:00.00 ./a.out
habibiefaried 24638 0.0 0.0 4277124 328 s000 S+ 11:42PM 0:00.00 ./a.out
habibiefaried 24637 0.0 0.0 4285316 336 s000 S+ 11:42PM 0:00.00 ./a.out
habibiefaried 24636 0.0 0.0 4277124 380 s000 S+ 11:42PM 0:00.00 ./a.out
habibiefaried 24635 0.0 0.0 4285316 384 s000 S+ 11:42PM 0:00.00 ./a.out
habibiefaried 24634 0.0 0.0 4276100 368 s000 S+ 11:42PM 0:00.00 ./a.out
habibiefaried 24633 0.0 0.0 4276100 776 s000 S+ 11:42PM 0:00.01 ./a.outIt runs with 8 processes, and it’s correct because each fork creates 2 process. It means, if there are 3 forks, then 2³=8 processes will be created like that
Kill the process
This exponential number of process will eventually eat all of the memory allocation on servers quickly, and it’s really a bad news for us.
So, let’s go to the manual here -> http://man7.org/linux/man-pages/man2/fork.2.html and see what we can do.
According to it, forks creates process and return these integers
- <0, it means failure
- =0, that forked process return to child
- >0, that forked process return to parent
Ok, let’s kill one of them. Thus, the code will be looking like this
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main() {
int pid;
pid = fork(); //let it go
pid = fork(); if (pid == 0) return 0; //kill child process
pid = fork(); if (pid == 0) return 0; //kill child process
while (1){
printf("Hi from PID %d\n",pid);
sleep(1);
}
return 0;
}
And the output
$ ./a.out
Hi from PID 24714
Hi from PID 24716
Hi from PID 24716
Hi from PID 24714
Hi from PID 24716
$ ps aux | grep a.outhabibiefaried 24673 0.0 0.0 0 0 s000 Z+ 11:49PM 0:00.00 (a.out)
habibiefaried 24672 0.0 0.0 0 0 s000 Z+ 11:49PM 0:00.00 (a.out)
habibiefaried 24671 0.0 0.0 0 0 s000 Z+ 11:49PM 0:00.00 (a.out)
habibiefaried 24670 0.0 0.0 0 0 s000 Z+ 11:49PM 0:00.00 (a.out)
habibiefaried 24669 0.0 0.0 4277124 380 s000 S+ 11:49PM 0:00.00 ./a.out
habibiefaried 24668 0.0 0.0 4267908 772 s000 S+ 11:49PM 0:00.01 ./a.out
It seems more controlled, doesn’t it? Anyway, the process between parentheses “( )” means that ps is unable to determine its status of the process. And it’s true since we killed that process at beginning.
At the end of the day, you don’t need that child process (0) to be alive. You can use the forked parent process to achieve concurrency objective. Or doing vice versa.
Modifying the program
If you understand what I explained before, the modification is really easy.
Refers to: https://github.com/habibiefaried/sysprousingc/blob/master/2.srv.c
Add this block of code below, put it after the accept code on socket (pay attention to source code above as well)
// Forks process after accept
int pid = fork();
if (pid < 0){
printf("Error while forking");
return 0;
} else if (pid == 0){ //continue on child
close(sockfd);
func(connfd, traffic);
} else { //close the parent
close(connfd);
}As I told you before, I killed fork that failed to be created (pid < 0), and I close the connection fd on parent process (pid > 0). I continue processing to the function on child process (pid == 0). I close the socket fd because technically I don’t need that on this child process.
And the rest, it’s the same thing. I didn’t modify anything except the tracking variable traffic to understand from which PID the data is coming from.
Testing
Now, I’m able to receive the data from all of the connected client here

As you can see above, I sent “client1” “client2” “client3” from different terminal at the same time. And then, I replied 3 times (“test”, “test1”, “test2”) and it’s being sent correctly to respective client.
As comparison, I re-run our previous code again and do the same thing

As you can see, it’s able to receive first connection but not able (yet) to receive second and third connection at same time.
Conclusion
The “forking” concept is pretty clear and easy, and we have everything we need to build our own strong web server.
Let’s go to the last article here -> https://medium.com/@habibiefaried/cnet-03-hello-world-webserver-1616a211cab9. Talking about how to develop web server and use kubernetes to orchestrate the service.