0% found this document useful (0 votes)
18 views17 pages

Linux Unit IV

Uploaded by

hmmailbox36
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
18 views17 pages

Linux Unit IV

Uploaded by

hmmailbox36
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 17

UNIT-IV

Processes Concepts:

A process is more than just a program. Especially in a multi-user, multi-tasking operating system
such as Linux there is much more to consider. Each program has a set of data that it uses to do
what it needs. Often, this data is not part of the program. For example, if you are using a text
editor, the file you are editing is not part of the program on disk, but is part of the process in
memory. If someone else were to be using the same editor, both of you would be using the same
program. However, each of you would have a different process in memory. See the figure below
to see how this looks graphically.

Kernel support for Process:


Image - Reading programs from the hard disk to create processes. (interactive)

Under Linux many different users can be on the system at the same time. In other words, they
have processes that are in memory all at the same time. The system needs to keep track of what
user is running what process, which terminal the process is running on, and what other resources
the process has (such as open files). All of this is part of the process.

With the exception of the init process (PID 1) every process is the child of another process.

Another example we see in the next figure. When you login, you normally have a single process,
which is your login shell(bash). If you start the X Windowing System, your shell starts another
process, xinit. At this point, both your shell and xinit are running, but the shell is waiting for xinit
to complete. Once X starts, you may want a terminal in which you can enter commands, so you
start xterm.
Process API

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.

exit() vs _exit():

The C library function exit() calls the kernel system call _exit() internally. The kernel system call
_exit() will cause the kernel to close descriptors, free memory, and perform the kernel
terminating process clean- up. The C library function exit() call will flush I/O buffers and
perform aditional clean-up before calling
_exit() internally. The function exit(status) causes the executable to return "status" as the return
code for main(). When exit(status) is called by a child process, it allows the parent process to
examine the terminating status of the child (if it terminates first). Without this call (or a call from
main() to return()) and specifying the status argument, the process will not return a value.

#include #include <unistd.h>


<stdlib.h>

void exit(int void _exit(int


status); status);
vfork():

The Vfork() function is the same as fork() except that it does not make a copy of the address
space. The memory is shared reducing the overhead of spawning a new process with a unique
copy of all the memory. This is typically used when using fork() to exec() a process and
terminate. The vfork() function also executes the child process first and resumes the parent
process when the child terminates.

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.

waitpid(): Options available to block calling process for a particular child process not the first
one.

Kill():

This is the real reason to set up a process group. One may kill all the processes in the
process group without having to keep track of how many processes have been forked and all of
their process id's.

execl() and execlp():

The function call "execl()" initiates a new program in the same environment in which it is
operating. An executable (with fully qualified path. i.e. /bin/ls) and arguments are passed to the
function. Note that "arg0" is the command/file name to execute.

int execl(const char *path, const char *arg0, const char *arg1, const char
*arg2, ... const char *argn, (char *) 0);

Where all function arguments are null terminated strings. The list of arguments is
terminated by NULL.

The routine execlp() will perform the same purpose except that it will use environment variable
PATH to determine which executable to process. Thus a fully qualified path name would not
have to be used. The first argument to the function could instead be "ls". The function execlp()
can also take the fully qualified name as it also resolves explicitly.
execv() and execvp():

This is the same as execl() except that the arguments are passed as null terminated array of pointers to
char. The first element "argv[0]" is the command name.

int execv(const char *path, char *const argv[]);

The routine execvp() will perform the same purpose except that it will use environment variable
PATH to determine which executable to process. Thus a fully qualified path name would not
have to be used. The first argument to the function could instead be "ls". The function execvp()
can also take the fully qualified name as it also resolves explicitly.

execve():

The function call "execve()" executes a process in an environment which it assigns.

Set the environment variables:

char *env[] = { "USER=user1", "PATH=/usr/bin:/bin:/opt/bin", (char *) 0 };

Zombie Process
On Linux operating systems, a zombie process or defunct process is a process that has completed
execution but still has an entry in the process table, allowing the process that started it to read its
exit status. In the term's colorful metaphor, the child process has died but has not yet
been reaped.

