Imagine we don't have OS or Kernel, and we have a powerful machine connected to many devices (scanner, printer, ethernet, use, ...) and there are many users want to use the machine.
If there isn't an OS, a user will run a specific program, then the program will directly access hard desk. What id there is a program needs the output of another, who can manage all these resources and connecting various devices to correct program. This is impossible without OS.
To manage all of these situations we need an OS.
With the help of the Kernel, a process will accomplish its job. Also the user will use the process through the Kernel and the Kernel will communicate with the devices as it has their drivers.
System Call
System call is the communication method between the process and the Kernel. It is an entry point into the Linux kernel.
If a process needs to do a service (privilege Service) such as accessing a hardware (not just a logic) like write on screen, read from usb, speak to another process, ... etc. It will not do this directly, it will ask the kernel to do that.
There is an interface (system call) between the Application and the Kernel. If an app need to do something, it will call the kernel and the kernel will do the task.
System call may differ from kernel to another, or from Unix to another. However, they have a standard to be Unix like operating system. Some systems may have system calls that don't exist in another. Command man syscalls
gives you the systems calls in the system.
C Library
C library in Linux is a collection of files containing a lot of functions that are needed by any OS or App to work its task. Ex. printf() function.
C library has two types of functions:
Normal Function
This type is working with user data like copying string from buffer to another, here we don't need the kernel to copy from memory space to another in the user App space.
Wrapper Function
System call isn't user friendly function to be used in an application. So, there is this type of wrapper function on the system call with added functionality.
If you want to read date from (terminal, screen, UART), you call a system call to read from standard input.
So, this type of function is not only a C code executed in the user space. It will do some tasks, then contact the kernel through a system call to accomplish a specific task and return to the C library then to the user.
Process Files
Any process when running, kernel opens three files for it:
- Standard Input (source of input comes from)
- Standard Output (default file for the output of the process)
- Standard Error (if error occurred, it will be printed in this file)
Each file is identified by file descriptor or file ID.
Examples on Files and System Calls
Let us print on screen by using a system call, we will use write system call. Command man 2 write
will gives you the information you need to use write
system call wrapper.
The prototype of write
is ssize_t write(int fd, const void *buf, size_t count);
- fd is the file descriptor
- *buf is an array of bytes
- count is the number of bytes in the buffer
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#define RET 0
int main()
{
char *buf;
buf = "Hello Linux\n";
int count = strlen(buf);
write(1, buf, count);
return RET;
}
Function write
is not the system call itself, it is a wrapper function on the system call. Calling a system call directly is a complex process.
How to know the system calls that are used by a function?
Command stacre
with the file name can do that strace ./myhello
Lets take another example and use another system call open
. In this example we will write the output of our process in a file not the screen.
The prototype of open
is int open(const char *pathname, int flags);
- pathname is the file path
- flags is what you need when opening the file like (Read only, read/write, create, ....)
Function open
returns the file descriptor on success.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#define RET 0
int main()
{
char *buf;
buf = "Hello Linux\n";
int count = strlen(buf);
int fd = open("./open_hello.txt", O_WRONLY|O_CREAT, 0644);
printf("FD is %d\n", fd);
write(fd, buf, count);
close(fd)
return RET;
}
Command Line Arguments
We knew that every command support options like ls -l
and arguments like ls /
or ls / /home
.
Sell takes the command and execute it, to execute a command it needs to parse the command line arguments.
As an example the below command has four arguments:
ls / /home /var
- ls
- /home
- /home
- /var
How the process takes the command line arguments?
ls
is an executable file on the hard disk. which ls
, file /usr/bin/ls
.
When you write ls
in terminal, shell construct the ls process and load it in memory to execute.
How ls process was passed arguments?
When we construct a process, we puts its command line arguments on its stack.
The first thing loaded on stack is argc and argv which are the command line arguments. And this is the way how the kernel passes the arguments that were invoked in the command line to the process.
Why we need to give the process the command line arguments?
Because the behavior of the process is dependent on these arguments.
main is not main
In Embedded Systems, the startup code initialize the main function. While we are creating a void main int main(void)
, it is actually have arguments int main(int argc, char *argv[])
when initialized by the startup code.
int argc
: the number of argumentschar *argv[]
: the arguments are passed as an array of strings
bash divide commands to tokens based on white spaces in between. By convention argv[]
ends with a null pointer. Then if argc
is 2, size of argv[]
is 3 pointers.
Any function argument is brought from the stack.
Let's write a program to print the command line arguments.
#include <stdio.h>
#define RET 0
int main(int argc, char *argv[])
{
printf("argc = %d\n", argc);
for(int i = 0; i < argc; i++)
{
printf("argv[%d] = %s\n", i, argv[i]);
}
return RET;
}
Now we know that when Kernel run a process, it gives it the command line arguments the were passe when running it. And if the process need to do some task with the system, it calls a system call.
Lets take another example and write our cp program.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define RET 0
int main(int argc, char *argv[])
{
char buf[100];
printf("argc= %d\n", argc);
if(argc != 3)
{
return -1;
}
int fd1 = open(argv[1], O_RDONLY);
int fd2 = open(argv[2], O_WRONLY|O_CREAT, 0644);
int count = read(fd1, buf, 100);
write(fd2, buf, count);
close(fd1);
close(fd2);
return RET;
}