Notes: Operative Systems – Part 4

< Previous (Operative Systems – Part 3) | (Operative Systems – Part 5) Next >

NOTIFICATION: These notes are published for educational purposes. Using these notes is under your own responsibility and risk. These notes are given ‘as is’. I do not take responsibilities for how you use them.

PDF Content:

  • Relocation and protection (continued)
  • Swapping
  • Managing free memory
  • Virtual memory
  • Memory Management Unit (MMU)
  • Paging
  • Page table
  • Transaction Lookup Buffers (TLB)
  • Transaction Look-a-side Buffers (TLB)

Operative_Systems_4

 

< Previous (Operative Systems – Part 3) | (Operative Systems – Part 5) Next >

Share

Notes: Operative Systems – Part 2

< Previous (Operative Systems – Part 1) | (Operative Systems – Part 3) Next >

NOTIFICATION: These notes are published for educational purposes. Using these notes is under your own responsibility and risk. These notes are given ‘as is’. I do not take responsibilities for how you use them.

PDF Content:

  • Dispatcher
  • Scheduling criteria
  • Optimization criteria
  • Scheduling algorithm goals
  • First-come, first-served scheduling (FCFS)
  • Shortest-job-first Scheduling (SJR)
  • Shortest Remaining Time Next (SRTF)
  • Pre-empty Shortest-job-first (PSJR)
  • CPU burst
  • Priority scheduling
  • Round Robin (RR)
  • Time quantum
  • Context switch time
  • Multilevel queue
  • Multilevel feedback queue (MFQ)
  • Real-time scheduling
  • Flow of control
  • Fair scheduling
  • Work-conserving
  • Non-work-conserving
  • Organization of Linux kernel
  • Privilege modes
  • System calls

Operative_Systems_2

 

< Previous (Operative Systems – Part 1) | (Operative Systems – Part 3) Next >

Share

Example of using Fork and pipe in Linux

The following program will create a chain of parents/child in which the last child will receive a message form the top parent through a pipe.

NOTIFICATION: These examples are provided for educational purposes. Using this code is under your own responsibility and risk. The code is given ‘as is’. I do not take responsibilities of how they are used.

The first example is without using recursion while the second example is the same thing but using example. In this way, you can see how fork and pipe behaves.

for_pipe_without_using_recursion.c:

/**
 * @author: Alejandro G. Carlstein R. M.
 * @description: This program will take an integer argument.
 *               It will create a chain of parent-child equal to the value
 *               provided by the integer argument, Example:
 *				 Top parent - forks ==> 
 * 				 child1 - forks ==> 
 *				 child1's child - forks ... ==> Nth generation child, where
 *				 N is the integer argument.
 *			     Then the top parent will then pass a message to 
 *               the child at the lowest level (Nth generation child) 
 *               through a pipe. That child will print 
 *               the received message to output stdout.
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#define MAX_N 3
#define IDX_ARG_N 1
#define MAX_BUFF 30

int read_pipe(int *pfds, char msg[], int max_buff, int i_tab);

int write_pipe(int *pfds, const char *msg, int max_buff, int i_tab);

int main(int argc, char *argv[])
{

	pid_t rtn_pid, pid[3];

	int rtn_error = 0;

	int i, j, status;

	int pfds[2];

	int i_N = (argc > 2) ? atoi(argv[IDX_ARG_N]): 0;

	char buf[MAX_BUFF];

	printf('\n');

	/* create a pipe */
    if (pipe(pfds) == -1) {
            perror('pipe');
            exit(1);
    }else{

		/* fork first child */
    	if ( (pid[0] = fork()) < 0) {
			perror('fork pid[0]');
			exit(1);
    	}// end if

    	/* Parent of first fork */
    	if (pid[0] > 0){

    		printf('Parent of first fork sending message: Hello! \n');    	

    		rtn_error = write_pipe(pfds, 'Hello! \n', MAX_BUFF, 0);	 

    		//close(pfds[0]);
    		//close(pfds[1]);

    	}//end if

    	/* Child of first fork */
    	if (pid[0] == 0){

    		printf('>>Child of first fork \n');

			/* fork second child */
	    	if ( (pid[1] = fork()) < 0) {
				perror('fork pid[1]');
				exit(1);
	    	}// end if

	    	/* Parent of second fork */
	    	if (pid[1] > 0){

	    		//printf('Parent of second fork...\n');    	

			}//end if

	    	/* Child of second fork */
	    	if (pid[1] == 0){

		    	printf('>>>>Child of second fork \n');

				/* fork third child */
		    	if ( (pid[2] = fork()) < 0) {
					perror('fork pid[2]');
					exit(1);
		    	}// end if

		    	/* Parent of third fork */
		    	if (pid[2] > 0){

		    		//printf('Parent of third fork...\n');    	

				}//end if		    	

		    	/* Child of third fork */
		    	if (pid[2] == 0){

			    	printf('>>>>>Child of third fork \n');

					rtn_error = read_pipe(pfds, buf, MAX_BUFF, 5);

					if (rtn_error == 0){
						printf('>>>>>CHILD OF THIRD FORK READ MESSAGE: %s \n',  buf);
					}//end if

		    	}//end if

	    	}//end if

    	}//end if

		wait();

	}// end if

	return rtn_error;
}