When a process ends, all of the memory and resources associated with it are deallocated so they
can be used by other processes. However, the process's entry in the process table remains. The
parent is sent a SIGCHLD signal indicating that a child has died; the handler for this signal will
typically execute the wait system call, which reads the exit status and removes the zombie. The
zombie's process ID and entry in the process table can then be reused. However, if a parent
ignores the SIGCHLD, the zombie will be left in the process table. In some situations this may
be desirable, for example if the parent creates another child process it ensures that it
will not be allocated the same process ID. A zombie process is not the
same as an orphan process. Orphan processes don't become zombie processes; instead, they
are adopted by init (process ID 1), which waits on its children.
The term zombie process derives from the common definition of zombie an undead
person. Zombies can be identified in the output from the Unix PS command by the presence of a
"Z" in the STAT
column. Zombies that exist for more than a short period of time typically indicate a bug in the
parent program.
To remove zombies from a system, the SIGCHLD signal can be sent to the parent manually,
using the kill command. If the parent process still refuses to reap the zombie, the next step would
be to remove the parent process. When a process loses its parent, init becomes its new parent.
Init periodically executes the wait system call to reap any zombies with init as parent.

Orphan Process

An orphan process is a computer process whose parent process has finished or terminated.

A process can become orphaned during remote invocation when the client process crashes after
making a request of the server.

Orphans waste server resources and can potentially leave a server in trouble. However there
are several solutions to the orphan process problem:

1. Extermination is the most commonly used technique; in this case the orphan process is killed.
2. Reincarnation is a technique in which machines periodically try to locate the parents of
any remote computations; at which point orphaned processes are killed.
3. Expiration is a technique where each process is allotted a certain amount of time to finish
before being killed. If need be a process may "ask" for more time to finish before the
allotted time expires.

A process can also be orphaned running on the same machine as its parent process. In a UNIX-
like operating system any orphaned process will be immediately adopted by the special "init"
system process. This operation is called re-parenting and occurs automatically. Even though
technically the process has the "init" process as its parent, it is still called an orphan process
since the process which originally created it no longer exists.

Inter Process Communication

Objective: how more than one process communicates with other processes sand calling
functions, kernel support

IPC:Message Queues:<sys/msg.h>

The basic idea of a message queue is a simple one.


Two (or more) processes can exchange information via access to a common system message
queue. The sending process places via some (OS) message-passing module a message onto a
queue which can be read by another process . Each message is given an identification or type so
that processes can select the appropriate message. Process must share a common key in order to
gain access to the queue in the first place (subject to other permissions -- see below).

Basic Message Passing IPC messaging lets processes send and receive messages, and queue
messages for processing in an arbitrary order. Unlike the file byte-stream data flow of pipes,
each IPC message has an explicit length. Messages can be assigned a specific type. Because of
this, a server process can direct message traffic between clients on its queue by using the client
process PID as the message type. For single-message transactions, multiple server processes can
work in parallel on transactions sent to a shared message queue.

Before a process can send or receive a message, the queue must be initialized (through the
msgget function see below) Operations to send and receive messages are performed by the
msgsnd() and msgrcv() functions, respectively.

When a message is sent, its text is copied to the message queue. The msgsnd() and msgrcv()
functions can be performed as either blocking or non-blocking operations. Non-blocking
operations allow for asynchronous message transfer -- the process is not suspended as a result of
sending or receiving a message. In blocking or synchronous message passing the sending process
cannot continue until the message has been transferred or has even been acknowledged by a
receiver. IPC signal and other mechanisms can be employed to implement such transfer. A
blocked message operation remains suspended until one of the following three conditions occurs:

 The call succeeds.


 The process receives a signal.
 The queue is removed.
Initialising the Message Queue

The msgget() function initializes a new message queue:

int msgget(key_t key, int msgflg)

It can also return the message queue ID (msqid) of the queue corresponding to the key argument.
The value passed as the msgflg argument must be an octal integer with settings for the queue's
permissions and control flags.

The following code illustrates the msgget()


function. #include <sys/ipc.h>;
#include <sys/msg.h>;
...
key_t key; /* key to be passed to msgget()
*/ int msgflg /* msgflg to be passed to
msgget() */ int msqid; /* return value from
msgget() */

...
key = ...
msgflg =
...

if ((msqid = msgget(key, msgflg)) == &ndash;1)


