Linux Server Programming
A few illustrated reminds about linux system programming.
For complete explanations, please have a look at : https://web.enib.fr/~harrouet
Every following examples can be downloaded here :
just write ‘make’ in a terminal to compile all of them.
Hello World
|
|
Repeat
|
|
Calculate Average
|
|
Using Files ( Low Level)
#include "main.h"
//======================================================================
int main(int argc, char **argv)
{
char buf[12] = "hello world\n";
if ( argc != 2 ) { printf("usage : ./prog04_file_low_level output_file \n"); return 1; }
int fd=open(argv[1],O_RDWR);
if ( fd < 0 ) { perror("open error"); return 1; }
int ok = write(fd, buf, strlen(buf));
if ( ok == -1 ) { perror("write error"); return 1; }
close(fd);
return 0;
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^ EOF ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
$ touch file_to_write # create file_to_write
$ ./prog04_file_low_level file_to_write # write inside
$ nano file_to_write # let's have a look
hello world
Using Files ( High Level)
#include "main.h"
//======================================================================
int main(int argc, char **argv)
{
char buf[13] = "hello world\n";
int res;
if ( argc != 2 ) { printf("usage : ./prog05_file_high_level output_file \n"); return 1; }
FILE *fd = NULL;
fd = fopen( argv[1], "wr");
if( fd == NULL ) { perror("open error"); return 1; }
int ok = fprintf(fd, buf);
if ( ok == -1 ) { perror("write error"); return 1; }
res = 5 + 8;
ok = fprintf(fd, "sum = %d\n", res);
if ( ok == -1 ) { perror("write error"); return 1; }
ok = fprintf(stdout, "I write on the standart output \n"); // Same as printf
if ( ok == -1 ) { perror("write error"); return 1; }
ok = fclose(fd);
if ( ok == -1 ) { perror("close error"); return 1; }
return 0;
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^ EOF ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
$ touch file_to_write # create file_to_write
$ ./prog05_file_high_level file_to_write
I write on the standart output
$ nano file_to_write # let's have a look
hello world
sum = 13
Exec Function
#include "main.h"
// exec v[arguments = tab] p [file to execute searched in PATH ]
//======================================================================
int main(int argc, char **argv)
{
if ( argc != 2 ) { printf("usage : ./prog06_exec file_to_unzip \n"); return 1; }
char *args[]={ "unzip", argv[1], NULL }; // {arg[0], arg[1], arg[2]} REM : NULL mandatory
int ok = execvp("unzip",args);
if ( ok == -1 ) { perror("execvp error"); return 1; }
return 0;
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^ EOF ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
$ touch file_to_zip
$ zip -r file_zipped.zip file_to_zip
adding: file_to_zip (stored 0%)
$ ./prog06_exec file_zipped.zip
Archive: file_zipped.zip
extracting: file_to_zip
Redirections
$ sort
pig # Write on std_in
cow
chicken # --> CTRL+D
chicken # display result on std_out
cow
pig
$ sort > results # redirect display in file 'result'
pig # Write on std_in
cow
chicken # --> CTRL+D
$ nano results
chicken
cow
pig
$ nano animals
pig
cow
chicken
$ sort < animals > results # read from file, write in file
#include "main.h"
//======================================================================
int main(int argc, char **argv)
{
int ok;
char *sort_args[] = {"sort", NULL};
// open input and output files
int fdIn = open("animals", O_RDONLY);
if(fdIn < 0) { perror("open"); return 1; }
int fdOut = open("results", O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IRGRP | S_IWGRP | S_IWUSR);
if(fdOut < 0) { perror("open"); return 1; }
// replace standard input with input file
ok = dup2(fdIn,0); // 0 = stdin
if(ok == -1) { perror("dup2"); return 1; }
// replace standard output with output file
ok = dup2(fdOut,1); // 1 =stdout
if(ok == -1) { perror("dup2"); return 1; }
// close unused file descriptors
ok = close(fdIn);
if(ok == -1) { perror("close"); return 1; }
ok = close(fdOut);
if(ok == -1) { perror("close"); return 1; }
// execute sort
ok = execvp("sort", sort_args); // Default case : execvp read from stdin, write on stdout
if(ok == -1) { perror("execvp"); return 1; }
return 0;
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^ EOF ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
$ ./prog07_dup
$ nano results
pig
cow
chicken
Process and Fork
A process is a running program.
If you want to launch another process ( child ) in a running process ( parent ),
you have to use the FORK() function.
Both of them share the same code, but the memory allowed is different.
You can distinguish them with their PID ( Process ID ).
#include "main.h"
//======================================================================
/*
$ ps aux # a:all users, u:detailled, x:daemons // look at bash PID
*/
//======================================================================
int main(int argc, char **argv)
{
int ok;
int value=100;
printf("before fork, pid=%d, ppid=%d, value=%d (%p)\n",
getpid(),getppid(),value,(void *)&value);
pid_t child=fork(); //>>>>>>>> CREATE CHILD >>>>>>>>>>>>>>>>>>>>>>>>
switch(child)
{
case -1: perror("fork"); return EXIT_FAILURE; break;
//############ CHILD CODE ######################################
case 0 :
value+=10;
printf("after fork in the child, child=%d, pid=%d, ppid=%d, value=%d (%p)\n",
child,getpid(),getppid(),value,(void *)&value);
sleep(10);
break;
//############ PARENT CODE #####################################
default :
value+=1000;
printf("after fork in the parent, child=%d, pid=%d, ppid=%d, value=%d (%p)\n",
child,getpid(),getppid(),value,(void *)&value);
int status;
pid_t p=waitpid(child,&status,0); // PARENT MUST WAIT FOR HIS CHILD
if(p==-1) { perror("waitpid"); return EXIT_FAILURE; }
if (WIFEXITED(status))
{
printf(" Child ended normaly .\n"
" Return code : %d.\n", WEXITSTATUS(status));
}
if (WIFSIGNALED(status))
{
printf(" Child didn't end normaly.\n"
" killed by signal : %d.\n", WTERMSIG(status));
}
break;
}
return EXIT_SUCCESS;
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^ EOF ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
$ ps -aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
kerhoas 11641 0.0 0.0 30156 5116 pts/2 Ss 14:25 0:00 bash
$ ./prog08_fork
before fork, pid=14481, ppid=11641, value=100 (0x7ffd31c53b60)
after fork in the parent, child=14482, pid=14481, ppid=11641, value=1100 (0x7ffd31c53b60)
after fork in the child, child=0, pid=14482, ppid=14481, value=110 (0x7ffd31c53b60)
Child ended normaly .
Return code : 0.
Process Dialog : Pipes
Since processes don’t share the same memory, you can use pipes to exchange datas.
#include "main.h"
//======================================================================
/*
$ ps aux # a:all users, u:detailled, x:daemons // look at bash PID
Unidirectional pipe : you have to choose the direction
PARENT CHILD
1 >> PtC >> 0
X 0 << PtC << 1 X
0 << CtP << 1
X 1 >> CtP >> 0 X
*/
//======================================================================
int main(int argc, char **argv)
{
int ok;
int value=100;
char *msg;
char buffer[0x100];
bool usePause=false;
// Parse Args
for(int i=1;i<argc;++i)
{
if(!strcmp(argv[i],"pause")) usePause=true;
}
int childToParent[2]; // Declare pipe, then create
ok = pipe(childToParent);
if( ok == -1 ) { perror("pipe"); exit(1); }
int parentToChild[2];
ok = pipe(parentToChild);
if( ok == -1 ) { perror("pipe"); exit(1); }
pid_t child=fork(); //>>>>>>>> CREATE CHILD >>>>>>>>>>>>>>>>>>>>>>>>
switch(child)
{
case -1: perror("fork"); return EXIT_FAILURE; break;
//############ CHILD CODE ######################################
case 0 :
close(childToParent[0]); // child will not read from this pipe
close(parentToChild[1]); // child will not write to this pipe
msg="first message from child to parent\n";
write(childToParent[1],msg,strlen(msg));
if(usePause) { sleep(1); }
msg="second message from child to parent\n";
write(childToParent[1],msg,strlen(msg));
close(childToParent[1]); // done with writing
for(;;)
{
int r=read(parentToChild[0],buffer,0xFF);
if(r==0) break; // end of file
if(r==-1) { perror("read"); exit(1); }
buffer[r]='\0';
printf("child received [%s]\n",buffer);
}
close(parentToChild[0]); // done with reading
break;
//############ PARENT CODE #####################################
default :
close(parentToChild[0]); // parent will not read from this pipe
close(childToParent[1]); // parent will not write to this pipe
for(;;)
{
int r=read(childToParent[0],buffer,0xFF);
if(r==0) break; // end of file
if(r==-1) { perror("read"); exit(1); }
buffer[r]='\0';
printf("parent received [%s]\n",buffer);
}
close(childToParent[0]); // done with reading
msg="first message from parent to child\n";
write(parentToChild[1],msg,strlen(msg));
if(usePause) { sleep(1); }
msg="second message from parent to child\n";
write(parentToChild[1],msg,strlen(msg));
close(parentToChild[1]); // done with writing
int status;
pid_t p=waitpid(child,&status,0); // PARENT MUST WAIT FOR HIS CHILD
if(p==-1) { perror("waitpid"); return EXIT_FAILURE; }
break;
}
return EXIT_SUCCESS;
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^ EOF ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
$ ./prog09_pipe
parent received [first message from child to parent
second message from child to parent
]
child received [first message from parent to child
second message from parent to child
]
Process Dialog : Socket
Sockets can be used as pipes as well.
#include "main.h"
//======================================================================
/*
$ ps aux # a:all users, u:detailled, x:daemons // look at bash PID
Socket = bidirectionnal ; choose an extremity
PARENT CHILD
[1] >> SOCKFD >> [0]
<< SOCKFD <<
*/
//======================================================================
int main(int argc, char **argv)
{
int ok;
int value=100;
char *msg;
char buffer[0x100];
int sockFd[2];
bool usePause=false;
bool useSock=false;
// Parse Args
for(int i=1;i<argc;++i)
{
if(!strcmp(argv[i],"pause")) usePause=true;
}
ok = socketpair(PF_LOCAL,SOCK_STREAM,0,sockFd);
if( ok == -1 ) { perror("pipe"); exit(1); }
pid_t child=fork(); //>>>>>>>> CREATE CHILD >>>>>>>>>>>>>>>>>>>>>>>>
switch(child)
{
case -1: perror("fork"); return EXIT_FAILURE; break;
//############ CHILD CODE ######################################
case 0 :
close(sockFd[1]); // child will not use this end
msg="first message from child to parent\n";
write(sockFd[0],msg,strlen(msg));
if(usePause) { sleep(1); }
msg="second message from child to parent\n";
write(sockFd[0],msg,strlen(msg));
shutdown(sockFd[0],SHUT_WR); // done with writing
for(;;)
{
int r=read(sockFd[0],buffer,0xFF);
if(r==0) break; // end of file
if(r==-1) { perror("read"); exit(1); }
buffer[r]='\0';
printf("child received [%s]\n",buffer);
}
close(sockFd[0]); // done with reading
break;
//############ PARENT CODE #####################################
default :
close(sockFd[0]); // parent will not use this end
for(;;)
{
int r=read(sockFd[1],buffer,0xFF);
if(r==0) break; // end of file
if(r==-1) { perror("read"); exit(1); }
buffer[r]='\0';
printf("parent received [%s]\n",buffer);
}
const char *msg="first message from parent to child\n";
write(sockFd[1],msg,strlen(msg));
if(usePause) { sleep(1); }
msg="second message from parent to child\n";
write(sockFd[1],msg,strlen(msg));
close(sockFd[1]); // done with writing
int status;
pid_t p=waitpid(child,&status,0); // PARENT MUST WAIT FOR HIS CHILD
if(p==-1) { perror("waitpid"); return EXIT_FAILURE; }
break;
}
return EXIT_SUCCESS;
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^ EOF ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
$ ./prog10_sock
parent received [first message from child to parent
second message from child to parent
]
child received [first message from parent to child
second message from parent to child
]
Process Dialog : Tubes and Redirection
#include "main.h"
//======================================================================
/*
$ ps aux # a:all users, u:detailled, x:daemons // look at bash PID
Unidirectional pipe : you have to choose the direction
PARENT CHILD
1 >> pipe >> 0 X
printf 0 << pipe << 1 <----- stdout << gunzip << stdin <-- inputFd
*/
//======================================================================
int main(int argc, char **argv)
{
int ok;
int value=100;
char *msg;
char buffer[0x100];
int inputFd;
bool usePause=false;
if ( argc != 2 ) { printf("usage : ./prog11_proc_redir file_to_unzip \n"); return 0; }
int pipeFd[2]; // Declare pipe, then create
ok = pipe(pipeFd);
if( ok == -1 ) { perror("pipe"); exit(1); }
pid_t child=fork(); //>>>>>>>> CREATE CHILD >>>>>>>>>>>>>>>>>>>>>>>>
switch(child)
{
case -1: perror("fork"); return EXIT_FAILURE; break;
//############ CHILD CODE ######################################
case 0 :
inputFd=open(argv[1],O_RDONLY);
if(inputFd==-1) { perror("open"); exit(1); }
ok = dup2(inputFd,0);
if (ok==-1) { perror("dup2"); exit(1); }
close(inputFd); // redirected so useless now
close(pipeFd[0]); // child1 will not read from this pipe
ok = dup2(pipeFd[1],1);
if (ok==-1) { perror("dup2"); exit(1); }
close(pipeFd[1]); // redirected so useless now
char *args[]={ "sort", (char *)0 };
ok = execvp("sort",args);
if (ok==-1) { perror("execvp"); exit(1); }
break;
//############ PARENT CODE #####################################
default :
for(;;)
{
int r=read(pipeFd[0],buffer,0xFF);
if(r==0) break; // end of file
if(r==-1) { perror("read"); exit(1); }
buffer[r]='\0';
printf("parent received [%s]\n",buffer);
}
close(pipeFd[0]); // done with reading
int status;
pid_t p=waitpid(child,&status,0); // PARENT MUST WAIT FOR HIS CHILD
if(p==-1) { perror("waitpid"); return EXIT_FAILURE; }
break;
}
return EXIT_SUCCESS;
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^ EOF ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
./prog11_proc_redir animals
parent received [
chicken
cow
pig
]
Threads
Threads can be seen as ’light’ processes. Threads created in a process share the same memory.
|
|
Calc with threads
#include "main.h"
//======================================================================
inline static double getTime(void) // time in seconds
{
struct timeval tv;
gettimeofday(&tv,(struct timezone *)0);
return tv.tv_sec+1e-6*tv.tv_usec;
}
//======================================================================
typedef struct _ThreadInfo
{
pthread_t th;
int size,nbThreads;
double *A,*B,*C;
int index;
} ThreadInfo;
void *task2(void *data)
{
ThreadInfo *info=(ThreadInfo *)data;
for(int i=info->index;i<info->size*info->size;i+=info->nbThreads)
{
int row=i/info->size;
int column=i-(row*info->size);
double sum=0.0;
for(int k=0;k<info->size;++k)
{
sum+=info->A[row*info->size+k]*info->B[k*info->size+column];
}
info->C[i]=sum;
}
return (void *)0;
}
//======================================================================
// MAIN
//======================================================================
int main(int argc, char **argv)
{
int ok;
int i,size,nbThreads;
if((argc!=3)||(sscanf(argv[1],"%d",&size)!=1)||(sscanf(argv[2],"%d",&nbThreads)!=1))
{ fprintf(stderr,"usage: %s size nb_threads\n",argv[0]); exit(1); }
double *A=(double *)malloc(size*size*sizeof(double));
double *B=(double *)malloc(size*size*sizeof(double));
double *C=(double *)malloc(size*size*sizeof(double));
srand(time((time_t *)0));
for(i=0;i<size*size;++i)
{
A[i]=rand()/(double)RAND_MAX;
B[i]=rand()/(double)RAND_MAX;
C[i]=0.0;
}
ThreadInfo *threads=(ThreadInfo *)malloc(nbThreads*sizeof(ThreadInfo));
double t0=getTime();
for(i=0;i<nbThreads;++i)
{
threads[i].size=size;
threads[i].nbThreads=nbThreads;
threads[i].A=A;
threads[i].B=B;
threads[i].C=C;
threads[i].index=i;
ok = pthread_create(&threads[i].th,(const pthread_attr_t *)0, &task2,threads+i);
if( ok == -1 ) { perror("thread create"); exit(1); }
}
for(i=0;i<nbThreads;++i)
{
ok = pthread_join(threads[i].th,(void **)0);
if( ok == -1 ) { perror("thread create"); exit(1); }
}
double t1=getTime();
free(threads);
free(A);
free(B);
#if 0
for(i=0;i<size;++i)
{
for(int j=0;j<size;++j)
{
printf("%g ",C[i*size+j]);
}
printf("\n");
}
#endif
free(C);
printf("duration: %g s\n",t1-t0);
return EXIT_SUCCESS;
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^ EOF ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
$ ./prog13_threads_calc 200 4
duration: 0.00437188 s