Difference between revisions of "Fork system call"
From Teknologisk videncenter
m |
m (→What is fork) |
||
(10 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
− | + | {{TOCright}} | |
− | * | + | '''fork()''' is one of several functions to "spawn" a process |
− | = | + | =Use of fork= |
+ | ==Magic or Qbit?== | ||
+ | <source lang=c line> | ||
+ | #include <stdio.h> | ||
+ | #include <sys/types.h> | ||
+ | #include <unistd.h> | ||
+ | |||
+ | int main(void) { | ||
+ | if (fork()) { | ||
+ | printf("True\n"); | ||
+ | } else { | ||
+ | printf("False\n"); | ||
+ | } | ||
+ | return(0); | ||
+ | } | ||
+ | </source> | ||
+ | When running the above code: | ||
+ | <source lang=bash> | ||
+ | heth@emb3:~/bin/$ gcc process0.c -o process0 | ||
+ | heth@emb3:~/bin/$ ./process0 | ||
+ | True | ||
+ | False | ||
+ | </source> | ||
+ | |||
+ | ==What is fork== | ||
+ | [[Fork system call|fork()]] creates a new process by duplicating the calling process. The new process is referred to as the child process. The calling process is referred to as the parent process. | ||
+ | *PID - Process ID - the ID of a running process | ||
+ | *PPID - Parent Process ID - default the ID of the process that created this process or 1 if the parent died. | ||
+ | |||
+ | <source lang=c line> | ||
+ | #include <stdio.h> | ||
+ | #include <sys/types.h> | ||
+ | #include <unistd.h> | ||
+ | |||
+ | int main(void) { | ||
+ | int pid; | ||
+ | |||
+ | printf("Parent: PPID is %d and PID is %d\n", getppid(), getpid() ); | ||
+ | |||
+ | if ((pid=fork()) > 0) { | ||
+ | printf("Parent: PPID is %d and PID is %d\n", getppid(), getpid() ); | ||
+ | printf("Parent: I got a child with PID %d\n", pid); | ||
+ | } else { | ||
+ | printf("Child.: My parent is PPID %d and my PID is %d\n", getppid(), getpid() ); | ||
+ | } | ||
+ | return(0); | ||
+ | } | ||
+ | </source> | ||
+ | When running this code - different results can occur: | ||
<source lang=bash> | <source lang=bash> | ||
− | heth@emb3:~/bin/ | + | heth@emb3:~/bin/$ gcc process1.c -o process1 |
− | + | heth@emb3:~/bin/$ ./process1 | |
− | + | Parent: PPID is 3480753 and PID is 3490474 | |
+ | Parent: PPID is 3480753 and PID is 3490474 | ||
+ | Child.: My parent is PPID 3490474 and my PID is 3490475 | ||
+ | Parent: I got a child with PID 3490475 | ||
+ | heth@emb3:~/bin$ ./process1 | ||
+ | Parent: PPID is 3480753 and PID is 3490481 | ||
+ | Parent: PPID is 3480753 and PID is 3490481 | ||
+ | Parent: I got a child with PID 3490482 | ||
+ | Child.: My parent is PPID 3490481 and my PID is 3490482 | ||
+ | </source> | ||
+ | |||
+ | ==Example of creating a daemon in Linux== | ||
+ | {{:Daemon linux}} | ||
+ | |||
+ | =Example of creating several processes= | ||
+ | See same example in [[Bash processes#example|bash]] | ||
+ | <source lang=c line> | ||
+ | #include <stdio.h> | ||
+ | #include <sys/types.h> | ||
+ | #include <unistd.h> | ||
+ | #include <wait.h> | ||
+ | |||
+ | void childprocess(char *name, int count_to) { | ||
+ | int counter = 0; | ||
+ | |||
+ | printf("My name is %s and my PID is %d\n", name, getpid()); | ||
+ | |||
+ | while (counter < count_to) { | ||
+ | sleep(1); | ||
+ | counter = counter +1; | ||
+ | printf("%s counted to %i\n", name, counter); | ||
+ | } | ||
+ | printf("%s signing off\n", name); | ||
+ | } | ||
+ | |||
+ | |||
+ | int main(void) { | ||
+ | int wstatus; // Used by c wait function | ||
+ | |||
+ | printf("I am the main function and my PID is %i\n", getpid()); | ||
+ | |||
+ | if (fork() == 0) { // fork returns 0 for child | ||
+ | childprocess("Hans", 5); | ||
+ | return(0); // Child process finished | ||
+ | } | ||
+ | |||
+ | if (fork() == 0) { | ||
+ | childprocess("Ulla", 3); | ||
+ | return(0); | ||
+ | } | ||
+ | |||
+ | while(wait(&wstatus) > 0); | ||
+ | printf("Main signing off\n"); | ||
+ | return(0); | ||
+ | } | ||
+ | |||
+ | </source> | ||
+ | =Parent and child with different code= | ||
+ | <onlyinclude> | ||
+ | If we want a parent and a child to execute different code and share a resource, for example a server that send information to a client. | ||
+ | |||
+ | Three files: | ||
+ | # '''my_system.c''' - forks and starts the server code as parent and the client code as child. | ||
+ | # '''server.c"' - contains the server code | ||
+ | # '''client.c''' - contains the client code | ||
+ | ==Typical output when starting my_system== | ||
+ | '''Note:''' | ||
+ | * Server executable running with the parent PID | ||
+ | * Client executable running with the child PID | ||
+ | <source lang=text> | ||
+ | Starting server as parent - my PID is 2644553 | ||
+ | Starting client as child - my PID is 2644554 | ||
+ | Server started my PID is 2644553 | ||
+ | Client started my PID is 2644554 | ||
+ | Client received: hello: 1 | ||
+ | Client received: hello: 2 | ||
+ | Client received: hello: 3 | ||
+ | Client received: hello: 4 | ||
+ | Client received: hello: 5 | ||
+ | Server signing out | ||
+ | Client signing out | ||
+ | </source> | ||
+ | |||
+ | ==my_system.c== | ||
+ | <source lang=c line> | ||
+ | #include <stdio.h> | ||
+ | #include <unistd.h> | ||
+ | #include <sys/types.h> | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | int arr[2]; | ||
+ | char argv[50]; | ||
+ | pipe(arr); | ||
+ | if(fork()) | ||
+ | { | ||
+ | printf("Starting server as parent - my PID is %d\n", getpid()); | ||
+ | fflush(stdout); | ||
+ | close(arr[0]); | ||
+ | sprintf(argv,"%d",arr[1]); | ||
+ | execlp("./server","server",argv,NULL); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | printf("Starting client as child - my PID is %d\n", getpid()); | ||
+ | fflush(stdout); | ||
+ | close(arr[1]); | ||
+ | sprintf(argv,"%d",arr[0]); | ||
+ | execlp("./client","client",argv,NULL); | ||
+ | } | ||
+ | return 1; | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | ==server.c== | ||
+ | <source lang=c line> | ||
+ | #include <stdio.h> | ||
+ | #include <sys/types.h> | ||
+ | #include <unistd.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <string.h> | ||
+ | |||
+ | int main(int argc,char *argv[]) | ||
+ | { | ||
+ | char buf[40]="hello"; | ||
+ | int fd,i; | ||
+ | |||
+ | fd = atoi(argv[1]); | ||
+ | printf("Server started my PID is %d\n", getpid()); | ||
+ | for (i=1; i <= 5; i++) { | ||
+ | sprintf(buf,"hello: %d",i); | ||
+ | write(fd,buf,strlen(buf)+1); | ||
+ | sleep(1); | ||
+ | } | ||
+ | buf[0] == 0; | ||
+ | close(fd); | ||
+ | puts("Server signing out"); | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </source> | ||
+ | ==client.c== | ||
+ | <source lang=c line> | ||
+ | #include <stdio.h> | ||
+ | #include <unistd.h> | ||
+ | #include <sys/types.h> | ||
+ | #include <stdlib.h> | ||
+ | |||
+ | int main(int argc,char *argv[]) | ||
+ | { | ||
+ | char buf[11]; | ||
+ | int fd; | ||
+ | |||
+ | printf("Client started my PID is %d\n", getpid()); | ||
+ | fd = atoi(argv[1]); | ||
+ | while(read(fd,buf,10) > 0) | ||
+ | { | ||
+ | printf("Client received: %s\n",buf); | ||
+ | } | ||
+ | close(fd); | ||
+ | printf("Client signing out\n"); | ||
+ | return 0; | ||
+ | } | ||
+ | |||
</source> | </source> | ||
+ | </onlyinclude> | ||
=Links= | =Links= | ||
*[https://devarea.com/linux-fork-system-call-and-its-pitfalls/#.Y5v3THbMJPY good article from devarea.com] | *[https://devarea.com/linux-fork-system-call-and-its-pitfalls/#.Y5v3THbMJPY good article from devarea.com] | ||
− | [[Category:Linux]] | + | [[Category:Linux]][[Category:C]] |
Latest revision as of 06:36, 19 December 2022
fork() is one of several functions to "spawn" a process
Use of fork
Magic or Qbit?
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <unistd.h>
4
5 int main(void) {
6 if (fork()) {
7 printf("True\n");
8 } else {
9 printf("False\n");
10 }
11 return(0);
12 }
When running the above code:
heth@emb3:~/bin/$ gcc process0.c -o process0
heth@emb3:~/bin/$ ./process0
True
False
What is fork
fork() creates a new process by duplicating the calling process. The new process is referred to as the child process. The calling process is referred to as the parent process.
- PID - Process ID - the ID of a running process
- PPID - Parent Process ID - default the ID of the process that created this process or 1 if the parent died.
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <unistd.h>
4
5 int main(void) {
6 int pid;
7
8 printf("Parent: PPID is %d and PID is %d\n", getppid(), getpid() );
9
10 if ((pid=fork()) > 0) {
11 printf("Parent: PPID is %d and PID is %d\n", getppid(), getpid() );
12 printf("Parent: I got a child with PID %d\n", pid);
13 } else {
14 printf("Child.: My parent is PPID %d and my PID is %d\n", getppid(), getpid() );
15 }
16 return(0);
17 }
When running this code - different results can occur:
heth@emb3:~/bin/$ gcc process1.c -o process1
heth@emb3:~/bin/$ ./process1
Parent: PPID is 3480753 and PID is 3490474
Parent: PPID is 3480753 and PID is 3490474
Child.: My parent is PPID 3490474 and my PID is 3490475
Parent: I got a child with PID 3490475
heth@emb3:~/bin$ ./process1
Parent: PPID is 3480753 and PID is 3490481
Parent: PPID is 3480753 and PID is 3490481
Parent: I got a child with PID 3490482
Child.: My parent is PPID 3490481 and my PID is 3490482
Example of creating a daemon in Linux
Example of creating a daemon in Linux: (See systemd debian for systemd daemons)
- setsid() - damon will be the session leader. Sets Session ID (SID) and Group ID (GID) to Process ID (PID)
- Reopening stdin, stdout and stderr to /dev/null detaches the process from the controlling terminal and Parent PID is set to 1 (init or systemd - depending on Linux version/distribution)
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <stdlib.h>
4 #include <syslog.h>
5 void daemon_create(void);
6
7 int main(void) {
8 printf("Starting my_daemon\n");
9 openlog("my_daemon", LOG_INFO, LOG_DAEMON);
10 daemon_create();
11 for (int i=0; i < 100; i++) {
12 syslog(LOG_INFO,"Countet to %d", i);
13 sleep(2);
14 }
15 return(0);
16 }
17
18 void daemon_create(void) {
19 pid_t pid;
20
21 pid = fork();
22 if(pid < 0){
23 syslog(LOG_ERR, "Error in fork: %m");
24 exit(1);
25 }
26 if(pid > 0) {
27 syslog(LOG_INFO, "Parent dying");
28 exit(0); // Parent die
29 }
30 if(setsid() < 0){ // Make child session leader
31 syslog(LOG_ERR, "Error in setsid: %m");
32 exit(2);
33 }
34 if (freopen("/dev/null", "r", stdin) == NULL
35 || freopen("/dev/null", "w", stdout) == NULL
36 || freopen("/dev/null", "w", stderr) == NULL ) {
37 syslog(LOG_ERR, "When creating daemon freopen from STDIN/OUT/ERR failed: %m");
38 exit(3);
39 }
40 }
Checking the daemon
- Daemon detached from TTY - PPID is 1
- Session and session group leader - SID and PGID same as PID
$ ps xo uname,pid,ppid,pgid,sid,tty,comm | grep -P '(my_daemon|COMMAND)'
USER PID PPID PGID SID TTY COMMAND
heth 778254 1 778254 778254 ? my_daemon
$ tail -f /var/log/sysg
Dec 20 12:37:59 emb3 my_daemon: Countet to 2
Dec 20 12:38:01 emb3 my_daemon: Countet to 3
Dec 20 12:38:03 emb3 my_daemon: Countet to 4
Dec 20 12:38:05 emb3 my_daemon: Countet to 5
...
Example of creating several processes
See same example in bash
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <unistd.h>
4 #include <wait.h>
5
6 void childprocess(char *name, int count_to) {
7 int counter = 0;
8
9 printf("My name is %s and my PID is %d\n", name, getpid());
10
11 while (counter < count_to) {
12 sleep(1);
13 counter = counter +1;
14 printf("%s counted to %i\n", name, counter);
15 }
16 printf("%s signing off\n", name);
17 }
18
19
20 int main(void) {
21 int wstatus; // Used by c wait function
22
23 printf("I am the main function and my PID is %i\n", getpid());
24
25 if (fork() == 0) { // fork returns 0 for child
26 childprocess("Hans", 5);
27 return(0); // Child process finished
28 }
29
30 if (fork() == 0) {
31 childprocess("Ulla", 3);
32 return(0);
33 }
34
35 while(wait(&wstatus) > 0);
36 printf("Main signing off\n");
37 return(0);
38 }
Parent and child with different code
If we want a parent and a child to execute different code and share a resource, for example a server that send information to a client.
Three files:
- my_system.c - forks and starts the server code as parent and the client code as child.
- server.c"' - contains the server code
- client.c - contains the client code
Typical output when starting my_system
Note:
- Server executable running with the parent PID
- Client executable running with the child PID
Starting server as parent - my PID is 2644553
Starting client as child - my PID is 2644554
Server started my PID is 2644553
Client started my PID is 2644554
Client received: hello: 1
Client received: hello: 2
Client received: hello: 3
Client received: hello: 4
Client received: hello: 5
Server signing out
Client signing out
my_system.c
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <sys/types.h>
4
5 int main()
6 {
7 int arr[2];
8 char argv[50];
9 pipe(arr);
10 if(fork())
11 {
12 printf("Starting server as parent - my PID is %d\n", getpid());
13 fflush(stdout);
14 close(arr[0]);
15 sprintf(argv,"%d",arr[1]);
16 execlp("./server","server",argv,NULL);
17 }
18 else
19 {
20 printf("Starting client as child - my PID is %d\n", getpid());
21 fflush(stdout);
22 close(arr[1]);
23 sprintf(argv,"%d",arr[0]);
24 execlp("./client","client",argv,NULL);
25 }
26 return 1;
27 }
server.c
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <unistd.h>
4 #include <stdlib.h>
5 #include <string.h>
6
7 int main(int argc,char *argv[])
8 {
9 char buf[40]="hello";
10 int fd,i;
11
12 fd = atoi(argv[1]);
13 printf("Server started my PID is %d\n", getpid());
14 for (i=1; i <= 5; i++) {
15 sprintf(buf,"hello: %d",i);
16 write(fd,buf,strlen(buf)+1);
17 sleep(1);
18 }
19 buf[0] == 0;
20 close(fd);
21 puts("Server signing out");
22
23 return 0;
24 }
client.c
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <sys/types.h>
4 #include <stdlib.h>
5
6 int main(int argc,char *argv[])
7 {
8 char buf[11];
9 int fd;
10
11 printf("Client started my PID is %d\n", getpid());
12 fd = atoi(argv[1]);
13 while(read(fd,buf,10) > 0)
14 {
15 printf("Client received: %s\n",buf);
16 }
17 close(fd);
18 printf("Client signing out\n");
19 return 0;
20 }