0% found this document useful (0 votes)
170 views15 pages

System Calls Resources

System calls allow programs to communicate with the operating system. They are generally slower than normal function calls because control is relinquished to the OS. It's important to always check the return value of system calls as that is how the OS communicates information back to the program. The fork() system call creates a new child process that is identical to the parent process. Both the parent and child continue execution from the line after fork() and have separate address spaces. The open() system call opens a file and returns a file descriptor.

Uploaded by

takeaxe
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
170 views15 pages

System Calls Resources

System calls allow programs to communicate with the operating system. They are generally slower than normal function calls because control is relinquished to the OS. It's important to always check the return value of system calls as that is how the OS communicates information back to the program. The fork() system call creates a new child process that is identical to the parent process. Both the parent and child continue execution from the line after fork() and have separate address spaces. The open() system call opens a file and returns a file descriptor.

Uploaded by

takeaxe
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
You are on page 1/ 15

System Calls

System calls are special set of functions available in several programming languages. They are used by programs to communicate directly with an operating system. The operating system communicates back to the user's program through the return value of the function. Generally, system calls are slower than normal function calls. The reason is because when you call a system call, control is relinquished to the operating system to perform the system call. In addition, depending on the nature of the system call, your program may be blocked by the OS until the system call has finished, thus making the execution time of your program even longer. One rule of thumb should always be followed when calling a system call: Always check the return value. The return value is the only method that the operating system has to communicate information to your program. Thus, you should always check the return value in the event that the system call failed.

system() :
The system() call will execute an OS shell command as described by a character command string. This function is implemented using fork(), exec() and waitpid(). The command string is executed by calling /bin/sh -c command-string. During execution of the command, SIGCHLD will be blocked, and SIGINT and SIGQUIT will be ignored. The call "blocks" and waits for the task to be performed before continuing.

1 #include <stdio.h> 2 #include <stdlib.h>

3 main() 4{

5 6

system("ls -l"); printf("Command done!");

7}

The statement "Command done!" will not print untill the "ls -l" command has completed.

fork():
The fork() system call will spawn a new child process which is an identical process to the parent except that has a new system process ID. The process is copied in memory from the parent and a new process structure is assigned by the kernel. The return value of the function is which discriminates the two threads of execution. A zero is returned by the fork function in the child's process. The environment, resource limits, umask, controlling terminal, current working directory, root directory, signal masks and other process resources are also duplicated from the parent in the forked child process. [Potential Pitfall]: Some memory duplicated by a forked process such as file pointers, will cause intermixed output from both processes. Use the wait() function so that the processes do not access the file at the same time or open unique file descriptors. Some like stdout or stderr will be shared unless synchronized using wait() or some other mechanism. The file close on exit is another gotcha. A terminating process will close files before exiting. File locks set by the parent process are not inherited by the child process. [Potential Pitfall]: Race conditions can be created due to the unpredictability of when the kernel scheduler runs portions or time slices of the process. One can use wait(). the use of sleep() does not guarentee reliability of execution on a heavily loaded system as the scheduler behavior is not predictable by the application.

The fork() System Call

System call fork() is used to create processes. It takes no arguments and returns a process ID. The purpose of fork() is to create a new process, which becomes the child process of the caller. After a new child process is created, both processes will execute the next instruction following the fork() system call. Therefore, we have to distinguish the parent from the child. This can be done by testing the returned value of fork():

If fork() returns a negative value, the creation of a child process was unsuccessful. fork() returns a zero to the newly created child process. fork() returns a positive value, the process ID of the child process, to the parent. The returned process ID is of type pid_t defined in sys/types.h. Normally, the process ID is an integer. Moreover, a process can use function getpid() to retrieve the process ID assigned to this process.

Therefore, after the system call to fork(), a simple test can tell which process is the child. Please note that Unix will make an exact copy of the parent's address space and give it to the child. Therefore, the parent and child processes have separate address spaces.

Let us take an example to make the above points clear. This example does not distinguish parent and the child processes. Click here to download this file fork-01.c.
#include #include #include #define #define void { <stdio.h> <string.h> <sys/types.h> MAX_COUNT BUF_SIZE 200 100