int write_pipe(int *pfds, const char *msg, int max_buff, int i_tab){

		int rtn_error = 0;

		/* close the read end */
		close(pfds[0]);

		//printf ('%*sWRITE_PIPE: %s\n', i_tab, ' ', msg);

		/* write message to parent  through the write end */
        if(write(pfds[1], msg, max_buff) <= 0) {

			printf('%*s[X] ERROR: Cannot write\n', i_tab, ' ');

			rtn_error = 1;

		}//end if

	return rtn_error;
}

int read_pipe(int *pfds, char msg[], int max_buff, int i_tab){

		int rtn_error = 0;

		/* close the write end */
		close(pfds[1]);

		//printf ('%*sREAD_PIPE: ', i_tab, ' ');

		/* read message from child through the read end */
		// If not data in the pipe, the read will block
	    if( read(pfds[0], msg, max_buff) <= 0 ) {

			printf ( '%*s[X] ERROR: Cannot read\n', i_tab, ' ');

			rtn_error = 1;

		}else{

			//printf('%*s%s \n', i_tab, ' ', msg);		

		}//end if

	return rtn_error;
}

fork_pipe_using_recursion.c:

/**
 * @author: Alejandro G. Carlstein R. M.
 * @description: This program will take an integer argument.
 *               It will create a chain of parent-child equal to the value
 *               provided by the integer argument, Example:
 *				 Top parent - forks ==>
 * 				 child1 - forks ==>
 *				 child1's child - forks ... ==> Nth generation child, where
 *				 N is the integer argument.
 *			     Then the top parent will then pass a message to
 *               the child at the lowest level (Nth generation child)
 *               through a pipe. That child will print
 *               the received message to output stdout.
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#define MAX_N 3
#define IDX_ARG_N 1
#define MAX_BUFF 30
#define DBG_LV1 0
#define DBG_LV2 1
#define MSG 'Hello! \n'

int read_pipe(int *pfds, char msg[], int max_buff, int i_tab);

int write_pipe(int *pfds, const char *msg, int max_buff, int i_tab);

int recursive_f(int *pfds, int i_count, int i_max_count);

int exec_order;

int main(int argc, char *argv[])
{
	pid_t rtn_pid, pid[3];

	int rtn_error = 0;

	int i, status;

	int pfds[2];

	int i_N = (argc > 2) ? atoi(argv[IDX_ARG_N]): 0;

	exec_order = 0;

	printf('\n');

	/* create a pipe */
    if (pipe(pfds) == -1) {
            perror('pipe');
            exit(1);
    }else{

    	rtn_error = recursive_f(pfds, MAX_N, MAX_N);

	}// end if

	close(pfds[0]);
	close(pfds[1]);

	for (i = 0; i < MAX_N - 1; i++){
		wait();
	}

	return rtn_error;
}