{
perror("msgget: msgget
failed"); exit(1);
} else
(void) fprintf(stderr, &ldquo;msgget succeeded");
...
IPC Functions, Key Arguments, and Creation Flags: <sys/ipc.h>

Processes requesting access to an IPC facility must be able to identify it. To do this, functions
that initialize or provide access to an IPC facility use a key_t key argument. (key_t is essentially
an int type defined in <sys/types.h>

The key is an arbitrary value or one that can be derived from a common seed at run time. One
way is with ftok() , which converts a filename to a key value that is unique within the system.
Functions that initialize or get access to messages (also semaphores or shared memory see later)
return an ID number of type int. IPC functions that perform read, write, and control operations
use this ID. If the key argument is specified as IPC_PRIVATE, the call initializes a new instance
of an IPC facility that is private to the creating process. When the IPC_CREAT flag is supplied
in the flags argument appropriate to the call, the function
tries to create the facility if it does not exist already. When called with both the IPC_CREAT and
IPC_EXCL flags, the function fails if the facility already exists. This can be useful when more
than one process might attempt to initialize the facility. One such case might involve several
server processes having access to the same facility. If they all attempt to create the facility with
IPC_EXCL in effect, only the first attempt succeeds. If neither of these flags is given and the
facility already exists, the functions to get access simply return the ID of the facility. If
IPC_CREAT is omitted and the facility is not already initialized, the calls fail. These control
flags are combined, using logical (bitwise) OR, with the octal permission modes to form the
flags argument. For example, the statement below initializes a new message queue if the queue
does not exist.

msqid = msgget(ftok("/tmp",

key), (IPC_CREAT | IPC_EXCL | 0400));

The first argument evaluates to a key based on the string ("/tmp"). The second argument
evaluates to the combined permissions and control flags.

Controlling message queues

The msgctl() function alters the permissions and other characteristics of a message queue. The
owner or creator of a queue can change its ownership or permissions using msgctl() Also, any
process with permission to do so can use msgctl() for control operations.

The msgctl() function is prototypes as follows:

int msgctl(int msqid, int cmd, struct msqid_ds *buf )

The msqid argument must be the ID of an existing message queue. The cmd argument is one of:

IPC_STAT

-- Place information about the status of the queue in the data structure pointed to by buf.
The process must have read permission for this call to succeed.

IPC_SET

-- Set the owner's user and group ID, the permissions, and the size (in number of bytes) of
the message queue. A process must have the effective user ID of the owner, creator, or
superuser for this call to succeed.

IPC_RMID

-- Remove the message queue specified by the msqid argument


The following code illustrates the msgctl() function with all its various flags:
#include<sys/types.h>

#include
<sys/ipc.h>
#include
<sys/msg.h>
...
if (msgctl(msqid, IPC_STAT, &buf) == -
1) { perror("msgctl: msgctl failed");
exit(1);
}
...
if (msgctl(msqid, IPC_SET, &buf) == -
1) { perror("msgctl: msgctl failed");
exit(1);
}
...

Sending and Receiving Messages

The msgsnd() and msgrcv() functions send and receive messages, respectively:

int msgsnd(int msqid, const void *msgp, size_t

msgsz, int msgflg);

int msgrcv(int msqid, void *msgp, size_t msgsz, long

msgtyp, int msgflg);

The msqid argument must be the ID of an existing message queue. The msgp argument is a
pointer to a structure that contains the type of the message and its text. The structure below is an
example of what this user-defined buffer might look like:

struct mymsg {

long mtype; /* message type */

char mtext[MSGSZ]; /* message text of length MSGSZ */

The msgsz argument specifies the length of the message in bytes.


The structure member msgtype is the received message's type as specified by the sending

process. The argument msgflg specifies the action to be taken if one or more of the

following are true:

 The number of bytes already on the queue is equal to msg_qbytes.


 The total number of messages on all queues system-wide is equal to the system-imposed limit.

These actions are as follows:

 If (msgflg & IPC_NOWAIT) is non-zero, the message will not be sent and the calling
process will return immediately.
 If (msgflg & IPC_NOWAIT) is 0, the calling process will suspend execution until
one of the following occurs:
o The condition responsible for the suspension no longer exists, in which case the
message is sent.
o The message queue identifier msqid is removed from the system; when this
occurs, errno is set equal to EIDRM and -1 is returned.
o The calling process receives a signal that is to be caught; in this case the
message is not sent and the calling process resumes execution.

Upon successful completion, the following actions are taken with respect to the data
structure associated with msqid:

o msg_qnum is incremented by 1.
o msg_lspid is set equal to the process ID of the calling process.
o msg_stime is set equal to the current time.

The following code illustrates msgsnd() and

msgrcv(): #include <sys/types.h>

#include

<sys/ipc.h>

#include

<sys/msg.h>

...
int msgflg; /* message flags for the operation */

struct msgbuf *msgp; /* pointer to the message

buffer */ int msgsz; /* message size */

long msgtyp; /* desired message type */

int msqid /* message queue ID to be used */

msgp = (struct msgbuf *)malloc((unsigned)(sizeof(struct msgbuf)

- sizeof msgp->mtext + maxmsgsz));

if (msgp == NULL) {

(void) fprintf(stderr, "msgop: %s %d byte

messages.\n", "could not allocate message buffer

for", maxmsgsz); exit(1);

msgsz

= ...

msgflg =

...

if (msgsnd(msqid, msgp, msgsz, msgflg)

== -1) perror("msgop: msgsnd failed");

...

msgsz = ...

msgtyp =

first_on_queue; msgflg

= ...

if (rtrn = msgrcv(msqid, msgp, msgsz, msgtyp, msgflg)


== -1) perror("msgop: msgrcv failed");

message_send.c -- creating and sending to a simple message queue

The full code listing for message_send.c is as follows:

#include <sys/types.h>

#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>

#define MSGSZ 128

/*
* Declare the message structure.
*/

typedef struct msgbuf


{ long mtype;
char mtext[MSGSZ];
} message_buf;

main()
{
int msqid;
int msgflg = IPC_CREAT |
0666; key_t key;
message_buf sbuf;
size_t buf_length;

/*
* Get the message queue id for the
* "name" 1234, which was created by
* the server.
*/
key = 1234;

(void) fprintf(stderr, "\nmsgget: Calling msgget(%#lx,\


%#o)\n",
key, msgflg);

if ((msqid = msgget(key, msgflg )) < 0) {


perror("msgget");
exit(1);
}
else
(void) fprintf(stderr,"msgget: msgget succeeded: msqid = %d\n", msqid);

/*
* We'll send message type 1

*/

sbuf.mtype = 1;

(void) fprintf(stderr,"msgget: msgget succeeded: msqid = %d\n", msqid);

(void) strcpy(sbuf.mtext, "Did you get this?");

(void) fprintf(stderr,"msgget: msgget succeeded: msqid = %d\n", msqid);

buf_length = strlen(sbuf.mtext) + 1 ;

/*
* Send a message.
*/
if (msgsnd(msqid, &sbuf, buf_length, IPC_NOWAIT) < 0) {
printf ("%d, %d, %s, %d\n", msqid, sbuf.mtype, sbuf.mtext, buf_length);
perror("msgsnd");
exit(1);
}

else
printf("Message: \"%s\" Sent\n", sbuf.mtext);

exit(0);
}

The essential points to note here are:

 The Message queue is created with a basic key and message flag msgflg = IPC_CREAT | 0666
-- create queue and make it read and appendable by all.
 A message of type (sbuf.mtype) 1 is sent to the queue with the message ``Did you get this?''
message_rec.c -- receiving the above message

The full code listing for message_send.c's companion process, message_rec.c is as follows:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>

#define MSGSZ 128


/*
* Declare the message structure.
*/

typedef struct msgbuf


{ long mtype;
char mtext[MSGSZ];
} message_buf;

main()
{
int msqid;
key_t key;
message_buf rbuf;

/*
* Get the message queue id for the
* "name" 1234, which was created by
* the server.
*/
key = 1234;

if ((msqid = msgget(key, 0666)) < 0)


{ perror("msgget");
exit(1);
}

/*
* Receive an answer of message type 1.
*/
if (msgrcv(msqid, &rbuf, MSGSZ, 1, 0) < 0) {
perror("msgrcv");
exit(1);
}
/*
* Print the answer.
*/
printf("%s\n", rbuf.mtext);
exit(0);
}

The essential points to note here are:

 The Message queue is opened with msgget (message flag 0666) and the same key as
message_send.c.
 A message of the same type 1 is received from the queue with the message ``Did you
get this?'' stored in r

You might also like