main(void) pid_t int char pid; i; buf[BUF_SIZE];

fork(); pid = getpid(); for (i = 1; i <= MAX_COUNT; i++) { sprintf(buf, "This line is from pid %d, value = %d\n", pid, i); write(1, buf, strlen(buf)); }

Suppose the above program executes up to the point of the call to fork() (marked in red color):

If the call to fork() is executed successfully, Unix will


make two identical copies of address spaces, one for the parent and the other for the child. Both processes will start their execution at the next statement following the fork() call. In this case, both processes will start their execution at the assignment statement as shown below:

Both processes start their execution right after the system call fork(). Since both processes have identical but separate address spaces, those variables initialized before the fork() call have the same values in both address spaces. Since every process has its own address space, any modifications will be independent of the others. In other words, if the parent changes the value of its variable, the modification will only affect the variable in the parent process's address space. Other address spaces created by fork() calls will not be affected even though they have identical variable names.

What is the reason of using write rather than printf? It is because printf() is "buffered," meaning printf() will group the output of a process together. While buffering the output for the parent process, the child may also use printf to print out some information, which will also be buffered. As a result, since the output will not be send to screen immediately, you may not get the right order of the expected result. Worse, the output from the two processes may be mixed in strange ways. To overcome this problem, you may consider to use the "unbuffered" write. If you run this program, you might see the following on the screen:
................ This line is from pid This line is from pid ................ This line is from pid This line is from pid This line is from pid ................ This line is from pid This line is from pid ................ 3456, value 13 3456, value 14 3456, value 20 4617, value 100 4617, value 101 3456, value 21 3456, value 22

Process ID 3456 may be the one assigned to the parent or the child. Due to the fact that these processes are run concurrently, their output lines are intermixed in a rather unpredictable way. Moreover, the order of these lines are determined by the CPU scheduler. Hence, if you run this program again, you may get a totally different result.

Consider one more simple example, which distinguishes the parent from the child. Click here to download this file fork-02.c.
#include #include #define void void void { <stdio.h> <sys/types.h> MAX_COUNT 200 /* child process prototype */ /* parent process prototype */

ChildProcess(void); ParentProcess(void); main(void) pid_t pid;

pid = fork(); if (pid == 0) ChildProcess(); else ParentProcess(); } void { ChildProcess(void) int i;

} void {

for (i = 1; i <= MAX_COUNT; i++) printf(" This line is from child, value = %d\n", i); printf(" *** Child process is done ***\n"); ParentProcess(void) int i;

for (i = 1; i <= MAX_COUNT; i++) printf("This line is from parent, value = %d\n", i); printf("*** Parent is done ***\n"); }

In this program, both processes print lines that indicate (1) whether the line is printed by the child or by the parent process, and (2) the value of variable i. For simplicity, printf() is used.

When the main program executes fork(), an identical copy of its address space, including the program and all data, is created. System call fork() returns the child process ID to the parent and returns 0 to the child process. The following figure shows that in both address spaces there is a variable pid. The one in the parent receives the child's process ID 3456 and the one in the child receives 0.

Now both programs (i.e., the parent and child) will execute independent of each other starting at the next statement:

In the parent, since pid is non-zero, it calls function ParentProcess(). On the other hand, the child has a zero pid and calls ChildProcess() as shown below:

Due to the fact that the CPU scheduler will assign a time quantum to each process, the parent or the child process will run for some time before the control is switched to the other and the running process will print some lines before you can see any line printed by the other process. Therefore, the value of MAX_COUNT should be large enough so that both processes will run for at least two

or more time quanta. If the value of MAX_COUNT is so small that a process can finish in one time quantum, you will see two groups of lines, each of which contains all lines printed by the same process
The getpid() system call returns the process ID of the calling process. Though the ID is guaranteed to be unique, it should NOT be used for constructing temporary file names, for security reasons; see mkstemp(3) instead. The getppid() system call returns the process ID of the parent of the calling process.