int recursive_f(int *pfds, int i_count, int i_max_count){

	int rtn_error = 0;

	char buf[MAX_BUFF];

	pid_t pid;

	if (DBG_LV1){

   		printf('[e_o: %d][count: %d/%d] recursive_f (*pfds, %d, %d)\n',
   			  exec_order, i_count, i_max_count, i_count, i_max_count);

	}//end if

	/* fork */
    if ( ( pid = fork() ) < 0) {

		if (DBG_LV2)
			perror('error fork pid');

		rtn_error = 1;

    }else{

    	/* Parent of first fork */
    	if (pid > 0 && i_count == i_max_count){

	   		if (DBG_LV1)
	   			printf('[e_o: %d][count: %d/%d] ', exec_order, i_count, i_max_count);

    		printf('Parent of first fork sending message: %s', MSG);    	

    		rtn_error = write_pipe(pfds, MSG, MAX_BUFF, 0);	 

	   		if (DBG_LV1)
	   			printf('[e_o: %d][count: %d/%d] ', exec_order, i_count, i_max_count);

    		printf('PARENT OF FIRST FORK SENT MESSAGE: %s', MSG);

    	}else{
	    	exec_order++;   	

    	}//end if

		/* Child of last fork */
		if (pid == 0){

			if (i_count < 1){

		   		if (DBG_LV1)
		   			printf('[e_o: %d][count: %d/%d] ', exec_order, i_count, i_max_count);

				printf('Last Child of fork reading...\n');

				rtn_error = read_pipe(pfds, buf, MAX_BUFF, 5);

				if (rtn_error == 0){

			   		if (DBG_LV1)
			   			printf('[e_o: %d][count: %d/%d] ', exec_order, i_count, i_max_count);

					printf('LAST CHILD OF FORK READ MESSAGE: %s', buf);

				}//end if

			}else{

	  			i_count--;

	  			if (DBG_LV1)
		  			printf('[e_o: %d][count: %d/%d] Calling recursive_f (i_count[%d] and i_max_count[%d]) \n',
		  				   exec_order, i_count, i_max_count, i_count, i_max_count);

	  			rtn_error = recursive_f(pfds, i_count, i_max_count);

	  		}//end if

    	}//end if

    }// end if	

	return rtn_error;
}

int write_pipe(int *pfds, const char *msg, int max_buff, int i_tab){

		int rtn_error = 0;

		/* close the read end */
		close(pfds[0]);

		if (DBG_LV1) printf ('%*sWRITE_PIPE: %s\n', i_tab, ' ', msg);

		/* write message to parent  through the write end */
        if(write(pfds[1], msg, max_buff) <= 0) {

			if (DBG_LV2) printf('%*s[X] ERROR: Cannot write\n', i_tab, ' ');

			rtn_error = 1;

		}//end if

	return rtn_error;
}

int read_pipe(int *pfds, char msg[], int max_buff, int i_tab){

		int rtn_error = 0;

		/* close the write end */
		close(pfds[1]);

		if (DBG_LV1) printf ('%*sREAD_PIPE: ', i_tab, ' ');

		/* read message from child through the read end */
		// If not data in the pipe, the read will block
	    if( read(pfds[0], msg, max_buff) <= 0 ) {

			if (DBG_LV2) printf ( '%*s[X] ERROR: Cannot read\n', i_tab, ' ');

			rtn_error = 1;

		}else{

			if (DBG_LV1) printf('%*s%s \n', i_tab, ' ', msg);		

		}//end if

	return rtn_error;
}

If you encounter any problems or errors, please let me know by providing an example of the code, input, output, and an explanation. Thanks..

