Вы находитесь на странице: 1из 33

UNIX SYSTEMS PROGRAMMING AND COMPILER DESIGN LABORATORY - 10CSL67

POSIX an acronym for "Portable Operating System Interface" is a family of standards specified by the IEEE for maintaining compatibility between operating systems. POSIX defines the application programming interface (API), along with command line shells and utility interfaces, for software compatibility with variants of Unix and other operating systems. Define the macros by using #define preprocessor directives at the top of your source code files. These directives must come before any #include of a system header file.

_POSIX_SOURCE If you define this macro, then the functionality from the POSIX.1 standard (IEEE Standard 1003.1) is available, as well as all of the ISO C facilities. The state of _POSIX_SOURCE is irrelevant if you define the macro

_POSIX_C_SOURCE to a positive integer. _POSIX_C_SOURCE Define this macro to a positive integer to control which POSIX functionality is made available. The greater the value of this macro, the more functionality is made available. If you define this macro to a value greater than or equal to 1, then the functionality from the 1990 edition of the POSIX.1 standard (IEEE Standard 1003.1-1990) is made available. If you define this macro to a value greater than or equal to 2, then the functionality from the 1992 edition of the POSIX.2 standard (IEEE Standard 1003.2-1992) is made available. If you define this macro to a value greater than or equal to 199309L, then the functionality from the 1993 edition of the POSIX.1b standard (IEEE Standard 1003.1b1993) is made available.

A Example Program to check and display _POSIX_VERSION constant of the system on which it runs.
#define _POSIX_SOURCE #define _POSIX_C_SOURCE 200809L #include <iostream.h> #include <unistd.h> int main() { #ifdef _POSIX_VERSION cout << "System conforms to POSIX: " << _POSIX_VERSION << endl; #else cout << "_POSIX_VERSION is undefined\n"; #endif return 0; }

Work on the following list of POSIX.1 defined constants available in the <limits.h> header. POSIX.1 define constants are related to processes and files

Work on the following list of POSIX.1b defined constants in the <limits.h> header. POSIX.1b defined including PCs constants are related to real time operating system interfaces

1. Write a C/C++ POSIX compliant program to check the following limits: No. of clock ticks Max. no. of child processes Max. path length Max. no. of characters in a file name Max. no. of open files/ process

sysconf This function is used to query general system wide configuration limits that are implemented on a given system. The function prototype is: long int sysconf (int parameter) The parameter argument refers to any of the `_SC_' symbols listed in the table below. The normal return value from sysconf is the value you requested. A value of -1 is returned both if the implementation does not impose a limit, and in case of an error. _SC_CLK_TCK The number of clock ticks per second _SC_CHILD_MAX Maximum number of child processes that may be owned by a process simulataneously. _SC_OPEN_MAX Maximum number of opened files per process. pathconf When the machine allows different files to have different values for a file system parameter, use these functions to find out the value that applies to any particular file. This function is used to inquire about the limits that apply to the file named filename. These functions and the associated constants for the parameter argument are declared in the header file `unistd.h'. long int pathconf (const char *filename, int parameter)

