Linux System Programming

Linux System 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 :

code.zip

just write ‘make’ in a terminal to compile all of them.


Hello World

hello_world.svg

#include "main.h"

//======================================================================
int main(int argc, char **argv)
{
	printf("hello world \n");

return 0;
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^ EOF ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

$ ./prog01_hello_world  
hello world  

Repeat

repeat.svg

#include "main.h"

#define STRING_SIZE 10

//======================================================================
int main(int argc, char **argv)
{
	char car;
	char buf[STRING_SIZE];
	
	while(1)
	{	
		if (fgets(buf, STRING_SIZE, stdin) == NULL)		// Check CTRL+D ( EOF )
									{ 	printf("exit \n");	break; }
				
		printf("you wrote : %s\n" , buf);	// Wite on STD_OUT		
	}
return 0;
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^ EOF ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

$ ./prog02_repeat
abcd
you wrote : abcd

1234
you wrote : 1234

exit   

Calculate Average

average.svg

#include "main.h"

//======================================================================
int main(int argc, char **argv)
{
	int sum=0;
	int count=0;
	int value;
	
	for(int i=0;i<argc;i++)
	  {
		  printf("argv[%d]=%s\n",i,argv[i]);
		  if(sscanf(argv[i],"%d",&value)==1)
			{
				sum+=value;
				++count;
			}
	  }

	printf("avg=%f\n",(float)sum/(float)count);	// Display result on STD_OUT

return 0;
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^ EOF ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

$ ./prog03_average 3 4 5
argv[0]=./prog03_average
argv[1]=3
argv[2]=4
argv[3]=5
avg=4.000000

Using Files ( Low Level)

file_low_level.svg

#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

redirect.svg

#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 ).

process_fork.svg

#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.

process_pipe.svg

#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.

process_socket.svg

#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

process_redirect.svg

#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.

#include "main.h"
//======================================================================
void *task1(void *data)
{
	char *msg=(char *)data;
	printf("thread reading msg: %s\n",msg);
	strcpy(msg,"thread writing to msg");
	return (void *)0;
}
//======================================================================
//							MAIN
//======================================================================
int main(int argc, char **argv)
{
  int ok;	
  char msg[0x100];
  
  strcpy(msg,"main thread writing to msg");
  
  pthread_t th;
  ok = pthread_create(&th,(const pthread_attr_t *)0,&task1,msg);
						if( ok == -1 ) { perror("thread create"); exit(1); }
						
  ok = pthread_join(th,(void **)0);
						if( ok == -1 ) { perror("thread join"); exit(1); }
  
  printf("main thread reading msg: %s\n",msg);
			
return EXIT_SUCCESS;
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^ EOF ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

$ ./prog12_threads
thread reading msg: main thread writing to msg
main thread reading msg: thread writing to msg				

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