/**
* @course: CS350 Lab
* @author: Alejandro G. Carlstein R. M.
* @description: This program will take an integer argument.
*               It will create a chain of parent-child equal to the value
*               provided by the integer argument, Example:
*                 Top parent – forks ==>
*                  child1 – forks ==>
*                 child1’s child – forks … ==> Nth generation child, where
*                 N is the integer argument.
*                 Then the top parent will then pass a message to
*               the child at the lowest level (Nth generation child)
*               through a pipe. That child will print
*               the received message to output stdout.
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#define MAX_N 3
#define IDX_ARG_N 1
#define MAX_BUFF 30
#define DBG_LV1 0
#define DBG_LV2 1
#define MSG “Hello! \n”

int read_pipe(int *pfds, char msg[], int max_buff, int i_tab);

int write_pipe(int *pfds, const char *msg, int max_buff, int i_tab);

int recursive_f(int *pfds, int i_count, int i_max_count);

int exec_order;

int main(int argc, char *argv[])
{
pid_t rtn_pid, pid[3];

int rtn_error = 0;

int i, status;

int pfds[2];

int i_N = (argc > 2) ? atoi(argv[IDX_ARG_N]): 0;

exec_order = 0;

printf(“\n”);

/* create a pipe */
if (pipe(pfds) == -1) {
perror(“pipe”);
exit(1);
}else{

rtn_error = recursive_f(pfds, MAX_N, MAX_N);

}// end if

close(pfds[0]);
close(pfds[1]);

for (i = 0; i < MAX_N – 1; i++){
wait();
}

return rtn_error;
}

int recursive_f(int *pfds, int i_count, int i_max_count){

int rtn_error = 0;

char buf[MAX_BUFF];

pid_t pid;

if (DBG_LV1){

printf(“[e_o: %d][count: %d/%d] recursive_f (*pfds, %d, %d)\n”,
exec_order, i_count, i_max_count, i_count, i_max_count);

}//end if

/* fork */
if ( ( pid = fork() ) < 0) {

if (DBG_LV2)
perror(“error fork pid”);

rtn_error = 1;

}else{

/* Parent of first fork */
if (pid > 0 && i_count == i_max_count){

if (DBG_LV1)
printf(“[e_o: %d][count: %d/%d] “, exec_order, i_count, i_max_count);

printf(“Parent of first fork sending message: %s”, MSG);

rtn_error = write_pipe(pfds, MSG, MAX_BUFF, 0);

if (DBG_LV1)
printf(“[e_o: %d][count: %d/%d] “, exec_order, i_count, i_max_count);

printf(“PARENT OF FIRST FORK SENT MESSAGE: %s”, MSG);

}else{
exec_order++;

}//end if

/* Child of last fork */
if (pid == 0){

if (i_count < 1){

if (DBG_LV1)
printf(“[e_o: %d][count: %d/%d] “, exec_order, i_count, i_max_count);

printf(“Last Child of fork reading…\n”);

rtn_error = read_pipe(pfds, buf, MAX_BUFF, 5);

if (rtn_error == 0){

if (DBG_LV1)
printf(“[e_o: %d][count: %d/%d] “, exec_order, i_count, i_max_count);

printf(“LAST CHILD OF FORK READ MESSAGE: %s”, buf);

}//end if

}else{

i_count–;

if (DBG_LV1)
printf(“[e_o: %d][count: %d/%d] Calling recursive_f (i_count[%d] and i_max_count[%d]) \n”,
exec_order, i_count, i_max_count, i_count, i_max_count);

rtn_error = recursive_f(pfds, i_count, i_max_count);

}//end if

}//end if

}// end if

return rtn_error;
}

int write_pipe(int *pfds, const char *msg, int max_buff, int i_tab){

int rtn_error = 0;

/* close the read end */
close(pfds[0]);

if (DBG_LV1) printf (“%*sWRITE_PIPE: %s\n”, i_tab, ” “, msg);

/* write message to parent  through the write end */
if(write(pfds[1], msg, max_buff) <= 0) {

if (DBG_LV2) printf(“%*s[X] ERROR: Cannot write\n”, i_tab, ” “);

rtn_error = 1;

}//end if

return rtn_error;
}