Open
open is a system call that is used to open a new file and obtain its file descriptor.

Function Definition
int open(const char *path, int oflags); int open(const char *path, int oflags, mode_t mode);
Field const char *path Description The relative or absolute path to the file that is to be opened. A bitwise 'or' separated list of values that determine the method in which the file is to be opened (whether it should be read only, read/write, whether it should be cleared when opened, etc). See a list of legal values for this field at the end. A bitwise 'or' separated list of values that determine the permissions of the file if it is created. See a list of legal values at the end. Returns the file descriptor for the new file. The file descriptor returned is always the smallest integer greater than zero that is still available. If a negative value is returned, then there was an error opening the file.

int oflags

mode_t mode

return value

Code Snippet
Example using the open system call:

#include <unistd.h> #include <fcntl.h> int main() { size_t filedesc = open("testfile.txt", O_WRONLY | O_APPEND);

if(filedesc < 0) return 1; if(write(filedesc,"This will be output to testfile.txt\n", 36) != 36) { write(2,"There was an error writing to testfile.txt\n",43); return 1; } return 0; }

Available Values for oflag


Value O_RDONLY O_WRONLY O_RDWR O_APPEND O_TRUNC O_CREAT Open the file so that it is read only. Open the file so that it is write only. Open the file so that it can be read from and written to. Append new information to the end of the file. Initially clear all data from the file. If the file does not exist, create it. If the O_CREAT option is used, then you must include the third parameter. Combined with the O_CREAT option, it ensures that the caller must create the file. If the file already exists, the call will fail. Meaning

O_EXCL

Available Values for mode


Value S_IRUSR S_IWUSR S_IXUSR S_IRGRP S_IWGRP S_IXGRP S_IROTH Meaning Set read rights for the owner to true. Set write rights for the owner to true. Set execution rights for the owner to true. Set read rights for the group to true. Set write rights for the group to true. Set execution rights for the group to true. Set read rights for other users to true.

S_IWOTH S_IXOTH

Set write rights for other users to true. Set execution rights for other users to true.

Close
close is a system call that is used to close an open file descriptor.

Required Include Files


#include <unistd.h>

Function Definition
int close(int fildes);
Field int fildes return value The file descriptor to be closed. Retuns a 0 upon success, and a -1 upon failure. It is important to check the return value, because some network errors are not returned until the file is closed. Description

Code Snippet
Example closing an open file descriptor:

#include <stdlib.h> #include <fcntl.h> int main() { size_t filedesc = open("testfile.txt", O_WRONLY | O_CREAT); if(filedesc < 0) return 1; if(close(filedesc) < 0) return 1; return 0; }

Write
write is a system call that is used to write data out of a buffer.

Required Include Files


#include <unistd.h>

Function Definition
size_t write(int fildes, const void *buf, size_t nbytes);
Field Description The file descriptor of where to write the output. You can either use a file descriptor obtained from the open system call, or you can use 0, 1, or 2, to refer to standard input, standard output, or standard error, respectively. A null terminated character string of the content to write.

int fildes

const void *buf

size_t nbytes The number of bytes to write. If smaller than the provided buffer, the output is truncated. return value Returns the number of bytes that were written. If value is negative, then the system call returned an error.

Code Snippet
Example using standard file descriptors:

#include <unistd.h> int main(void) { if (write(1, "This will be output to standard out\n", 36) != 36) { write(2, "There was an error writing to standard out\n", 44); return -1; } return 0; }
Example using a file descriptor:

#include <unistd.h> #include <fcntl.h> int main(void) { int filedesc = open("testfile.txt", O_WRONLY | O_APPEND); if (filedesc < 0) { return -1; }

if (write(filedesc, "This will be output to testfile.txt\n", 36) != 36) { write(2, "There was an error writing to testfile.txt\n", 43); return -1; } return 0; }

Read
read is a system call used to read data into a buffer.

Required Include Files


#include <unistd.h>