The parameter argument should be one of the `_PC_' constants listed below. The normal return value from pathconf is the value you requested. A value of -1 is returned both if the implementation does not impose a limit, and in case of an error. _PC_PATH_MAX Maximum length, in bytes, of pathname. _POSIX_NAME_MAX Maximum length, in bytes, of filename.
#define _POSIX_SOURCE #define _POSIX_C_SOURCE 200809L #include<iostream> using namespace std; #include<unistd.h> #include<limits.h> int main() { cout<<"No. of clock ticks: " <<sysconf(_SC_CLK_TCK)<<endl; cout<<"Max no. of child processes:" <<sysconf(_SC_CHILD_MAX)<<endl; cout<<"Max path length:" <<pathconf("/",_PC_PATH_MAX)<<endl; cout<<"Max characters in filen name:" <<pathconf("/",_PC_NAME_MAX)<<endl; cout<<"Max no. of open files per process:" <<sysconf(_SC_OPEN_MAX)<<endl; return 0; }

/*OUTPUT*/ No. of clock ticks: 100 Max no. of child processes:1024 Max path length:4096 Max characters in filen name:255 Max no. of open files per process:1024

Work on the Following list of manifested constant supported by sysconf function are:

Work on the Following list of manifested constant supported by pathconf function are:

2. Write a C/C++ POSIX compliant program that prints the POSIX defined configuration options supported on any given system using feature test macros. The POSIX Feature Test Macros :Feature-test macros provide a means for writing portable programs. It allows the programmer to control the definitions that are exposed by system header files when a program is compiled. The exact set of features available when you compile a source file is controlled by which feature test macros you define. POSIX.1 defines a set of feature test macros which if defined on a system, means that the system has implemented the corresponding features. All these test macros are defined in <unistd.h> header. Some feature test macros are useful for creating portable applications, by preventing nonstandard definitions from being exposed. Other macros can be used to expose nonstandard definitions that are not exposed by default. For the following macros used in the program , if the macro is defined in unistd.h, then the
option is supported.

_POSIX_JOB_CONTROL : If this symbol is defined, it indicates that the system supports job control. Otherwise, the implementation behaves as if all processes within a session belong to a single process group. _POSIX_SAVED_IDS: If this symbol is defined, it indicates that the system remembers the effective user and group IDs of a process before it executes an executable file with the set-userID or set-group-ID bits set, and that explicitly changing the effective user or group IDs back to these values is permitted. If this option is not defined, then if a nonprivileged process changes its effective user or group ID to the real user or group ID of the process, it can't change it back again _POSIX_CHOWN_RESTRICTED:If this option is in effect, the chown function is restricted so that the only changes permitted to nonprivileged processes is to change the group owner of a file to either be the effective group ID of the process, or one of its supplementary group IDs. _POSIX_NO_TRUNC:If this option is in effect, file name components longer than NAME_MAX generate an ENAMETOOLONG error. Otherwise, file name components that are too long are silently truncated. _POSIX_VDISABLE:This option is only meaningful for files that are terminal devices. If it is enabled, then handling for special control characters can be disabled individually.

#define _POSIX_SOURCE #define _POSIX_C_SOURCE 200809L #include <iostream> #include <unistd.h> using namespace std; int main() { #ifdef _POSIX_JOB_CONTROL cout << "System supports job control\n"; #else cout << "System does not support job control\n"; #endif #ifdef _POSIX_SAVED_IDS cout << "System supports saved set-UID and saved set-GID\n"; #else cout << "System does not support saved set-UID and saved set-GID\n"; #endif #ifdef _POSIX_CHOWN_RESTRICTED cout << "chown restricted option is: " << _POSIX_CHOWN_RESTRICTED << endl; #else cout << "System does not support system-wide chown_restricted option\n"; #endif #ifdef _POSIX_NO_TRUNC cout << "Pathname trucnation option is: " << _POSIX_NO_TRUNC << endl; #else cout << "System does not support system-wide pathname trucnation option\n"; #endif #ifdef _POSIX_VDISABLE cout << "Diable character for terminal files is: " << _POSIX_VDISABLE << endl; #else cout << "System does not support _POSIX_VDISABLE\n"; #endif return 0; }

/*OUTPUT*/ System supports job control System supports saved set-UID and saved set-GID chown restricted option is: 0 Pathname trucnation option is: 1 Diable character for terminal files is:

3. Consider the last 100 bytes as a region. Write a C/C++ program to check whether the region is locked or not. If the region is locked, print pid of the process which has locked. If the region is not locked, lock the region with an exclusive lock, read the last 50 bytes and unlock the region.
#include<iostream> using namespace std; #include<stdio.h> #include<sys/types.h> #include<fcntl.h> #include<unistd.h> int main(int argc, char* argv[]) { struct flock fvar; int fdesc; while (--argc > 0) { /* do the following for each file */ if ((fdesc=open(*++argv,O_RDWR))==-1) { perror("open"); continue; } fvar.l_type = F_WRLCK; fvar.l_whence = SEEK_SET; fvar.l_start = 0; fvar.l_len = 0; /* Attempt to set an exclusive (write) lock on the entire file */ while (fcntl(fdesc, F_SETLK,&fvar)==-1) { /* Set lock fails, find out who has locked the file */ while(fcntl(fdesc,F_GETLK,&fvar)!=-1&& var.l_type!=F_UNLCK) { cout << *argv << " locked by " << fvar.l_pid<< " from " << fvar.l_start << " for "<< fvar.l_len << " byte for " <<(fvar.l_type==F_WRLCK ? 'w' : 'r') << endl; if (!fvar.l_len) break; fvar.l_start += fvar.l_len; fvar.l_len = 0; } /* while there are locks set by other processes */ }/* while set lock un-successful */ /* Lock the file OK. Now process data in the file */ /*... */ /* Now unlock the entire file */ fvar.l_type = F_UNLCK; fvar.l_whence = SEEK_SET; fvar.l_start = 0; fvar.l_len = 0; if (fcntl(fdesc, F_SETLKW,&fvar)==-1) perror("fcntl"); } return 0; }

4. Write a C/C++ program which demonstrates interprocess communication between a reader process and a writer process. Use mkfifo, open, read, write and close APIs in your program. Inter process communication using named pipes(FIFO) A FIFO(a named pipe) is similar to a pipe, except that it is accessed as part of the file system. It can be opened by multiple processes for reading or writing. When processes are exchanging data via the FIFO, the kernel passes all data internally without writing it to the file system. A FIFO (First In First Out) is a one-way flow of data. FIFOs have a name, so unrelated processes can share the FIFO. The kernel maintains exactly one pipe object for each FIFO special file that is opened by at least one process. The FIFO must be opened on both ends (reading and writing) before data can be passed. Normally, opening the FIFO blocks until the other end is opened also. A FIFO is created by the mkfifo function: int mkfifo(const char *pathname, mode_t mode); pathname a UNIX pathname (path and filename). mode the file permission bits. Open: mkfifo tries to create a new FIFO. If the FIFO already exists, then an EEXIST error is returned.

Close: to close an open FIFO, use close().

Write:It writes data, in bytes as specified by the caller, from a buffer declared by the user in the program and then writes it into the file supplied by the calling process. size_t write(int fd, const void *buf, size_t nbytes); Write, thus takes three arguments: 1. The file descriptor of the file (fd). 2. The buffer from where data is to be written into the file (buf). 3. The number of bytes to be read from the buffer (nbytes).

Read:This system call reads data, in bytes as specified by the caller, from the file and stores then into a buffer supplied by the calling process. ssize_t read(int fd, void *buf, size_t count); The read system call can take three arguments: 1. The file descriptor of the file, 2. the buffer where the read data is to be stored and 3. the number of bytes to be read from the file.
#include <iostream> using namespace std; #include <errno.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <sys/stat.h> int main( int argc, char* argv[]) { if (argc !=2 && argc !=3) { cout << "usage: " << argv[0] << " <file> [<arg>]\n"; return 0; } int fd,count=0; char buf[2560]; (void)mkfifo(argv[1],S_IFIFO|0777);/*reader process */ if(argc==2) { fd = open(argv[1],O_RDONLY|O_NONBLOCK); //while (read(fd,buf,sizeof(buf))==-1 && errno==EAGAIN) //sleep(1); while (read(fd,buf,sizeof(buf)) > 0) { cout << buf << endl; } cout<<"Read completed"; } else { /* writer process */ fd = open(argv[1],O_WRONLY); write(fd,argv[2],strlen(argv[2])); cout<<"Write completed"; } close(fd);

return 0; }

5. a) Write a C/C++ program that outputs the contents of its Environment list

Environment list is an array of character pointers, with each pointer containing the address of a null-terminated C string. Environment list as a set of parameters that are inherited from process to process. Each program is also passed an environment list. Like the argument list. In almost all operating systems each process has its own private set of environment variables. By default, when a process is created it inherits a duplicate environment of its parent process, except for explicit changes made by the parent when it creates the child. Running programs can access the values of environment variables for configuration purposes.

Examples of environment variables include:

Path - lists directories the shell searches, for the commands the user may type without having to provide the full path.

Temp - location where processes can store temporary files UserProfile - indicate where a user's home directory is located in the file system. Temp - location where processes can store temporary files. TERM -The name of the user's terminal. Used to determine the capabilities of the terminal.

USER- Current logged in user's name. SHELL - The current shell.

The function prototype of the main function looks like: int main(int argc, char *argv[]); argc is non-negative - argument count - number of command-line arguments. argv[argc] is a null pointer - argument vector arguments. The names of argc and argv may be any valid identifier in C. Unix (though not POSIX.1) and Microsoft Windows have a third argument giving the program's environment, int main(int argc, char **argv, char **envp); By convention, the command-line arguments specified by argc and argv include the name of the program as the first element if argc is greater than 0; if a user types a command of "rm file", the shell will initialise the rm process with argc = 2 and argv = ["rm", "file", NULL]. envp is available as an argument to main, as envp - a null terminated array of strings: - values of the program's command-line

#include<unistd.h> #include<iostream> #include<stdlib.h> using namespace std; int main(int argc,char **argv,char **envp) { cout<<"ENVIRONMENT LIST:\n"; while(*envp!=NULL) { cout<<*envp<<endl; envp++; } return 0; }

/*OUTPUT*/ ENVIRONMENT LIST: ORBIT_SOCKETDIR=/tmp/orbit-ksit HOSTNAME=networklab1 IMSETTINGS_INTEGRATE_DESKTOP=yes GPG_AGENT_INFO=/tmp/seahorse-O9TRh3/S.gpg-agent:2368:1; TERM=xterm SHELL=/bin/bash XDG_SESSION_COOKIE=f0f769483ec1e461615dfc8c0000001c-1358851278.6791031904428157 HISTSIZE=1000 GTK_RC_FILES=/etc/gtk/gtkrc:/home/ksit/.gtkrc-1.2-gnome2 WINDOWID=39845891 QTDIR=/usr/lib/qt-3.3 QTINC=/usr/lib/qt-3.3/include IMSETTINGS_MODULE=none USER=ksit b) Write a C / C++ program to emulate the unix ln command

ln is a standard Unix command used to create links (link) to files. Links allow more than one file name to refer to the same file, elsewhere. There are two types of links, both of which are created by ln: 1. symbolic links, which refer to a symbolic path indicating the abstract location of another file 2. hard links, which refer to the specific location of physical data.

Symbolic link creation A soft link, also called symbolic link, is a file that contains the name of another file. We can then access the contents of the other file through that name. That is, a symbolic link is like a pointer to the pointer to the file's contents. The function Prototype is int symlink(const char *oldlink, const char *symlink); How to create a symlink? Use the ln command with the -s parameter. $ ln -s fileA fileB where fileA is the original file and fileB is the name you want to give to the symbolic link. Now, let's take a look at these two objects with the ls command again: $ ls -l You can see that you get different result as compared to when we displayed the hard link. The first difference between symlink and the original file is the inode number. The inode is different for the original file and for the symbolic link. There is the pipe symbol "l" before the permissions on the symlink line. Has different permissions than the original file (because it is just a symbolic link). The content of the symlink is just a string pointing to the original file. The size of the symlink is not the same as the size of the original file. The symbolic link is a separate entity and as such occupies some space on your hard drive. Hard link creation A hard link is essentially a label or name assigned to a file. It is possible to create a number of different names that all refer to the same contents. A hard link is a pointer to the file's i-node.

How to create a hardlink? Use the ln command to create a hard link. $ ln fileA fileB where fileA is the original file and fileB is the name you want to give to the hardlink. You have the original file and one hard link that is attached to it. Now, you look at these two objects with the ls command: $ ls -l You can see in the output of this command that both files fileA and fileB have the same inode number. In addition to having the same inode, both files have the same file permissions and the same size. Because that size is reported for the same inode, we can see that a hard link does not occupy any extra space on your space.

#include <iostream> using namespace std; #include <sys/types.h> #include <unistd.h> #include <string.h> int main (int argc, char* argv[]) { char* buf[256], tname[256]; if ((argc< 3 && argc > 4) || (argc==4 && strcmp(argv[1],"-s"))) { cout << "usage: " << argv[0] << " [-s] <orig_file> <new_link>\n"; return 1; } if (argc==4) { cout<<"Symbolic Link"; return symlink( argv[2], argv[3]); /* create a symbolic link */ } else return link(argv[1], argv[2]); /* create a hard link */ return 0; }

/*OUTPUT for symbolic link */ [ksit@networklab1 ~]$ g++ 5B.CPP

[ksit@networklab1 ~]$ ./a.out -s 2.CPP a1.txt Symbolic link [ksit@networklab1 ~]$ ls -l total 52 -rwxr-xr-x. 2 ksit ksit 680 Jan 22 16:17 1.CPP -rw-r--r--. 1 ksit ksit 680 Jan 21 20:34 1.CPP~ -rwxr-xr-x. 1 ksit ksit 1284 Jan 21 16:47 2.CPP -rwxr-xr-x. 1 ksit ksit 1321 Jan 21 16:53 3.CPP -rwxr-xr-x. 1 ksit ksit 897 Jan 21 21:34 4.CPP -rwxr-xr-x. 1 ksit ksit 669 Jan 22 16:47 5A.CPP -rwxr-xr-x. 1 ksit ksit 542 Jan 22 16:50 5B.CPP lrwxrwxrwx. 1 ksit ksit 5 Jan 22 17:05 a1.txt -> 2.CPP -rwxrwxr-x. 1 ksit ksit 6423 Jan 22 17:04 a.out drwxr-xr-x. 5 ksit ksit 4096 Jan 21 15:09 Desktop -rwxr-xr-x. 2 ksit ksit 680 Jan 22 16:17 new1.txt -rwxr-xr-x. 1 ksit ksit 220 Jan 21 17:29 sam.CPP -rw-rw-r--. 1 ksit ksit 11 Jan 21 21:16 SAN.CPP

/*OUTPUT for hard link */ [ksit@networklab1 ~]$ g++ 5B.CPP [ksit@networklab1 ~]$ ./a.out 3.CPP a2.txt [ksit@networklab1 ~]$ ls -l total 56 -rwxr-xr-x. 2 ksit ksit 680 Jan 22 16:17 1.CPP -rw-r--r--. 1 ksit ksit 680 Jan 21 20:34 1.CPP~ -rwxr-xr-x. 1 ksit ksit 1284 Jan 21 16:47 2.CPP -rwxr-xr-x. 2 ksit ksit 1321 Jan 21 16:53 3.CPP -rwxr-xr-x. 1 ksit ksit 897 Jan 21 21:34 4.CPP -rwxr-xr-x. 1 ksit ksit 669 Jan 22 16:47 5A.CPP -rwxr-xr-x. 1 ksit ksit 542 Jan 22 16:50 5B.CPP lrwxrwxrwx. 1 ksit ksit 5 Jan 22 17:05 a1.txt -> 2.CPP -rwxr-xr-x. 2 ksit ksit 1321 Jan 21 16:53 a2.txt -rwxrwxr-x. 1 ksit ksit 6423 Jan 22 17:04 a.out drwxr-xr-x. 5 ksit ksit 4096 Jan 21 15:09 Desktop -rwxr-xr-x. 2 ksit ksit 680 Jan 22 16:17 new1.txt -rwxr-xr-x. 1 ksit ksit 220 Jan 21 17:29 sam.CPP -rw-rw-r--. 1 ksit ksit 11 Jan 21 21:16 SAN.CPP

6. Write a C/C++ program to illustrate the race condition.

Race condition occurs when multiple processes are trying to do something with shared data and the final outcome depends on the order in which the processes run. The fork function is a source for it. Depends on whether the parent or child runs first after the fork. In general, we cannot predict which process runs first. Even if we knew which process would run first, what happens after that process starts running depends on the system load and the kernel's scheduling algorithm. To avoid race conditions and to avoid polling, some form of signaling is required between multiple processes. Various forms of inter process communication (IPC) can also be used.

The below program outputs two strings: one from the child and one from the parent. The program contains a race condition because the output depends on the order in which the processes are run by the kernel and for how long each process runs. We set the standard output unbuffered, so every character output generates a write. The goal in this example is to allow the kernel to switch between the two processes as often as possible to demonstrate the race condition.
#include<iostream> #include<stdio.h> #include<stdlib.h> #include<sys/types.h> #include<string.h> #include<unistd.h> using namespace std; void charatatime(char *); /*Here child will wait for the parent to finish its execution and then the child continues*/ int main() { pid_t pid; if ((pid=fork())<0) { cout<<"FORK ERROR\n" <<endl; } else if (pid==0)

{ charatatime("output from child\n"); } else { charatatime("output from parent\n"); } exit(0); } void charatatime(char *str) { char *ptr; int c; setbuf(stdout,NULL); for(ptr=str;(c=*ptr++)!=0;) putc(c,stdout); }

/*OUTPUT*/ output from parent output from child

7. Write a C/C++ program that creates a zombie and then calls system to execute the ps command to verify that the process is zombie. Zombie process or defunct process is a process that have completed the execution, have released all the resources (CPU, memory) but still had an entry in the process table. When a process finishes execution, it will have an exit status to report to its parent process. Because of this last little bit of information, the process will remain in the operating systems process ta ble as a zombie process, indicating that it is not to be scheduled for further execution, but that it cannot be completely removed (and its process ID cannot be reused) until it has been determined that the exit status is no longer needed. When a child exits, the parent process will receive a SIGCHLD signal to indicate that one of its children has finished executing; the parent process will typically call the wait() system call at this point. That call will provide the parent with the childs exit status, and will cause the child to be reaped, or removed from the process table. Most of the time, the reason for existence of Zombie process is bad coding. Normally, when a child (subprocess) finishes its task and exits, then its parent is suppose to use the wait system call and get the status of the process. So, until the parent process dont check for the childs exit status, the process is a Zombie process, but it usually is very small duration. But if due to any

reason (bad programming or a bug), the parent process didnt check the status of the child and dont call wait, the child process stays in the state of Zombie waiting for parent to check its status. To see if there are zombie processes on a system run ps aux and look for a Z in the STAT column. The ps (i.e., process status) command is used to provide information about the currently running processes, including their process identification numbers (PIDs). system() executes a command specified in string by calling /bin/sh -c string, and returns after the command has been completed. sleep is a Unix command line program that suspends program execution for a specified period of time.
#include<iostream> #include<stdlib.h> #include<unistd.h> #include<errno.h> #include<ctype.h> #include<stdio.h> #include<sys/types.h> using namespace std; #define PSCMD "ps -el|grep 'Z'" int main(void) { pid_t pid; if((pid=fork( ))<0) cout<<"fork error"; else if(pid==0) exit(0); /*parent*/ sleep(4); system(PSCMD); exit(0); }

output:
./a.out F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY 1 Z 501 2913 2912 0 80 0 0? TIME CMD

pts/0 00:00:00 a.out <defunct>

8. Write a C/C++ program to avoid zombie process by forking twice. A zombie process occupies a pid in the system, decrease the available pids in the system. Zombies are mark as "defunct" while checking the process by the "ps" command. However, sometimes we do not want the parent process to wait for its child process for a long time. There is a way to achieve both "not create zombie process" and "not wait for the child process to its termination", and the way is to do a double forkwhen a parent process (say A) want to fork a child process to do "something". Process A does not fork a process to do "something" directly. Process A first forks a child process (say B), and process then forks its child process (say C) to do "something" and process B terminates as soon as process C is created. In this way, process A only has to wait for process B for a short time. In the same time, since it has no parent process (process B is dead), the system will "rechild" process C to the init process. The init process calls wait() for its child process, solving the zombie process problem. The child becomes a zombie after it terminates, if its parent is still alive, and it doesn't call wait. Until the parent calls wait, or the parent exits, and the child is inherited by init, the child remains a zombie.By using the double-fork trick, the child (well, the grandchild really) is immediately inherited by init, so it can never become a zombie (and you can never retrieve its exit code either).When a process's parent terminates, the "init" process takes over as its parent. So when the child process exits, the grandchild loses its parent, and is adopted by init. Init always reaps its dead children, so they don't become zombies.

#include <iostream> #include<stdio.h> #include<unistd.h> #include<stdlib.h> using namespace std; #include <sys/wait.h> #include<errno.h> int main(void)

pid_t pid; if((pid=fork())<0) { printf("fork error"); } else if(pid == 0) { if((pid =fork())<0) printf("fork error"); else if(pid>0) exit(0); sleep(2); printf("second child, parent pid = %d\n", getppid()); exit(0); }

if(waitpid(pid, NULL,0)!=pid) printf("waitpid error"); exit(0); }

output: ./a.out second child, parent pid = 1

9. Write a C/C++ program to implement the system function.

The below program prompts users to enter shell commands from standard input and executes each command via the system function. The program terminates normally when either user enters end-of-file(<ctrl-D>) at the shell prompt or the return status odf a System call is nonzero.
The system function emulates the C library function system. The system function prototype is: int system(const char *cmd);

Both functions invoke the Bourne shell(/bin/sh) to interpret and execute a shell command that is specified via the argument cmd. A command may consist of simple shell command or a series of shell commands separated by semicolons or pipes. The system function calls fork to create a child process. The child process in turn, calls execlp to execute a Bourne shell program(/bin/sh) with c and cmd arguments. The c option instructs the Bourne shell to interpret and execute the cmd arguments as if they were entered at shell level. After cmd is executed, the child process is terminated and the exit status of the Bourne shell is passed to the parent process, which calls the system function. Note that the system function calls waitpid to specifically wait for the child that it forked. This is important, as the system function may be called by a process before that forked a child process calling system; thus, the system function would wait only for child processes forked by it and not those created by the calling process. When the waitpid returns, the system function checks that1) the return PID matches that of the child process that it forked; and (2) the child terminated via _exit. If both conditions are true, the system function returns the child exit code. Otherwise a -1 to indicate failure status. The exec collection of functions of Unix-like operating systems cause the running process to be completely replaced by the program passed as an argument to the function. As a new process is not created, the process identifier (PID) does not change, but the data, heap and stack of the original process are replaced by those of the new process. The functions are declared in the unistd.h header for the POSIX standard and in process.h for DOS, OS/2, and Windows.
int execl(char const *path, char const *arg0, ...);

l - Command-line arguments are passed individually to the function.

path The argument specifies the path name of the file to execute as the new process image. Arguments beginning at arg0 are pointers to arguments to be passed to the new process image. The argv value is an array of pointers to arguments. arg0 The first argument arg0 should be the name of the executable file. Usually it is the same value as
the path argument.

#include <iostream> using namespace std; #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h>

#include <sys/wait.h> #include <unistd.h> int System( const char *cmd) // emulate the C system function { pid_t int pid; status;

switch (pid=fork()) { case -1: return -1;

case 0: execl("/bin/sh","sh","-c",cmd,0); perror("execl"); exit(errno); } if (waitpid(pid,&status,0)==pid && WIFEXITED(status)) return WEXITSTATUS(status); return -1; } int main() { int char do rc = 0; buf[256];

{ printf("sh> "); fflush(stdout); if (!gets(buf)) break; rc = System(buf); } while (!rc); return (rc); }

output: ./a.out sh> cal January 2013 Su Mo Tu We Th Fr Sa 1 6 7 8 2 3 4 5

9 10 11 12

13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 sh> date Tue Jan 22 16:37:53 IST 2013 sh>ctrl d

10. Write a C/C++ program to set up a real-time clock interval timer using the alarm API. #include <stdio.h> #include <unistd.h> #include <signal.h> void handler(int); int main() { struct sigaction action;

sigemptyset(&action.sa_mask); action.sa_handler = handler; action.sa_flags = SA_RESTART; if( sigaction( SIGALRM,&action,0 )==-1 ) { perror( "sigaction"); return 1; } if (alarm(500) == -1) { perror("alarm" ); } else for(;;) { printf("System } return 0; } void handler(int signum) { alarm(500); set\n");

printf("Scheduled\n"); }

11. Write a C program to implement the syntax-directed definition of if E then S1 and if E then S1 else S2. (Refer Fig. 8.23 in the text book prescribed for 06CS62 Compiler Design, Alfred V Aho, Ravi Sethi, and Jeffrey D Ullman: Compilers- Principles, Techniques and Tools, 2nd Edition, Pearson Education, 2007).

/* Input to the program is assumed to be syntactically correct. The expression of if statement, statement for true condition and statement for false condition are enclosed in parenthesis */ #include <stdio.h> #include <stdlib.h> #include <conio.h> #include <string.h> int parsecondition(char[],int,char*,int); void gen(char [],char [],char[],int);

int main() { int counter = 0,stlen =0,elseflag=0; char stmt[60]; // contains the input statement char strB[54]; // holds the expression for 'if' condition char strS1[50]; // holds the statement for true condition char strS2[45]; // holds the statement for false condition

clrscr(); printf("Format of if statement \n Example ...\n"); printf("if (a<b) then (s=a);\n"); printf("if (a<b) then (s=a) else (s=b);\n\n"); printf("Enter the statement \n"); gets(stmt); stlen = strlen(stmt);

counter = counter + 2; // increment over 'if' counter = parsecondition(stmt,counter,strB,stlen);

if(stmt[counter]==')') counter++; counter = counter + 3; // increment over 'then'

counter = parsecondition(stmt,counter,strS1,stlen); if(stmt[counter+1]==';') { // reached end of statement, generate the output printf("\n Parsing the input statement...."); gen(strB,strS1,strS2,elseflag); getch(); return 0; }

if(stmt[counter]==')') counter++; // increment over ')' // increment over 'else'

counter = counter + 3;

counter = parsecondition(stmt,counter,strS2,stlen); counter = counter + 2; if(counter == stlen) { //generate the output elseflag = 1; printf("\n Parsing the input statement...."); gen(strB,strS1,strS2,elseflag); getch(); return 0; } return 0; } /* Function : parsecondition Description : This function parses the statement from the given index to get the statement enclosed in () Input : Statement, index to begin search, string to store the condition, total string length Output : Returns 0 on failure, Non zero counter value on success*/ int parsecondition(char input[],int cntr,char *dest,int totallen) // move to the end of statement

{ int index = 0,pos = 0; while(input[cntr]!= '(' && cntr <= totallen) cntr++; if(cntr >= totallen) return 0; index = cntr; while (input[cntr]!=')') cntr++; if(cntr >= totallen) return 0; while(index<=cntr) dest[pos++] = input[index++]; dest[pos]='\0'; //null terminate the string return cntr; //non zero value }

/* Function : gen () Description : This function generates three address Expression, statement for true condition, statement for flag to denote if the 'else' part is present in the statement output :Three address code*/ code Input : false condition,

void gen(char B[],char S1[],char S2[],int elsepart) { int Bt =101,Bf = 102,Sn =103; printf("\n\tIf %s goto %d",B,Bt); printf("\n\tgoto %d",Bf); printf("\n%d: ",Bt); printf("%s",S1); if(!elsepart) printf("\n%d: ",Bf);

else { printf("\n\tgoto %d",Sn);

printf("\n%d: %s",Bf,S2); printf("\n%d:",Sn); } } output: Format of if statement Example ... if (a<b) then (s=a); if (a<b) then (s=a) else (s=b);

Enter the statement if (a<b) then (s=a);

Parsing the input statement.... 100:If (a<b) goto 102 101:goto 103 102: (s=a) 103: s.next

output: Format of if statement Example ... if (a<b) then (s=a); if (a<b) then (s=a) else (s=b);

Enter the statement if (a<b) then (s=a) else (s=b);

Parsing the input statement.... 100:If (a<b) goto 102 101:goto 104 102: (s=a) 103:goto 105 104: (s=b) 105 s.next:

12. Write a yacc program that accepts a regular expression as input and produce its parse tree as output.
//yacc program %{ #include<ctype.h> char str[20]; int i=0; %} %token id %left '+''/''*''-' %% E:S {infix_postfix(str);} S:S'+'T |S'-'T |T ; T:T'*'F | T'/'F |F ; F:id |'('S')' | ; %% //C Program #include<stdio.h> main() { printf("\nEnter an identifier:"); yyparse(); } yyerror() { printf("invalid"); } yylex(){ char ch=' '; while(ch!='\n'){

ch=getchar(); str[i++]=ch; if(isalpha(ch)) return id; if(ch=='+'||ch=='*'|| ch=='-'||ch=='/') return ch;} str[--i]='\0'; return(0); exit(0); } void push(char stack[],int *top,char ch) { stack[++(*top)]=ch; } char pop(char stack[],int *top) { return(stack[(*top)--]); } int prec(char ch) { switch(ch) { case '/': case '*': return 2; case '+': case '-': return 1; case '(': return 0; default: return -1; } } void infix_postfix(char infix[]) { int top=-1,iptr=-1,pptr=-1; char postfix[20],stack[20],stksymb,cursymb; push(stack,&top,'\0'); while((cursymb=infix[++iptr])!='\0') { switch(cursymb) { case '(': push(stack,&top,cursymb); break; case ')': stksymb=pop(stack,&top); while(stksymb!='(') { postfix[++pptr]=stksymb; stksymb=pop(stack,&top); } break; case '*': case '/': case '+': case -:while(prec(stack[top])>=prec(cursymb)) postfix[++pptr]=pop(stack,&top); push(stack,&top,cursymb);break; default: if(isalnum(cursymb)==0) {printf("Error in input!"); exit(0);} postfix[++pptr]=cursymb; }

} while(top!=-1) postfix[++pptr]=pop(stack,&top); printf("%s\n",postfix); } Input a+b*c/d (a+b)*c/d Output abc*d/+ ab+c*d/

Вам также может понравиться