int read_pipe(int *pfds, char msg[], int max_buff, int i_tab){

int rtn_error = 0;

/* close the write end */
close(pfds[1]);

if (DBG_LV1) printf (“%*sREAD_PIPE: “, i_tab, ” “);

/* read message from child through the read end */
// If not data in the pipe, the read will block
if( read(pfds[0], msg, max_buff) <= 0 ) {

if (DBG_LV2) printf ( “%*s[X] ERROR: Cannot read\n”, i_tab, ” “);

rtn_error = 1;

}else{

if (DBG_LV1) printf(“%*s%s \n”, i_tab, ” “, msg);

}//end if

return rtn_error;
}

Share

How to Create a System Call

Note: Please check previous post about How to Build a Custom Kernel.

NOTIFICATION: These examples are provided for educational purposes. Using this code is under your own responsibility and risk. The code is given ‘as is’. I do not take responsibilities of how they are used.

  1. Prepare your system call:
    1. Go to the kernel source code folder: cd /home/your-home-folder/linux
    2. Create a personal folder: mkdir yourcall
    3. Access your folder: cd yourcall
    4. Create your source file: your_sys_call.c
    5. Create a makefile: Makefile
  2. Configure some kernel files
    1. Add your system call at the end of the file syscall_table_32.S
      1. cd /home/your-home-directory/linux/arch/x86/kernel
      2. gedit syscall_table_32.S
        iii. Add at the end of file: .long sys_your_new_system_call
    2. Add your system call at the end of the file unistd_32.h
      1. cd /home/your-home-directory/linux/x86/include/asm
      2. gedit unistd_32.h
      3. At the end of the file, add (Where XXX is the previous number plus 1)
        #define __NR_your_new_system_call XXX
    3. Increase by the number of system calls to the total system calls number: #define __NR_syscalls XXX (Where XXX is the previous number that was plus one.)
    4. Add the declaration of your system call at the end of syscalls.h
      1. cd /home/your-home-directory/linux/include/linux
      2. gedit syscalls.h
      3. asmlinkage long your_new_system_call (parameters you want to pass)
    5. Add the new folder to the kernel compile’s Makefile
      core-y += /kernel ... other folder... /yoursyscall
      
      
  3. Configure your system call
    1. Write your system call, inside yoursyscall.c
      asmlinkage long new_system_call (whatever params you want to pass){
      // whatever you want to do
      }
    2. Change the makefile by adding the following line: obj-y := yoursyscall.o
    3. Compile your kernel (Follow steps at Building a New Custom Kernel
  4. Test your system call
    1. Create a user level program that calls your system call
    2. Create a header file that the user space program can use:
      /* header.h */
      #include < linux/unistd.h >
      #define __NR_new_system_call XXX
      
      /* if you system call returns int and takes no parameter
      * use this macro
      */
      _syscall0(int,new_system_call)
      
      /* Otherwise, depending on the number of parameters
      * being passed use the _syscallN macro, N being the no
      * of params, like
      _syscall1(int, new_system_call, int)
      */
  5. Starting at kernel 2.6.18, the _syscallXX macros were removed from the header files to user space, therefore syscall() functions is required to use:
    1. printf (“System call returned %d \n”, syscall (__NR_new_system_call, params_if_any));
    2. or change the header.h file:
      * header.h */
      #include < linux/unistd.h >
      #include < sys/syscall.h >
      #define __NR_new_system_call XXX
      
      long new_system_call (params_if_any){
         return syscall (__NR_new_system_call, params_if_any);
      }
  6. Test the code:
    /* test client */
    #include 'header.h'
    
    int main (void){
        printf ('System call returned %d \n', new_system_call());
        return 1;
    }

NEW!!!

Before you do update-grub (if using , do the following command line (Where x.x.xx is the kernel version you compiled):

sudo update-initramfs -c -k x.x.xx

After Ubuntu 10.04 (or kernel 2.6.32) this seems to be a needed extra step to make it work.

If you encounter any problems or errors, please let me know by providing an example of the code, input, output, and an explanation. Thanks.

Share

Building a New Custom Kernel

NOTIFICATION: These examples are provided for educational purposes. Using this code is under your own responsibility and risk. The code is given ‘as is’. I do not take responsibilities of how they are used.

1- Preparing your system:
a- Download your new kernel from www.kernel.org
b- Make yourself the administrator: sudo -i
c- Install the following libraries which are going to be use by menuconfig and xconfig when making changes to your kernel
i. if you plan to use ‘make xconfig’: sudo apt-get install qt3-dev-tools libqt3-mt-dev
ii. if you plan to use ‘make menuconfig’: sudo apt-get install libncurses5 libncurses5-dev
d. Make a backup of all your information. You never know how bad things can get.

2- Prepare your new kernel for installation (as root):

a- Move your file to your home folder:
i. cd /home/your-home-folder
ii. Assuming your compress kernel file is in your Desktop:
I. mv ./Desktop/linux-2.6.XX.XX.tar.YY
II. XX are the numbers your kernel version, YY is the extension of the compress file
b- Extract the files of your new kernel:
i. if your compressed file kernel ends in .bz2: tar xvjf linux-2.6.XX.XX.tar.bz2
ii.if your compressed file kernel ends in .gz: tar xvzf linux-2.6.XX.XX.tar.gz
c- Create a symbolic link from one subdirectory to another subdirectory
i. ln -s linux-2.6.XX.XX linux
ii. linux-2.6.XX.XX is the subfolder created when uncompressing the compressed kernel file, linux is the directory symbolic link pointing to linux-2.6.XX.XX subfolder. So, if you need to start again, you only have to uncompress the kernel compressed file.

3- Install your new kernel (as root):
a. make clean
b. if you want to configure everything from scratch without using the old config: make mrproper
c. If you would like to see what is different between your original kernel config and the new one: make oldconfig
d. If you want to update the configuration to to only compile modules that are actually used in your system (feature only since 2.6.32 kernel): make localmodconfig
e. Configurate your config, you can you menuconfig(Text Mode) or xconfig(GUI mode):
i. make menuconfig
ii. make xconfig
f. After making any modification on which modules should be on the kernel (y or n) and which should be load after the kernel is load (m).
i. Save the configuration in .config or .Kconfig
ii. For information about which modules are being use by the system you can:
I. Look at the information about all loaded modules: lsmod
II. Check the kernel ring buffer: dmesg
III. Check the system log: cat /var/log/syslog | more
IV. Check the system messages: cat /var/log/messages | more
g. Compile the kernel (can take some time): make bzImage 2> bzImage.err
i. The 2> bzImage.err will forward any error messages to the file bzImage.err
h. Compile all of the modules which you did not specify in the kernel configuration: make modules
i. Installs the modules in /lib/modules/x.y.z, where x.y.z is the kernel release: make modules_install
j. Install kernel itself: make install
k. Create an image:
i. cd /boot
ii. Generating an initramfs image: mkinitramfs -o initrd.img.2.6.XX 2.6.XX

4- Configurate grub (as root)
a- install gedit or use your preference editor
i. apt-get install gedit
b- Modify the grub configuration file:
i. if you are using Grub
I. cd /boot/grub
II. gedit menu.lst
III. Using # to make a line as a comment, turn to comments lines that have
1. “nomenu” or “hiddenmenu”,
IV. Increase the timeout (for example from 5 to 30, so you will have 30 seconds)
ii. if you are using Grub2
I. cd /etc/default
II. gedit grub
1. Comment this line using #, so menu will show:

GRUB_HIDDEN_TIMEOUT=0

2. Increase number of second: GRUB_TIMEOUT=5 to GRUB_TIMEOUT=30
3. if you want to see the counting of second: GRUB_HIDDEN_TIMEOUT_QUIET=true to GRUB_HIDDEN_TIMEOUT_QUIET=false
c. Use update-grub which will check your folder /boot and make the proper changes: update-grub
d. reboot

If you encounter any problems or errors, please let me know by providing an example of the code, input, output, and an explanation. Thanks.

Share