Function Definition
size_t read(int fildes, void *buf, size_t nbytes);
Field Description The file descriptor of where to write the output. You can either use a file descriptor obtained from the open system call, or you can use 0, 1, or 2, to refer to standard input, standard output, or standard error, respectively. A character array where the read content will be stored. The number of bytes to read before truncating the data. If the data to be read is smaller than nbytes, all data is saved in the buffer. Returns the number of bytes that were read. If value is negative, then the system call returned an error.

int fildes

const void *buf size_t nbytes

return value

Code Snippet
#include <unistd.h> int main() { char data[128]; if(read(0, data, 128) < 0) write(2, "An error occurred in the read.\n", 31); exit(0); }

Stat
stat is a system call that is used to determine information about a file based on its file path.

Required Include Files


#include <unistd.h> #include <sys/stat.h> #include <sys/types.h>

Function Definition
int stat(const char *path, struct stat *buf);
Field const char *path struct stat *buf return value Description The file descriptor of the file that is being inquired. A structure where data about the file will be stored. A detailed look at all of the fields in this structure can be found in the struct stat page. Returns a negative value on failure.

Code Snippet
An example of code that uses the stat() system call is below.

#include #include #include #include

<unistd.h> <stdio.h> <sys/stat.h> <sys/types.h>

int main(int argc, char **argv) { if(argc != 2) return 1; struct stat fileStat; if(stat(argv[1],&fileStat) < 0) return 1; printf("Information for %s\n",argv[1]); printf("---------------------------\n"); printf("File Size: \t\t%d bytes\n",fileStat.st_size); printf("Number of Links: \t%d\n",fileStat.st_nlink); printf("File inode: \t\t%d\n",fileStat.st_ino); printf("File Permissions: \t"); printf( (S_ISDIR(fileStat.st_mode)) ? "d" : "-");

printf( (fileStat.st_mode printf( (fileStat.st_mode printf( (fileStat.st_mode printf( (fileStat.st_mode printf( (fileStat.st_mode printf( (fileStat.st_mode printf( (fileStat.st_mode printf( (fileStat.st_mode printf( (fileStat.st_mode printf("\n\n");

& & & & & & & & &

S_IRUSR) S_IWUSR) S_IXUSR) S_IRGRP) S_IWGRP) S_IXGRP) S_IROTH) S_IWOTH) S_IXOTH)

? ? ? ? ? ? ? ? ?

"r" "w" "x" "r" "w" "x" "r" "w" "x"

: : : : : : : : :

"-"); "-"); "-"); "-"); "-"); "-"); "-"); "-"); "-");

printf("The file %s a symbolic link\n", (S_ISLNK(fileStat.st_mode)) ? "is" : "is not"); } return 0;

The output of this program is shown in the following set of commands: Information on the current files in the directory:

$ ls -l total 16 -rwxr-xr-x 1 stargazer stargazer 36 2008-05-06 20:50 testfile.sh -rwxr-xr-x 1 stargazer stargazer 7780 2008-05-07 12:36 testProgram -rw-r--r-- 1 stargazer stargazer 1229 2008-05-07 12:04 testProgram.c
Running the program with the file testfile.sh

$ ./testProgram testfile.sh Information for testfile.sh --------------------------File Size: 36 bytes Number of Links: 1 File inode: 180055 File Permissions: -rwxr-xr-x The file is not a symbolic link
Running the program with the files testProgram.c

$ ./testProgram testProgram.c Information for testProgram.c --------------------------File Size: 1229 bytes Number of Links: 1 File inode: 295487 File Permissions: -rw-r--r-The file is not a symbolic link

Running the program with the directory /home/stargazer

$ ./testProgram /home/stargazer Information for /home/stargazer --------------------------File Size: 4096 bytes Number of Links: 61 File inode: 32706 File Permissions: drwxr-xr-x The file is not a symbolic link

wait():
The parent process will often want to wait until all child processes have been completed. this can be implemented with the wait() function call. wait(): Blocks calling process until the child process terminates. If child process has already teminated, the wait() call returns immediately. if the calling process has multiple child processes, the function returns when one returns

You might also like