Linux Program
Linux Program
Sven Goldt
Sven van der Meer
Scott Burkett
Matt Welsh
Version 0.4
March 1995
0
...Our continuing mission: to seek out knowledge of C, to explore strange unix
commands, and to boldly code where no one has man page 4.
2
Contents
4 System calls 13
7 Sound Programming 69
7.1 Programming the internal speaker . . . . . . . . . . . . . . . . . . . . . . 69
7.2 Programming a sound card . . . . . . . . . . . . . . . . . . . . . . . . . . 69
3
4 CONTENTS
12 Abbreviations 131
• Copyright
The Linux Programmer’s Guide is
c 1994, 1995 by Sven Goldt
Sven Goldt, Sachsendamm 47b, 10829 Berlin, Germany
< [email protected] − berlin.de > .
Chapter 8 is
c 1994, 1995 by Sven van der Meer < [email protected] − berlin.de > .
Chapter 6 is
c 1995 Scott Burkett < scottb@IntN et.net > .
Chapter 10 is
c 1994, 1995 Matt Welsh < [email protected] > .
Special thanks goes to John D. Harper < [email protected] > for proofreading
this guide.
Permission to reproduce this document in whole or in part is subject to the following
conditions:
1. The copyright notice remains intact and is included.
2. If you make money with it the authors want a share.
3. The authors are not responsible for any harm that might arise by the use of it.
• Preface
This guide is far from being complete.
The first release started at version 0.1 in September 1994. It concentrated on system
calls because of lack of manpower and information. Planned are the description of
library functions and major kernel changes as well as excursions into important areas
like networking, sound, graphics and asynchronous I/O. Maybe some hints about
how to build shared libraries and pointers to useful toolkits will later be included.
This guide will only be a success with generous help in the form of information or
perhaps even submission of whole chapters.
• Introduction
Once upon a time I installed Linux on my PC to learn more about system administra-
tion. I tried to install a slip server but it didn’t work with shadow and mgetty. I had
to patch sliplogin and it worked until the new Linux 1.1 releases. No one could tell
me what had happened. There was no documentation about changes since the 0.99
kernel except the kernel change summaries from Russ Nelson, but they didn’t help
me very much in solving problems.
The Linux Programmer’s Guide is meant to do what the name implies— It is to help
Linux programmers understand the peculiarities of Linux. By its nature, this also
means that it should be useful when porting programs from other operating systems
to Linux. Therefore, this guide must describe the system calls and the major kernel
changes which have effects on older programs like serial I/O and networking.
6
Chapter 1
In March 1991 Linus Benedict Torvalds bought the multitasking system Minix for his AT
386. He used it to develop his own multitasking system which he called Linux. In Septem-
ber 1991 he released the first prototype by e-mail to some other Minix users on the internet,
thus beginning the Linux project. Many programmers from that point on have supported
Linux. They have added device drivers, developed applications, and aimed for POSIX
compliance. Today Linux is very powerful, but what is best is that it’s free. Work is beeing
done to port Linux to other platforms.
7
8 CHAPTER 1. THE LINUX OPERATING SYSTEM
Chapter 2
The base of Linux is the kernel. You could replace each and every library, but as long
as the Linux kernel remained, it would still be Linux. The kernel contains device drivers,
memory management, process management and communication management. The kernel
hacker gurus follow POSIX guidelines which sometimes makes programming easier and
sometimes harder. If your program behaves differently on a new Linux kernel release,
chances are that a new POSIX guideline has been implemented. For programming infor-
mation about the Linux kernel, read the Linux Kernel Hacker’s Guide.
9
10 CHAPTER 2. THE LINUX KERNEL
Chapter 3
11
12 CHAPTER 3. THE LINUX LIBC PACKAGE
Chapter 4
System calls
#include <syscall.h>
On the i386 architecture, system calls are limited to 5 arguments besides the system
call number because of the number of hardware registers. If you use Linux on another
architecture you can check < asm/unistd.h > for the syscall macros to see how many
arguments your hardware supports or how many the developers chose to support. These
syscall macros can be used instead of syscall(), but this is not recommended since such
a macro expands to a full function which might already exist in a library. Therefore, only
kernel hackers should play with the syscall macros. To demonstrate, here is the close()
example using a syscall macro.
#include <linux/unistd.h>
The syscall1 macro expands revealing the close() function. Thus we have close()
twice–once in libc and once in our program. The return value of syscall() or a syscall
macro is -1 if the system call failed and 0 or greater on success. Take a look at the global
variable errno to see what happened if a system call failed.
The following system calls that are available on BSD and SYS V are not available on
Linux:
audit(), auditon(), auditsvc(), fchroot(), getauid(), getdents(), getmsg(), mincore(), poll(),
putmsg(), setaudit(), setauid().
Sven Goldt The Linux Programmer’s Guide
13
14 CHAPTER 4. SYSTEM CALLS
Chapter 5
ioctl stands for input/output control and is used to manipulate a character device via a
filedescriptor. The format of ioctl is
ioctl(unsigned int fd, unsigned int request, unsigned long argument).
The return value is -1 if an error occured and a value greater or equal than 0 if the request
succeeded just like other system calls. The kernel distinguishes special and regular files.
Special files are mainly found in /dev and /proc. They differ from regular files in that way
that they hide an interface to a driver and not to a real (regular) file that contains text or
binary data. This is the UNIX philosophy and allows to use normal read/write operations
on every file. But if you need to do more with a special file or a regular file you can do it
with ... yes, ioctl. You more often need ioctl for special files than for regular files, but it’s
possible to use ioctl on regular files as well.
15
16 CHAPTER 5. THE “SWISS ARMY KNIFE” IOCTL
Chapter 6
Linux Interprocess
Communications
6.1 Introduction
The Linux IPC (Inter-process communication) facilities provide a method for multiple pro-
cesses to communicate with one another. There are several methods of IPC available to
Linux C programmers:
These facilities, when used effectively, provide a solid framework for client/server de-
velopment on any UNIX system (including Linux).
ls | sort | lp
17
18 CHAPTER 6. LINUX INTERPROCESS COMMUNICATIONS
The above sets up a pipeline, taking the output of ls as the input of sort, and the output
of sort as the input of lp. The data is running through a half duplex pipe, traveling (visually)
left to right through the pipeline.
Although most of us use pipes quite religiously in shell script programming, we often
do so without giving a second thought to what transpires at the kernel level.
When a process creates a pipe, the kernel sets up two file descriptors for use by the
pipe. One descriptor is used to allow a path of input into the pipe (write), while the other
is used to obtain data from the pipe (read). At this point, the pipe is of little practical use,
as the creating process can only use the pipe to communicate with itself. Consider this
representation of a process and the kernel after a pipe has been created:
From the above diagram, it is easy to see how the descriptors are connected together. If
the process sends data through the pipe (fd0), it has the ability to obtain (read) that infor-
mation from fd1. However, there is a much larger objective of the simplistic sketch above.
While a pipe initially connects a process to itself, data traveling through the pipe moves
through the kernel. Under Linux, in particular, pipes are actually represented internally
with a valid inode. Of course, this inode resides within the kernel itself, and not within the
bounds of any physical file system. This particular point will open up some pretty handy
I/O doors for us, as we will see a bit later on.
At this point, the pipe is fairly useless. After all, why go to the trouble of creating a
pipe if we are only going to talk to ourself? At this point, the creating process typically
forks a child process. Since a child process will inherit any open file descriptors from the
parent, we now have the basis for multiprocess communication (between parent and child).
Consider this updated version of our simple sketch:
Above, we see that both processes now have access to the file descriptors which consti-
tute the pipeline. It is at this stage, that a critical decision must be made. In which direction
do we desire data to travel? Does the child process send information to the parent, or vice-
versa? The two processes mutually agree on this issue, and proceed to “close” the end
of the pipe that they are not concerned with. For discussion purposes, let’s say the child
performs some processing, and sends information back through the pipe to the parent. Our
newly revised sketch would appear as such:
6.2. HALF-DUPLEX UNIX PIPES 19
Construction of the pipeline is now complete! The only thing left to do is make use of
the pipe. To access a pipe directly, the same system calls that are used for low-level file I/O
can be used (recall that pipes are actually represented internally as a valid inode).
To send data to the pipe, we use the write() system call, and to retrieve data from the
pipe, we use the read() system call. Remember, low-level file I/O system calls work with
file descriptors! However, keep in mind that certain system calls, such as lseek(), do not
work with descriptors to pipes.
The first integer in the array (element 0) is set up and opened for reading, while the
second integer (element 1) is set up and opened for writing. Visually speaking, the output
of fd1 becomes the input for fd0. Once again, all data traveling through the pipe moves
through the kernel.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
main()
{
int fd[2];
pipe(fd);
.
.
}
20 CHAPTER 6. LINUX INTERPROCESS COMMUNICATIONS
Remember that an array name in C decays into a pointer to its first member. Above,
fd is equivalent to &fd[0]. Once we have established the pipeline, we then fork our new
child process:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
main()
{
int fd[2];
pid_t childpid;
pipe(fd);
If the parent wants to receive data from the child, it should close fd1, and the child
should close fd0. If the parent wants to send data to the child, it should close fd0, and
the child should close fd1. Since descriptors are shared between the parent and child, we
should always be sure to close the end of pipe we aren’t concerned with. On a technical
note, the EOF will never be returned if the unnecessary ends of the pipe are not explicitly
closed.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
main()
{
int fd[2];
pid_t childpid;
pipe(fd);
if(childpid == 0)
{
/* Child process closes up input side of pipe */
close(fd[0]);
}
else
{
6.2. HALF-DUPLEX UNIX PIPES 21
As mentioned previously, once the pipeline has been established, the file descriptors
may be treated like descriptors to normal files.
/*****************************************************************************
Excerpt from "Linux Programmer’s Guide - Chapter 6"
(C)opyright 1994-1995, Scott Burkett
*****************************************************************************
MODULE: pipe.c
*****************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(void)
{
int fd[2], nbytes;
pid_t childpid;
char string[] = "Hello, world!\n";
char readbuffer[80];
pipe(fd);
if(childpid == 0)
{
/* Child process closes up input side of pipe */
close(fd[0]);
return(0);
}
Often, the descriptors in the child are duplicated onto standard input or output. The
child can then exec() another program, which inherits the standard streams. Let’s look at
the dup() system call:
NOTES: the old descriptor is not closed! Both may be used interchangeably
Although the old descriptor and the newly created descriptor can be used interchange-
ably, we will typically close one of the standard streams first. The dup() system call uses
the lowest-numbered, unused descriptor for the new one.
Consider:
.
.
childpid = fork();
if(childpid == 0)
{
/* Close up standard input of the child */
close(0);
Since file descriptor 0 (stdin) was closed, the call to dup() duplicated the input descrip-
tor of the pipe (fd0) onto its standard input. We then make a call to execlp(), to overlay
the child’s text segment (code) with that of the sort program. Since newly exec’d programs
inherit standard streams from their spawners, it actually inherits the input side of the pipe
as its standard input! Now, anything that the original parent process sends to the pipe, goes
into the sort facility.
There is another system call, dup2(), which can be used as well. This particular call
originated with Version 7 of UNIX, and was carried on through the BSD releases and is
now required by the POSIX standard.
if(childpid == 0)
{
/* Close stdin, duplicate the input side of pipe to stdin */
dup2(0, fd[0]);
execlp("sort", "sort", NULL);
.
.
}
NOTES: waits on the pipe process to terminate, then closes the stream.
The pclose() function performs a wait4() on the process forked by popen(). When it
returns, it destroys the pipe and the file stream. Once again, it is synonymous with the
fclose() function for normal stream-based file I/O.
Consider this example, which opens up a pipe to the sort command, and proceeds to
sort an array of strings:
/*****************************************************************************
Excerpt from "Linux Programmer’s Guide - Chapter 6"
(C)opyright 1994-1995, Scott Burkett
*****************************************************************************
MODULE: popen1.c
*****************************************************************************/
#include <stdio.h>
#define MAXSTRS 5
int main(void)
{
int cntr;
FILE *pipe_fp;
char *strings[MAXSTRS] = { "echo", "bravo", "alpha",
"charlie", "delta"};
/* Processing loop */
for(cntr=0; cntr<MAXSTRS; cntr++) {
fputs(strings[cntr], pipe_fp);
fputc(’\n’, pipe_fp);
}
return(0);
}
Since popen() uses the shell to do its bidding, all shell expansion characters and
metacharacters are available for use! In addition, more advanced techniques such as redi-
6.2. HALF-DUPLEX UNIX PIPES 25
rection, and even output piping, can be utilized with popen(). Consider the following
sample calls:
As another example of popen(), consider this small program, which opens up two pipes
(one to the ls command, the other to sort):
/*****************************************************************************
Excerpt from "Linux Programmer’s Guide - Chapter 6"
(C)opyright 1994-1995, Scott Burkett
*****************************************************************************
MODULE: popen2.c
*****************************************************************************/
#include <stdio.h>
int main(void)
{
FILE *pipein_fp, *pipeout_fp;
char readbuf[80];
/* Processing loop */
while(fgets(readbuf, 80, pipein_fp))
fputs(readbuf, pipeout_fp);
return(0);
}
For our final demonstration of popen(), let’s create a generic program that opens up a
pipeline between a passed command and filename:
/*****************************************************************************
26 CHAPTER 6. LINUX INTERPROCESS COMMUNICATIONS
#include <stdio.h>
if( argc != 3) {
fprintf(stderr, "USAGE: popen3 [command] [filename]\n");
exit(1);
}
/* Processing loop */
do {
fgets(readbuf, 80, infile);
if(feof(infile)) break;
fputs(readbuf, pipe_fp);
} while(!feof(infile));
fclose(infile);
pclose(pipe_fp);
return(0);
}
Up to 512 bytes can be written or retrieved from a pipe atomically. Anything that
crosses this threshold will be split, and not atomic. Under Linux, however, the atomic
operational limit is defined in “linux/limits.h” as:
As you can see, Linux accommodates the minimum number of bytes required by
POSIX, quite considerably I might add. The atomicity of a pipe operation becomes im-
portant when more than one process is involved (FIFOS). For example, if the number of
bytes written to a pipe exceeds the atomic limit for a single operation, and multiple pro-
cesses are writing to the pipe, the data will be “interleaved” or “chunked”. In other words,
one process may insert data into the pipeline between the writes of another.
• The pipe() call must be made BEFORE a call to fork(), or the descriptors will not be
inherited by the child! (same for popen()).
• With half-duplex pipes, any connected processes must share a related ancestry. Since
the pipe resides within the confines of the kernel, any process that is not in the ances-
try for the creator of the pipe has no way of addressing it. This is not the case with
named pipes (FIFOS).
• When all I/O is done by sharing processes, the named pipe remains in the file system
for later use.
mknod MYFIFO p
mkfifo a=rw MYFIFO
28 CHAPTER 6. LINUX INTERPROCESS COMMUNICATIONS
The above two commands perform identical operations, with one exception. The mk-
fifo command provides a hook for altering the permissions on the FIFO file directly after
creation. With mknod, a quick call to the chmod command will be necessary.
FIFO files can be quickly identified in a physical file system by the “p” indicator seen
here in a long directory listing:
$ ls -l MYFIFO
prw-r--r-- 1 root root 0 Dec 14 22:15 MYFIFO|
Also notice the vertical bar (“pipe sign”) located directly after the file name. Another
great reason to run Linux, eh?
To create a FIFO in C, we can make use of the mknod() system call:
I will leave a more detailed discussion of mknod() to the man page, but let’s consider a
simple example of FIFO creation from C:
In this case, the file “/tmp/MYFIFO” is created as a FIFO file. The requested permis-
sions are “0666”, although they are affected by the umask setting as follows:
A common trick is to use the umask() system call to temporarily zap the umask value:
umask(0);
mknod("/tmp/MYFIFO", S_IFIFO|0666, 0);
In addition, the third argument to mknod() is ignored unless we are creating a device
file. In that instance, it should specify the major and minor numbers of the device file.
/*****************************************************************************
Excerpt from "Linux Programmer’s Guide - Chapter 6"
(C)opyright 1994-1995, Scott Burkett
*****************************************************************************
MODULE: fifoserver.c
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/stat.h>
int main(void)
{
FILE *fp;
char readbuf[80];
while(1)
{
fp = fopen(FIFO_FILE, "r");
fgets(readbuf, 80, fp);
printf("Received string: %s\n", readbuf);
fclose(fp);
}
return(0);
}
Since a FIFO blocks by default, run the server in the background after you compile it:
$ fifoserver&
We will discuss a FIFO’s blocking action in a moment. First, consider the following
simple client frontend to our server:
/*****************************************************************************
Excerpt from "Linux Programmer’s Guide - Chapter 6"
(C)opyright 1994-1995, Scott Burkett
*****************************************************************************
MODULE: fifoclient.c
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
if ( argc != 2 ) {
printf("USAGE: fifoclient [string]\n");
exit(1);
}
fputs(argv[1], fp);
fclose(fp);
return(0);
}
IPC Identifiers
Each IPC object has a unique IPC identifier associated with it. When we say “IPC object”,
we are speaking of a single message queue, semaphore set, or shared memory segment.
6.4. SYSTEM V IPC 31
This identifier is used within the kernel to uniquely identify an IPC object. For example, to
access a particular shared memory segment, the only item you need is the unique ID value
which has been assigned to that segment.
The uniqueness of an identifier is relevant to the type of object in question. To illustrate
this, assume a numeric identifier of “12345”. While there can never be two message queues
with this same identifier, there exists the distinct possibility of a message queue and, say, a
shared memory segment, which have the same numeric identifier.
IPC Keys
To obtain a unique ID, a key must be used. The key must be mutually agreed upon by both
client and server processes. This represents the first step in constructing a client/server
framework for an application.
When you use a telephone to call someone, you must know their number. In addition,
the phone company must know how to relay your outgoing call to its final destination. Once
the other party responds by answering the telephone call, the connection is made.
In the case of System V IPC facilities, the “telephone” correllates directly with the type
of object being used. The “phone company”, or routing method, can be directly associated
with an IPC key.
The key can be the same value every time, by hardcoding a key value into an applica-
tion. This has the disadvantage of the key possibly being in use already. Often, the ftok()
function is used to generate key values for both the client and the server.
The returned key value from ftok() is generated by combining the inode number and
minor device number from the file in argument one, with the one character project inden-
tifier in the second argument. This doesn’t guarantee uniqueness, but an application can
check for collisions and retry the key generation.
key_t mykey;
mykey = ftok("/tmp/myapp", ’a’);
In the above snippet, the directory /tmp/myapp is combined with the one letter iden-
tifier of ’a’. Another common example is to use the current directory:
key_t mykey;
mykey = ftok(".", ’a’);
The key generation algorithm used is completely up to the discretion of the application
programmer. As long as measures are in place to prevent race conditions, deadlocks, etc,
any method is viable. For our demonstration purposes, we will use the ftok() approach. If
we assume that each client process will be running from a unique “home” directory, the
keys generated should suffice for our needs.
The key value, however it is obtained, is used in subsequent IPC system calls to create
or gain access to IPC objects.
32 CHAPTER 6. LINUX INTERPROCESS COMMUNICATIONS
By default, all three categories of objects are shown. Consider the following sample
output of ipcs:
Here we see a single message queue which has an identifier of “0”. It is owned by the
user root, and has octal permissions of 660, or -rw-rw---. There is one message in the
queue, and that message has a total size of 5 bytes.
The ipcs command is a very powerful tool which provides a peek into the kernel’s
storage mechanisms for IPC objects. Learn it, use it, revere it.
Simply specify whether the object to be deleted is a message queue (msg), a semaphore
set (sem), or a shared memory segment (shm). The IPC ID can be obtained by the ipcs
command. You have to specify the type of object, since identifiers are unique among the
same type (recall our discussion of this earlier).
Message buffer The first structure we’ll visit is the msgbuf structure. This particular
data structure can be thought of as a template for message data. While it is up to the
programmer to define structures of this type, it is imperative that you understand that there
is actually a structure of type msgbuf. It is declared in linux/msg.h as follows:
mtype
The message type, represented in a positive number. This must be a positive number!
mtext
The message data itself.
The ability to assign a given message a type, essentially gives you the capability to
multiplex messages on a single queue. For instance, client processes could be assigned a
magic number, which could be used as the message type for messages sent from a server
process. The server itself could use some other number, which clients could use to send
messages to it. In another scenario, an application could mark error messages as having a
message type of 1, request messages could be type 2, etc. The possibilities are endless.
On another note, do not be misled by the almost too-descriptive name assigned to the
message data element (mtext). This field is not restricted to holding only arrays of char-
acters, but any data, in any form. The field itself is actually completely arbitrary, since this
structure gets redefined by the application programmer. Consider this redefinition:
struct my_msgbuf {
long mtype; /* Message type */
long request_id; /* Request identifier */
struct client info; /* Client information structure */
};
Here we see the message type, as before, but the remainder of the structure has been
replaced by two other elements, one of which is another structure! This is the beauty of
message queues. The kernel makes no translations of data whatsoever. Any information
can be sent.
There does exist an internal limit, however, of the maximum size of a given message.
In Linux, this is defined in linux/msg.h as follows:
Messages can be no larger than 4,056 bytes in total size, including the mtype member,
which is 4 bytes in length (long).
34 CHAPTER 6. LINUX INTERPROCESS COMMUNICATIONS
Kernel msg structure The kernel stores each message in the queue within the framework
of the msg structure. It is defined for us in linux/msg.h as follows:
msg next
This is a pointer to the next message in the queue. They are stored as a singly linked
list within kernel addressing space.
msg type
This is the message type, as assigned in the user structure msgbuf.
msg spot
A pointer to the beginning of the message body.
msg ts
The length of the message text, or body.
Kernel msqid ds structure Each of the three types of IPC objects has an internal data
structure which is maintained by the kernel. For message queues, this is the msqid ds
structure. The kernel creates, stores, and maintains an instance of this structure for every
message queue created on the system. It is defined in linux/msg.h as follows:
While you will rarely have to concern yourself with most of the members of this struc-
ture, a brief description of each is in order to complete our tour:
msg perm
An instance of the ipc perm structure, which is defined for us in linux/ipc.h.
This holds the permission information for the message queue, including the access
permissions, and information about the creator of the queue (uid, etc).
6.4. SYSTEM V IPC 35
msg first
Link to the first message in the queue (the head of the list).
msg last
Link to the last message in the queue (the tail of the list).
msg stime
Timestamp (time t) of the last message that was sent to the queue.
msg rtime
Timestamp of the last message retrieved from the queue.
msg ctime
Timestamp of the last “change” made to the queue (more on this later).
wwait
and
rwait
Pointers into the kernel’s wait queue. They are used when an operation on a message
queue deems the process go into a sleep state (i.e. queue is full and the process is
waiting for an opening).
msg cbytes
Total number of bytes residing on the queue (sum of the sizes of all messages).
msg qnum
Number of messages currently in the queue.
msg qbytes
Maximum number of bytes on the queue.
msg lspid
The PID of the process who sent the last message.
msg lrpid
The PID of the process who retrieved the last message.
Kernel ipc perm structure The kernel stores permission information for IPC objects
in a structure of type ipc perm. For example, in the internal structure for a message
queue described above, the msg perm member is of this type. It is declared for us in
linux/ipc.h as follows:
struct ipc_perm
{
key_t key;
ushort uid; /* owner euid and egid */
ushort gid;
ushort cuid; /* creator euid and egid */
ushort cgid;
ushort mode; /* access modes see mode flags below */
ushort seq; /* slot usage sequence number */
};
36 CHAPTER 6. LINUX INTERPROCESS COMMUNICATIONS
All of the above are fairly self-explanatory. Stored along with the IPC key of the object
is information about both the creator and owner of the object (they may be different). The
octal access modes are also stored here, as an unsigned short. Finally, the slot usage
sequence number is stored at the end. Each time an IPC object is closed via a system call
(destroyed), this value gets incremented by the maximum number of IPC objects that can
reside in a system. Will you have to concern yourself with this value? No.
NOTE:There is an excellent discussion on this topic, and the security reasons as to its
existence and behavior, in Richard Stevens’ UNIX Network Programming book, pp. 125.
The first argument to msgget() is the key value (in our case returned by a call to
ftok()). This key value is then compared to existing key values that exist within the
kernel for other message queues. At that point, the open or access operation is dependent
upon the contents of the msgflg argument.
IPC CREAT
Create the queue if it doesn’t already exist in the kernel.
IPC EXCL
When used with IPC CREAT, fail if queue already exists.
If IPC CREAT is used alone, msgget() either returns the message queue identifier
for a newly created message queue, or returns the identifier for a queue which exists with
the same key value. If IPC EXCL is used along with IPC CREAT, then either a new queue
is created, or if the queue exists, the call fails with -1. IPC EXCL is useless by itself, but
when combined with IPC CREAT, it can be used as a facility to guarantee that no existing
queue is opened for access.
An optional octal mode may be OR’d into the mask, since each IPC object has permis-
sions that are similar in functionality to file permissions on a UNIX file system!
Let’s create a quick wrapper function for opening or creating message queue:
return(-1);
}
return(qid);
}
Note the use of the explicit permissions of 0660. This small function either returns a
message queue identifier (int), or -1 on error. The key value must be passed to it as its
only argument.
PROTOTYPE: int msgsnd ( int msqid, struct msgbuf *msgp, int msgsz, int msgflg )
RETURNS: 0 on success
-1 on error: errno = EAGAIN (queue is full, and IPC_NOWAIT was asser
EACCES (permission denied, no write permission)
EFAULT (msgp address isn’t accessable - invalid
EIDRM (The message queue has been removed)
EINTR (Received a signal while waiting to writ
EINVAL (Invalid message queue identifier, nonpo
message type, or invalid message size)
ENOMEM (Not enough memory to copy message buffe
NOTES:
The first argument to msgsnd is our queue identifier, returned by a previous call to
msgget. The second argument, msgp, is a pointer to our redeclared and loaded message
buffer. The msgsz argument contains the size of the message in bytes, excluding the length
of the message type (4 byte long).
The msgflg argument can be set to 0 (ignored), or:
IPC NOWAIT
If the message queue is full, then the message is not written to the queue, and con-
trol is returned to the calling process. If not specified, then the calling process will
suspend (block) until the message can be written.
Let’s create another wrapper function for sending messages:
return(result);
}
38 CHAPTER 6. LINUX INTERPROCESS COMMUNICATIONS
This small function attempts to send the message residing at the passed address (qbuf)
to the message queue designated by the passed queue identifier (qid). Here is a sample
code snippet utilizing the two wrapper functions we have developed so far:
#include <stdio.h>
#include <stdlib.h>
#include <linux/ipc.h>
#include <linux/msg.h>
main()
{
int qid;
key_t msgkey;
struct mymsgbuf {
long mtype; /* Message type */
int request; /* Work request number */
double salary; /* Employee’s salary */
} msg;
/* Bombs away! */
if((send_message( qid, &msg )) == -1) {
perror("send_message");
exit(1);
}
}
After creating/opening our message queue, we proceed to load up the message buffer
with test data (note the lack of character data to illustrate our point about sending binary
information). A quick call to send message merrily distributes our message out to the
message queue.
Now that we have a message on our queue, try the ipcs command to view the status
of your queue. Now let’s turn the discussion to actually retrieving the message from the
queue. To do this, you use the msgrcv() system call:
Obviously, the first argument is used to specify the queue to be used during the message
retrieval process (should have been returned by an earlier call to msgget). The second ar-
gument (msgp) represents the address of a message buffer variable to store the retrieved
message at. The third argument (msgsz) represents the size of the message buffer struc-
ture, excluding the length of the mtype member. Once again, this can easily be calculated
as:
The fourth argument (mtype) specifies the type of message to retrieve from the queue.
The kernel will search the queue for the oldest message having a matching type, and will
return a copy of it in the address pointed to by the msgp argument. One special case exists.
If the mtype argument is passed with a value of zero, then the oldest message on the queue
is returned, regardless of type.
If IPC NOWAIT is passed as a flag, and no messages are available, the call returns
ENOMSG to the calling process. Otherwise, the calling process blocks until a message
arrives in the queue that satisfies the msgrcv() parameters. If the queue is deleted while
a client is waiting on a message, EIDRM is returned. EINTR is returned if a signal is
caught while the process is in the middle of blocking, and waiting for a message to arrive.
Let’s examine a quick wrapper function for retrieving a message from our queue:
return(result);
}
After successfully retrieving a message from the queue, the message entry within the
queue is destroyed.
The MSG NOERROR bit in the msgflg argument provides some additional capabili-
ties. If the size of the physical message data is greater than msgsz, and MSG NOERROR
is asserted, then the message is truncated, and only msgsz bytes are returned. Normally,
the msgrcv() system call returns -1 (E2BIG), and the message will remain on the queue
for later retrieval. This behavior can used to create another wrapper function, which will
allow us to “peek” inside the queue, to see if a message has arrived that satisfies our request:
40 CHAPTER 6. LINUX INTERPROCESS COMMUNICATIONS
return(FALSE);
}
Above, you will notice the lack of a buffer address and a length. In this particular
case, we want the call to fail. However, we check for the return of E2BIG which indicates
that a message does exist which matches our requested type. The wrapper function returns
TRUE on success, FALSE otherwise. Also note the use of the IPC NOWAIT flag, which
prevents the blocking behavior described earlier.
Now, common sense dictates that direct manipulation of the internal kernel data struc-
tures could lead to some late night fun. Unfortunately, the resulting duties on the part of
the programmer could only be classified as fun if you like trashing the IPC subsystem. By
using msgctl() with a selective set of commands, you have the ability to manipulate
those items which are less likely to cause grief. Let’s look at these commands:
IPC STAT
Retrieves the msqid ds structure for a queue, and stores it in the address of the buf
argument.
IPC SET
Sets the value of the ipc perm member of the msqid ds structure for a queue. Takes
the values from the buf argument.
6.4. SYSTEM V IPC 41
IPC RMID
Removes the queue from the kernel.
Recall our discussion about the internal data structure for message queues (msqid ds).
The kernel maintains an instance of this structure for each queue which exists in the system.
By using the IPC STAT command, we can retrieve a copy of this structure for examination.
Let’s look at a quick wrapper function that will retrieve the internal structure and copy it
into a passed address:
return(0);
}
If we are unable to copy the internal buffer, -1 is returned to the calling function. If all
went well, a value of 0 (zero) is returned, and the passed buffer should contain a copy of
the internal data structure for the message queue represented by the passed queue identifier
(qid).
Now that we have a copy of the internal data structure for a queue, what attributes
can be manipulated, and how can we alter them? The only modifiable item in the data
structure is the ipc perm member. This contains the permissions for the queue, as well
as information about the owner and creator. However, the only members of the ipc perm
structure that are modifiable are mode, uid, and gid. You can change the owner’s user
id, the owner’s group id, and the access permissions for the queue.
Let’s create a wrapper function designed to change the mode of a queue. The mode
must be passed in as a character array (i.e. “660”).
return(0);
}
We retrieve a current copy of the internal data structure by a quick call to our
get queue ds wrapper function. We then make a call to sscanf() to alter the mode
member of the associated msg perm structure. No changes take place, however, until
42 CHAPTER 6. LINUX INTERPROCESS COMMUNICATIONS
the new copy is used to update the internal version. This duty is performed by a call to
msgctl() using the IPC SET command.
BE CAREFUL! It is possible to alter the permissions on a queue, and in doing so,
inadvertently lock yourself out! Remember, these IPC objects don’t go away unless they
are properly removed, or the system is rebooted. So, even if you can’t see a queue with
ipcs doesn’t mean that it isn’t there.
To illustrate this point, a somewhat humorous anecdote seems to be in
order. While teaching a class on UNIX internals at the University of South
Florida, I ran into a rather embarrassing stumbling block. I had dialed into
their lab server the night before, in order to compile and test the labwork to
be used in the week-long class. In the process of my testing, I realized that
I had made a typo in the code used to alter the permissions on a message
queue. I created a simple message queue, and tested the sending and receiving
capabilities with no incident. However, when I attempted to change the mode
of the queue from “660” to “600”, the resulting action was that I was locked
out of my own queue! As a result, I could not test the message queue labwork
in the same area of my source directory. Since I used the ftok() function to
create the IPC key, I was trying to access a queue that I did not have proper
permissions for. I ended up contacting the local system administrator on the
morning of the class, only to spend an hour explaining to him what a message
queue was, and why I needed him to run the ipcrm command for me. grrrr.
After successfully retrieving a message from a queue, the message is removed. How-
ever, as mentioned earlier, IPC objects remain in the system unless explicitly removed, or
the system is rebooted. Therefore, our message queue still exists within the kernel, avail-
able for use long after a single message disappears. To complete the life cycle of a message
queue, they should be removed with a call to msgctl(), using the IPC RMID command:
int remove_queue( int qid )
{
if( msgctl( qid, IPC_RMID, 0) == -1)
{
return(-1);
}
return(0);
}
This wrapper function returns 0 if the queue was removed without incident, else a value
of -1. The removal of the queue is atomic in nature, and any subsequent accesses to the
queue for whatever purpose will fail miserably.
Sending Messages
Retrieving Messages
msgtool r (type)
msgtool m (mode)
Deleting a Queue
msgtool d
Examples
msgtool s 1 test
msgtool s 5 test
msgtool s 1 "This is a test"
msgtool r 1
msgtool d
msgtool m 660
The Source The following is the source code for the msgtool facility. It should compile
clean on any recent (decent) kernel revision that supports System V IPC. Be sure to enable
System V IPC in your kernel when doing a rebuild!
On a side note, this utility will create a message queue if it does not exist, no matter
what type of action is requested.
NOTE: Since this tool uses the ftok() function to generate IPC key values,
you may encounter directory conflicts. If you change directories at any point
in your script, it probably won’t work. Another solution would be to hardcode
a more complete path into msgtool, such as “/tmp/msgtool”, or possibly even
allow the path to be passed on the command line, along with the operational
arguments.
/*****************************************************************************
Excerpt from "Linux Programmer’s Guide - Chapter 6"
(C)opyright 1994-1995, Scott Burkett
*****************************************************************************
MODULE: msgtool.c
*****************************************************************************
44 CHAPTER 6. LINUX INTERPROCESS COMMUNICATIONS
A command line tool for tinkering with SysV style Message Queues
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MAX_SEND_SIZE 80
struct mymsgbuf {
long mtype;
char mtext[MAX_SEND_SIZE];
};
void send_message(int qid, struct mymsgbuf *qbuf, long type, char *text);
void read_message(int qid, struct mymsgbuf *qbuf, long type);
void remove_queue(int qid);
void change_queue_mode(int qid, char *mode);
void usage(void);
if(argc == 1)
usage();
switch(tolower(argv[1][0]))
{
case ’s’: send_message(msgqueue_id, (struct mymsgbuf *)&qbuf,
atol(argv[2]), argv[3]);
break;
case ’r’: read_message(msgqueue_id, &qbuf, atol(argv[2]));
break;
case ’d’: remove_queue(msgqueue_id);
break;
case ’m’: change_queue_mode(msgqueue_id, argv[2]);
break;
6.4. SYSTEM V IPC 45
default: usage();
return(0);
}
void send_message(int qid, struct mymsgbuf *qbuf, long type, char *text)
{
/* Send a message to the queue */
printf("Sending a message ...\n");
qbuf->mtype = type;
strcpy(qbuf->mtext, text);
void usage(void)
{
46 CHAPTER 6. LINUX INTERPROCESS COMMUNICATIONS
6.4.3 Semaphores
Basic Concepts
Semaphores can best be described as counters used to control access to shared resources by
multiple processes. They are most often used as a locking mechanism to prevent processes
from accessing a particular resource while another process is performing operations on it.
Semaphores are often dubbed the most difficult to grasp of the three types of System V
IPC objects. In order to fully understand semaphores, we’ll discuss them briefly before
engaging any system calls and operational theory.
The name semaphore is actually an old railroad term, referring to the crossroad “arms”
that prevent cars from crossing the tracks at intersections. The same can be said about a
simple semaphore set. If the semaphore is on (the arms are up), then a resource is available
(cars may cross the tracks). However, if the semaphore is off (the arms are down), then
resources are not available (the cars must wait).
While this simple example may stand to introduce the concept, it is important to realize
that semaphores are actually implemented as sets, rather than as single entities. Of course,
a given semaphore set might only have one semaphore, as in our railroad example.
Perhaps another approach to the concept of semaphores, is to think of them as resource
counters. Let’s apply this concept to another real world scenario. Consider a print spooler,
capable of handling multiple printers, with each printer handling multiple print requests.
A hypothetical print spool manager will utilize semaphore sets to monitor access to each
printer.
Assume that in our corporate print room, we have 5 printers online. Our print spool
manager allocates a semaphore set with 5 semaphores in it, one for each printer on the
system. Since each printer is only physically capable of printing one job at a time, each of
our five semaphores in our set will be initialized to a value of 1 (one), meaning that they
are all online, and accepting requests.
John sends a print request to the spooler. The print manager looks at the semaphore set,
and finds the first semaphore which has a value of one. Before sending John’s request to the
physical device, the print manager decrements the semaphore for the corresponding printer
by a value of negative one (-1). Now, that semaphore’s value is zero. In the world of System
V semaphores, a value of zero represents 100% resource utilization on that semaphore. In
our example, no other request can be sent to that printer until it is no longer equal to zero.
When John’s print job has completed, the print manager increments the value of the
semaphore which corresponds to the printer. Its value is now back up to one (1), which
means it is available again. Naturally, if all 5 semaphores had a value of zero, that would
indicate that they are all busy printing requests, and that no printers are available.
Although this was a simple example, please do not be confused by the initial value of
one (1) which was assigned to each semaphore in the set. Semaphores, when thought of as
resource counters, may be initialized to any positive integer value, and are not limited to
either being zero or one. If it were possible for each of our five printers to handle 10 print
jobs at a time, we could initialize each of our semaphores to 10, decrementing by one for
every new job, and incrementing by one whenever a print job was finished. As you will
discover in the next chapter, semaphores have a close working relationship with shared
6.4. SYSTEM V IPC 47
memory segments, acting as a watchdog to prevent multiple writes to the same memory
segment.
Before delving into the associated system calls, lets take a brief tour through the various
internal data structures utilized during semaphore operations.
Let’s briefly look at data structures maintained by the kernel for semaphore sets.
Kernel semid ds structure As with message queues, the kernel maintains a special
internal data structure for each semaphore set which exists within its addressing space.
This structure is of type semid ds, and is defined in linux/sem.h as follows:
/* One semid data structure for each set of semaphores in the system. */
struct semid_ds {
struct ipc_perm sem_perm; /* permissions .. see ipc.h */
time_t sem_otime; /* last semop time */
time_t sem_ctime; /* last change time */
struct sem *sem_base; /* ptr to first semaphore in arra
struct wait_queue *eventn;
struct wait_queue *eventz;
struct sem_undo *undo; /* undo requests on this array */
ushort sem_nsems; /* no. of semaphores in array */
};
As with message queues, operations on this structure are performed by a special system
call, and should not be tinkered with directly. Here are descriptions of the more pertinent
fields:
sem perm
This is an instance of the ipc perm structure, which is defined for us in
linux/ipc.h. This holds the permission information for the semaphore set, in-
cluding the access permissions, and information about the creator of the set (uid,
etc).
sem otime
Time of the last semop() operation (more on this in a moment)
sem ctime
Time of the last change to this structure (mode change, etc)
sem base
Pointer to the first semaphore in the array (see next structure)
sem undo
Number of undo requests in this array (more on this in a moment)
sem nsems
Number of semaphores in the semaphore set (the array)
48 CHAPTER 6. LINUX INTERPROCESS COMMUNICATIONS
Kernel sem structure In the semid ds structure, there exists a pointer to the base of
the semaphore array itself. Each array member is of the sem structure type. It is also
defined in linux/sem.h:
sem pid
The PID (process ID) that performed the last operation
sem semval
The current value of the semaphore
sem semncnt
Number of processes waiting for resources to become available
sem semzcnt
Number of processes waiting for 100% resource utilization
The first argument to semget() is the key value (in our case returned by a call to
ftok()). This key value is then compared to existing key values that exist within the
kernel for other semaphore sets. At that point, the open or access operation is dependent
upon the contents of the semflg argument.
IPC CREAT
Create the semaphore set if it doesn’t already exist in the kernel.
IPC EXCL
When used with IPC CREAT, fail if semaphore set already exists.
6.4. SYSTEM V IPC 49
If IPC CREAT is used alone, semget() either returns the semaphore set identifier
for a newly created set, or returns the identifier for a set which exists with the same key
value. If IPC EXCL is used along with IPC CREAT, then either a new set is created, or
if the set exists, the call fails with -1. IPC EXCL is useless by itself, but when combined
with IPC CREAT, it can be used as a facility to guarantee that no existing semaphore set
is opened for access.
As with the other forms of System V IPC, an optional octal mode may be OR’d into the
mask to form the permissions on the semaphore set.
The nsems argument specifies the number of semaphores that should be created in a
new set. This represents the number of printers in our fictional print room described earlier.
The maximum number of semaphores in a set is defined in “linux/sem.h” as:
Note that the nsems argument is ignored if you are explicitly opening an existing set.
Let’s create a wrapper function for opening or creating semaphore sets:
if ( ! numsems )
return(-1);
return(sid);
}
Note the use of the explicit permissions of 0660. This small function either returns a
semaphore set identifier (int), or -1 on error. The key value must be passed to it, as well
as the number of semaphores to allocate space for if creating a new set. In the example
presented at the end of this section, notice the use of the IPC EXCL flag to determine
whether or not the semaphore set exists or not.
The first argument to semget() is the key value (in our case returned by a call to
semget). The second argument (sops) is a pointer to an array of operations to be per-
formed on the semaphore set, while the third argument (nsops) is the number of opera-
tions in that array.
The sops argument points to an array of type sembuf. This structure is declared in
linux/sem.h as follows:
sem num
The number of the semaphore you wish to deal with
sem op
The operation to perform (positive, negative, or zero)
sem flg
Operational flags
If sem op is negative, then its value is subtracted from the semaphore. This cor-
relates with obtaining resources that the semaphore controls or monitors access of. If
IPC NOWAIT is not specified, then the calling process sleeps until the requested amount
of resources are available in the semaphore (another process has released some).
If sem op is positive, then it’s value is added to the semaphore. This correlates with
returning resources back to the application’s semaphore set. Resources should always be
returned to a semaphore set when they are no longer needed!
Finally, if sem op is zero (0), then the calling process will sleep() until the semaphore’s
value is 0. This correlates to waiting for a semaphore to reach 100% utilization. A good
example of this would be a daemon running with superuser permissions that could dynam-
ically adjust the size of the semaphore set if it reaches full utilization.
In order to explain the semop call, let’s revisit our print room scenario. Let’s assume
only one printer, capable of only one job at a time. We create a semaphore set with only
one semaphore in it (only one printer), and initialize that one semaphore to a value of one
(only one job at a time).
Each time we desire to send a job to this printer, we need to first make sure that the
resource is available. We do this by attempting to obtain one unit from the semaphore.
Let’s load up a sembuf array to perform the operation:
Translation of the above initialized structure dictates that a value of “-1” will be added
to semaphore number 0 in the semaphore set. In other words, one unit of resources will be
obtained from the only semaphore in our set (0th member). IPC NOWAIT is specified, so
the call will either go through immediately, or fail if another print job is currently printing.
Here is an example of using this initialized sembuf structure with the semop system call:
The third argument (nsops) says that we are only performing one (1) operation (there
is only one sembuf structure in our array of operations). The sid argument is the IPC
identifier for our semaphore set.
When our print job has completed, we must return the resources back to the semaphore
set, so that others may use the printer.
Translation of the above initialized structure dictates that a value of “1” will be added
to semaphore number 0 in the semaphore set. In other words, one unit of resources will be
returned to the set.
The semctl system call is used to perform control operations on a semaphore set. This
call is analogous to the msgctl system call which is used for operations on message queues.
If you compare the argument lists of the two system calls, you will notice that the list for
semctl varies slightly from that of msgctl. Recall that semaphores are actually implemented
as sets, rather than as single entities. With semaphore operations, not only does the IPC
key need to be passed, but the target semaphore within the set as well.
Both system calls utilize a cmd argument, for specification of the command to be per-
formed on the IPC object. The remaining difference lies in the final argument to both
calls. In msgctl, the final argument represents a copy of the internal data structure used
by the kernel. Recall that we used this structure to retrieve internal information about a
message queue, as well as to set or change permissions and ownership of the queue. With
semaphores, additional operational commands are supported, thus requiring a more com-
plex data type as the final argument. The use of a union confuses many neophyte semaphore
programmers to a substantial degree. We will dissect this structure carefully, in an effort to
prevent any confusion.
The first argument to semctl() is the key value (in our case returned by a call to
semget). The second argument (semun) is the semaphore number that an operation is
targeted towards. In essence, this can be thought of as an index into the semaphore set, with
the first semaphore (or only one) in the set being represented by a value of zero (0).
The cmd argument represents the command to be performed against the set. As you
can see, the familiar IPC STAT/IPC SET commands are present, along with a wealth of
additional commands specific to semaphore sets:
IPC STAT
Retrieves the semid ds structure for a set, and stores it in the address of the buf
argument in the semun union.
52 CHAPTER 6. LINUX INTERPROCESS COMMUNICATIONS
IPC SET
Sets the value of the ipc perm member of the semid ds structure for a set. Takes the
values from the buf argument of the semun union.
IPC RMID
Removes the set from the kernel.
GETALL
Used to obtain the values of all semaphores in a set. The integer values are stored in
an array of unsigned short integers pointed to by the array member of the union.
GETNCNT
Returns the number of processes currently waiting for resources.
GETPID
Returns the PID of the process which performed the last semop call.
GETVAL
Returns the value of a single semaphore within the set.
GETZCNT
Returns the number of processes currently waiting for 100% resource utilization.
SETALL
Sets all semaphore values with a set to the matching values contained in the array
member of the union.
SETVAL
Sets the value of an individual semaphore within the set to the val member of the
union.
The arg argument represents an instance of type semun. This particular union is
declared in linux/sem.h as follows:
val
Used when the SETVAL command is performed. Specifies the value to set the
semaphore to.
buf
Used in the IPC STAT/IPC SET commands. Represents a copy of the internal
semaphore data structure used in the kernel.
array
A pointer used in the GETALL/SETALL commands. Should point to an array of
integer values to be used in setting or retrieving all semaphore values in a set.
6.4. SYSTEM V IPC 53
The remaining arguments buf and pad are used internally in the semaphore code
within the kernel, and are of little or no use to the application developer. As a matter of
fact, these two arguments are specific to the Linux operating system, and are not found in
other UNIX implementations.
Since this particular system call is arguably the most difficult to grasp of all the System
V IPC calls, we’ll examine multiple examples of it in action.
The following snippet returns the value of the passed semaphore. The final argument
(the union) is ignored when the GETVAL command is used:
To revisit the printer example, let’s say the status of all five printers was required:
#define MAX_PRINTERS 5
printer_usage()
{
int x;
Consider the following function, which could be used to initialize a new semaphore
value:
semopts.val = initval;
semctl( sid, semnum, SETVAL, semopts);
}
Note that the final argument of semctl is a copy of the union, rather than a pointer to it.
While we’re on the subject of the union as an argument, allow me to demonstrate a rather
common mistake when using this system call.
Recall from the msgtool project that the IPC STAT and IPC SET commands were used
to alter permissions on the queue. While these commands are supported in the semaphore
implementation, their usage is a bit different, as the internal data structure is retrieved and
copied from a member of the union, rather than as a single entity. Can you locate the bug
in this code?
printf("Updated...\n");
}
The code is attempting to make a local copy of the internal data structure for the set,
modify the permissions, and IPC SET them back to the kernel. However, the first call to
semctl promptly returns EFAULT, or bad address for the last argument (the union!). In
addition, if we hadn’t checked for errors from that call, we would have gotten a memory
fault. Why?
Recall that the IPC SET/IPC STAT commands use the buf member of the union, which
is a pointer to a type semid ds. Pointers are pointers are pointers are pointers! The buf
member must point to some valid storage location in order for our function to work prop-
erly. Consider this revamped version:
printf("Updated...\n");
}
Locking a Semaphore
Unlocking a Semaphore
semtool m (mode)
semtool d
Examples
semtool c 5
semtool l
semtool u
semtool m 660
semtool d
The Source
/*****************************************************************************
Excerpt from "Linux Programmer’s Guide - Chapter 6"
(C)opyright 1994-1995, Scott Burkett
*****************************************************************************
MODULE: semtool.c
*****************************************************************************
A command line tool for tinkering with SysV style Semaphore Sets
*****************************************************************************/
56 CHAPTER 6. LINUX INTERPROCESS COMMUNICATIONS
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
if(argc == 1)
usage();
switch(tolower(argv[1][0]))
{
case ’c’: if(argc != 3)
usage();
createsem(&semset_id, key, atoi(argv[2]));
break;
case ’l’: if(argc != 3)
usage();
opensem(&semset_id, key);
locksem(semset_id, atoi(argv[2]));
break;
case ’u’: if(argc != 3)
usage();
opensem(&semset_id, key);
unlocksem(semset_id, atoi(argv[2]));
break;
case ’d’: opensem(&semset_id, key);
removesem(semset_id);
break;
case ’m’: opensem(&semset_id, key);
changemode(semset_id, argv[2]);
break;
default: usage();
6.4. SYSTEM V IPC 57
return(0);
}
semopts.val = SEM_RESOURCE_MAX;
sem_lock.sem_num = member;
dispval(sid, member);
}
sem_unlock.sem_num = member;
dispval(sid, member);
}
6.4. SYSTEM V IPC 59
semopts.buf = &mysemds;
if (rc == -1) {
perror("semctl");
exit(1);
}
printf("Updated...\n");
{
int semval;
void usage(void)
{
fprintf(stderr, "semtool - A utility for tinkering with semaphores\n");
fprintf(stderr, "\nUSAGE: semtool4 (c)reate <semcount>\n");
fprintf(stderr, " (l)ock <sem #>\n");
fprintf(stderr, " (u)nlock <sem #>\n");
fprintf(stderr, " (d)elete\n");
fprintf(stderr, " (m)ode <mode>\n");
exit(1);
}
/*****************************************************************************
Excerpt from "Linux Programmer’s Guide - Chapter 6"
(C)opyright 1994-1995, Scott Burkett
*****************************************************************************
MODULE: semstat.c
*****************************************************************************
A companion command line tool for the semtool package. semstat displays
the current value of all semaphores in the set created by semtool.
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
show_sem_usage(semset_id);
return(0);
}
maxsems = get_sem_count(sid);
Kernel shmid ds structure As with message queues and semaphore sets, the kernel
maintains a special internal data structure for each shared memory segment which ex-
ists within its addressing space. This structure is of type shmid ds, and is defined in
linux/shm.h as follows:
/* One shmid data structure for each shared memory segment in the syste
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
time_t shm_atime; /* last attach time */
time_t shm_dtime; /* last detach time */
time_t shm_ctime; /* last change time */
unsigned short shm_cpid; /* pid of creator */
unsigned short shm_lpid; /* pid of last operator */
short shm_nattch; /* no. of current attaches */
Operations on this structure are performed by a special system call, and should not be
tinkered with directly. Here are descriptions of the more pertinent fields:
shm perm
This is an instance of the ipc perm structure, which is defined for us in
linux/ipc.h. This holds the permission information for the segment, includ-
ing the access permissions, and information about the creator of the segment (uid,
etc).
shm segsz
Size of the segment (measured in bytes).
shm atime
Time the last process attached the segment.
shm dtime
Time the last process detached the segment.
6.4. SYSTEM V IPC 63
shm ctime
Time of the last change to this structure (mode change, etc).
shm cpid
The PID of the creating process.
shm lpid
The PID of the last process to operate on the segment.
shm nattch
Number of processes currently attached to the segment.
This particular call should almost seem like old news at this point. It is strikingly similar
to the corresponding get calls for message queues and semaphore sets.
The first argument to shmget() is the key value (in our case returned by a call to
ftok()). This key value is then compared to existing key values that exist within the
kernel for other shared memory segments. At that point, the open or access operation is
dependent upon the contents of the shmflg argument.
IPC CREAT
Create the segment if it doesn’t already exist in the kernel.
IPC EXCL
When used with IPC CREAT, fail if segment already exists.
If IPC CREAT is used alone, shmget() either returns the segment identifier for a
newly created segment, or returns the identifier for a segment which exists with the same
key value. If IPC EXCL is used along with IPC CREAT, then either a new segment is
created, or if the segment exists, the call fails with -1. IPC EXCL is useless by itself, but
when combined with IPC CREAT, it can be used as a facility to guarantee that no existing
segment is opened for access.
Once again, an optional octal mode may be OR’d into the mask.
Let’s create a wrapper function for locating or creating a shared memory segment :
64 CHAPTER 6. LINUX INTERPROCESS COMMUNICATIONS
return(shmid);
}
Note the use of the explicit permissions of 0660. This small function either returns
a shared memory segment identifier (int), or -1 on error. The key value and requested
segment size (in bytes) are passed as arguments.
Once a process has a valid IPC identifier for a given segment, the next step is for the
process to attach or map the segment into its own addressing space.
If the addr argument is zero (0), the kernel tries to find an unmapped region. This is the
recommended method. An address can be specified, but is typically only used to facilitate
proprietary hardware or to resolve conflicts with other apps. The SHM RND flag can be
OR’d into the flag argument to force a passed address to be page aligned (rounds down to
the nearest page size).
In addition, if the SHM RDONLY flag is OR’d in with the flag argument, then the
shared memory segment will be mapped in, but marked as readonly.
This call is perhaps the simplest to use. Consider this wrapper function, which is passed
a valid IPC identifier for a segment, and returns the address that the segment was attached
to:
Once a segment has been properly attached, and a process has a pointer to the start of
that segment, reading and writing to the segment become as easy as simply referencing or
dereferencing the pointer! Be careful not to lose the value of the original pointer! If this
happens, you will have no way of accessing the base (start) of the segment.
This particular call is modeled directly after the msgctl call for message queues. In light
of this fact, it won’t be discussed in too much detail. Valid command values are:
IPC STAT
Retrieves the shmid ds structure for a segment, and stores it in the address of the buf
argument
IPC SET
Sets the value of the ipc perm member of the shmid ds structure for a segment. Takes
the values from the buf argument.
IPC RMID
Marks a segment for removal.
The IPC RMID command doesn’t actually remove a segment from the kernel. Rather,
it marks the segment for removal. The actual removal itself occurs when the last process
currently attached to the segment has properly detached it. Of course, if no processes are
currently attached to the segment, the removal seems immediate.
To properly detach a shared memory segment, a process calls the shmdt system call.
Examples
shmtool w test
shmtool w "This is a test"
shmtool r
shmtool d
shmtool m 660
The Source
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
if(argc == 1)
usage();
/* Attach (map) the shared memory segment into the current process */
if((segptr = shmat(shmid, 0, 0)) == -1)
{
perror("shmat");
exit(1);
}
switch(tolower(argv[1][0]))
{
case ’w’: writeshm(shmid, segptr, argv[2]);
break;
case ’r’: readshm(shmid, segptr);
break;
case ’d’: removeshm(shmid);
break;
case ’m’: changemode(shmid, argv[2]);
break;
default: usage();
}
}
removeshm(int shmid)
{
shmctl(shmid, IPC_RMID, 0);
printf("Shared memory segment marked for deletion\n");
}
usage()
{
fprintf(stderr, "shmtool - A utility for tinkering with shared memory\n
fprintf(stderr, "\nUSAGE: shmtool (w)rite <text>\n");
fprintf(stderr, " (r)ead\n");
fprintf(stderr, " (d)elete\n");
fprintf(stderr, " (m)ode change <octal mode>\n");
exit(1);
}
Sound Programming
A PC has at least one sound device: the internal speaker. But, you can also buy a sound
card to plug into your PC to provide a more sophisticated sound device. Look at the Linux
Sound User’s Guide or the Sound-HOWTO for supported sound cards.
1. KDMKTONE
Generates a beep for a specified time using the kernel timer.
Example: ioctl (fd, KDMKTONE,(long) argument).
2. KIOCSOUND
Generates an endless beep or stops a currently sounding beep.
Example: ioctl(fd,KIOCSOUND,(int) tone).
The argument consists of the tone value in the low word and the duration in the
high word. The tone value is not the frequency. The PC mainboard timer 8254 is clocked
at 1.19 MHz and so it’s 1190000/frequency. The duration is measured in timer ticks. Both
ioctl calls return immediately so you can this way produce beeps without blocking the
program.
KDMKTONE should be used for warning signals because you don’t have to worry about
stopping the tone.
KIOCSOUND can be used to play melodies as demonstrated in the example program splay
(please send more .sng files to me). To stop the beep you have to use the tone value 0.
69
70 CHAPTER 7. SOUND PROGRAMMING
By using the sound driver for your programs, chances are that they will work on other
i386 systems as well, since some clever people decided to use the same driver for Linux,
isc, FreeBSD and most other i386 based systems. It will aid in porting programs if Linux
on other architectures offers the same sound device interface. A sound card is not part of
the Linux console, but is a special device. A sound card mostly offers three main features:
• A midi interface
Each of these features have their own device driver interface. For digital samples it
is /dev/dsp, for the frequency modulation it is /dev/sequencer and for the midi interface it
is /dev/midi. The sound settings (like volume, balance or bass) can be controlled via the
/dev/mixer interface. For compatibility reasons a /dev/audio device exists which can read
SUN µ-law sound data, but it maps to the digital sample device.
You are right if you guessed that you use ioctl() to manipulate these devices. The ioctl()
requests are defined in < linux/soundcard.h > and begin with SNDCTL .
This chapter deals with screen input and output that is not pixel based, but character based.
When we say character, we mean a composition of pixels that can be changed depending
on a charset. Your graphic card already offers one or more charsets and operates by default
in text (charset) mode because text can be processed much faster than pixel graphic. There
is more to do with terminals than to use them as simple (dumb) and boring text displays. I
will describe how to use the special features that your linux terminal, especially the linux
console, offers.
71
72 CHAPTER 8. CHARACTER CELL GRAPHICS
curses as defined in SYSVR4 and has some extensions such as color manipulation,
special optimization for output, terminal specific optimizations, and more. It has
been tested on a lot of systems such as Sun-OS, HP and Linux. I recommend using
ncurses instead of the others. On SYSV Unix systems (such as Sun’s Solaris) there
should exist a curses library with the same functionality as ncurses (actually the
solaris curses has some more functions and mouse support).
In the following sections I will describe how to use the different packages to access a
terminal. With Linux we have the GNU-version of termcap and we can use ncurses instead
of curses.
– Flags
∗ -
The formatted argument will be printed on the left margin (default is the
right margin in the argument field).
∗ +
Every number will be printed with a sign, e.g. +12 or -2.32.
– Blank
When the first character is not a sign, a blank will be inserted.
– 0
For numeric transformation the field width will be filled up with 0’s on the left
side.
– #
Alternate output depending on the transformation for the argument
∗ For o the first number is a 0.
∗ For x or X 0x or 0X will be printed in front of the argument.
∗ For e, E, f or F the output has a decimal point.
∗ For g or G zeroes on the end of the argument are printed.
– A number for the minimal field width.
The transformed argument is printed in a field which is at least as big as the
argument itself. With a number you can make the field width bigger. If the
formatted argument is smaller, then the field width will be filled with zeroes or
blanks.
8.1. I/O FUNCTION IN LIBC 73
Character Formatted to
d,i int signed, decimal
o int unsigned, octal, without leading 0
x,X int unsigned, hexadecimal without leading 0x
u int unsigned, decimal
c int (unsigned) single character
s char * up to \0
f double as [-]mmm.ddd
e,E double as [-]m.dddddde±xx
g,G double using %e or %f as needed
p void *
n int *
% %
Possible values for the transformation are in table 8.1 on page 73.
Programs using the termcap library must include termcap.h and should be linked with:
Termcap functions are terminal independent routines but only give the programmer low
level access to the terminal. For a higher level package, curses or ncurses should be used.
8.2. THE TERMCAP LIBRARY 75
#define buffer 0
char *termtype=getenv("TERM");
int ok;
ok=tgetent(buffer,termtype);
if(ok==1)
/* all right, we have the entry */
else if(ok==0)
/* uups, something wrong with TERM
* check termtype first, then termcap database
*/
else
/* huuu, fatal error */
By, default termcap uses /etc/termcap/ as the database. If the environment vari-
able TERMCAP is set, with $HOME/mytermcap for instance, all functions will use
mytermcap instead of /etc/termcap. With no leading slash in TERMCAP, the defined
value is used as a name for a terminal.
Each capability is associated with a single value type. (co is always numeric, hc is
always a flag and st is always a string). There are three different types of values, so there
are also three functions to interrogate them. char *name is the two letter code for the
capability.
void term_caps()
{
char *tmp;
PC=tmp ? *tmp : 0;
BC=tgetstr("le",0); /* cursor left one char */
UP=tgetstr("up",0); /* cursor up one line */
}
Numeric Capabilities
String Capabilities
• screen - is a window with the size of the entire screen (from the upper left to the
lower right). Stdscr and curscr are screens.
• terminal - is a special screen with information about what the screen currently looks
like.
• functions - in the function description the arguments are of the following type:
– win - WINDOW*
– bf - bool
– ch - chtype
– str - char*
– chstr - chtype*
– fmt - char*
– otherwise int
#include <ncurses.h>
...
main()
{
...
initscr();
/* ncurses function calls */
endwin();
...
}
Including ncurses.h will define variables and types for ncurses, such as WINDOW and
function prototypes. It automatically includes stdio.h, stdarg.h, termios.h and unctrl.h.
initscr() is used to initialize the ncurses data structures and to read the proper terminfo
file. Memory for stdscr and curscr will be allocated. If an error occurs, initscr will return
ERR, otherwise a pointer to stdscr will be returned. Additionally, the screen will be erased
and LINES and COLS will be initialized.
8.3. NCURSES - INTRODUCTION 81
endwin() will clean up all allocated resources from ncurses and restore the tty modes
to the status they had before calling initscr(). It must be called before any other function
from the ncurses library and endwin() must be called before your program exits. When you
want to do output to more than one terminal, you can use newterm(...) instead of initscr().
Compile the program with:
In flags you can include anything you like (see gcc(1)). Since the path for ncurses.h has
changed you have to include the following line:
-I/usr/include/ncurses
Otherwise, ncurses.h, nterm.h, termcap.h and unctrl.h will not be found. Possible other
flags for Linux are:
O2 tells gcc to do some optimization, -ansi is for ansi conformant c-code, -Wall will
print out all warnings, -m486 will use optimized code for an Intel 486 (the binary can be
used on an Intel 386, too).
The ncurses library can be found in /usr/lib/. There are three versions of the ncurses
library:
• libncurses.a the normal ncurses library.
• libdcurses.a ncurses for debugging.
• libpcurses.a ncurses for profiling (since 1.8.6libpcurses.a exists no longer ?).
• libcurses.a No fourth version, but the original BSD curses (in my slackware 2.1.0 it
is the bsd package).
The data structures for the screen are called windows as defined in ncurses.h. A win-
dow is something like a character array in memory which the programmer can manipulate
without output to the terminal. The default window is stdscr with the size of the terminal.
You can create other windows with newwin(...).
To update the physical terminal optimally, ncurses has another window declared, curscr.
This is an image of how the terminal actually looks and stdscr is an image of how the
terminal should look. The output will be done when you call refresh(). Ncurses will then
update curscr and the physical terminal with the information in stdscr. The library functions
will use internal optimization for the update process so you can change different windows
and then update the screen at once in the most optimal way.
With the ncurses functions you can manipulate the data structure window. Functions
beginning with w allow you to specify a window, while others will usually affect stdscr.
Functions beginning with mv will move the cursor to the position y,x first.
A character has the type chtype which is long unsigned int to store additional informa-
tion about it (attributes etc.).
Ncurses use the terminfo database. Normally the database is located in usr/lib/terminfo/
and ncurses will look there for local terminal definitions. If you want to test some other def-
initions for a terminal without changing the original terminfo, set the environment variable
TERMINFO. Ncurses will check this variable and use the definitions stored there instead
of /usr/lib/terminfo/.
Current ncurses version is 1.8.6().
At the end of this chapter you can find a table with an overview for the BSD-Curses,
ncurses and the curses from Sun-OS 5.4. Refer to it when you want to look for a specific
function and where it is implemented.
82 CHAPTER 8. CHARACTER CELL GRAPHICS
8.4 Initializing
• WINDOW *initscr()
This is the first function usually called from a program using ncurses. In some
cases it is useful to call slk init(int), filter(), ripoffline(...) or use env(bf) before
initscr(). When using multiple terminals (or perhaps testing capabilities), you can
use newterm(...) instead of initscr().
initscr() will read the proper terminfo file and initialize the ncurses data structures,
allocate memory for curscr and stdscr and set LINES and COLS to the values the
terminal has. It will return a pointer to stdscr or ERR when an error has occured.
You don’t need to initialize the pointer with:
stdscr=initscr();
initscr() will do this for you. If the return value is ERR, your program should exit
because no ncurses function will work:
if(!(initscr())){
fprintf(stderr,"type: initscr() failed\n\n");
exit (1);
}
• int endwin()
endwin() will do the cleanup, restore the terminal modes in the state they had before
calling initscr() and move the cursor to the lower left corner. Don’t forget to close
all opened windows before you call endwin() to exit your program.
An additional call to refresh() after endwin() will restore the terminal to the status
it had before calling initscr() (visual-mode) otherwise it will be cleared (non-visual-
mode).
• int isendwin()
Returns TRUE if endwin() was called with a following refresh(), otherwise FALSE.
8.5 Windows
Windows can be created, deleted, moved, copied, touched, duplicated and more.
0 begx
0 COLS -
. . . . . . . . ncols
. . . . . . . . . . -
.
begy
.6
.
.
.
.
.
nlines . newwin(nlines, ncols, begy, begx)
.
.
.
.
.
?
LINES
?
WINDOW *mywin;
mywin=newwin(10,60,10,10);
The upper left corner of our window is in line 10 and column 10 and the window has
10 lines and 60 columns. If nlines is zero, the window will have LIN ES − begy
rows. In the same way the, window will have COLS − begx columns when ncols
is zero.
When you call newwin(...) with all argument zero:
WINDOW *mywin;
mywin=newwin(0,0,0,0);
fprintf(stderr,"screen to small\n\n");
endwin(); exit (1);
}
win=newwin(MYLINES,MYCOLS,MYLINE,MYCOL);
...
This will open a window with 22 lines and 70 rows in the middle of the screen.
Check the screen size before opening windows. In the Linux console we have 25 or
more lines and 80 or more columns, but in xterms this may not be the case (they’re
resizable).
Alternatively, use LINES and COLS to adapt two windows to the screen size:
8.6 Output
• int addch(ch)
int waddch(win, ch)
int mvaddch(y, x, ch)
int mvwaddch(win, y, x, ch)
These functions are used for character output to a window. They will manipulate
the window and you will have to call refresh() to put it on screen. addch(...) and
waddch(...) put the character ch in the window stdscr or win. mvaddch(...) and
mvwaddch(...) do the same except that they move the cursor to position y,x first.
• int addstr(str)
int addnstr(str, n)
int waddstr(win, str)
int waddnstr(win, str, n)
int mvaddstr(y, x, str)
int mvaddnstr(y, x, str, n)
int mvwaddstr(win, y, x, str)
int mvwaddnstr(win, y, x, str, n)
These functions write a string to a window and are equivalent to series of calls to
addch(...). str is a null terminated string (”blafoo\0”). Functions with w write the
string str to the window win, while other funcions write to stdscr. Functions with
n write n characters of str. If n is -1, the entire string str is written.
• int addchstr(chstr)
int addchnstr(chstr, n)
int waddchstr(win, chstr)
int waddchnstr(win, chstr, n)
int mvaddchstr(y, x, chstr)
int mvaddchnstr(y, x, chstr, n)
int mvwaddchstr(win, y, x, chstr)
int mvwaddchnstr(win, y, x, chstr, n)
These functions copy chstr to the window image (stdscr or win). The starting
position is the current cursor position. Functions with n write n characters of chstr.
If n is -1, the entire string chstr is written. The cursor is not moved and no control
character check is done. These functions are faster than the addstr(...) routines.
chstr is a pointer to an array of chtype.
• int echochar(ch)
int wechochar(win, ch)
The same as call addch(...) (waddch(...) followed by refresh() (wrefresh(win).
86 CHAPTER 8. CHARACTER CELL GRAPHICS
tl ts tt ts tr
ls rs
lt rt
ls rs
bl bs bt bs br
y and x are the coordinates to which the cursor will be moved to before deleting.
• int deleteln()
int wdeleteln(win)
Delete the line under the cursor and move all other lines below one position up.
Additionally, the bottom line of the window will be erased.
int hline(ch, n)
int whline(win, ch, n)
These functions draw a vertical or horizontal line starting at the current cursor posi-
tion. ch is the character to use and n is the number of characters to draw. The cursor
position is not advanced.
• int bkgd(ch)
int wbkgd(win, ch)
Will change the background character and attribute to ch.
8.7 Input
• int getch()
int wgetch(win)
int mvgetch(y, x)
int mvwgetch(win, y, x)
getch() will read input from the terminal in a manner depending on whether delay
mode is set or not. If delay is on, getch() will wait until a key is pressed, otherwise
it will return the key in the input buffer or ERR if this buffer is empty. mvgetch(...)
and mvwgetch(...) will move the cursor to position y,x first. The w functions read
input from the terminal related to the window win, getch() and mvgetch(...) from
the terminal related to stdscr.
With keypad(...) enabled, getch() will return a code defined in ncurses.h as KEY *
macros when a function key is pressed. When ESCAPE is pressed (which can be the
beginning of a function key) ncurses will start a one second timer. If the remainder
of the keystroke is not finished in this second, the key is returned. Otherwise, the
function key value is returned. (If necessary, use notimeout() to disable the second
timer).
• int ungetch(ch)
Will put the character ch back to the input buffer.
• int getstr(str)
int wgetstr(win, str)
int mvgetstr(y, x, str)
int mvwgetstr(win, y, x, str)
int wgetnstr(win, str, n)
These functions will do a series of calls to getch() until a newline is received. The
characters are placed in str (so don’t forget to allocate memory for your character
pointer before calling getstr(...)). If echo is enabled the string is echoed (use noe-
cho() to disable echo) and the user’s kill and delete characters will be interpreted.
• chtype inch()
chtype winch(win)
chtype mvinch(y, x)
chtype mvwinch(win, y, x)
8.8. OPTIONS 89
These functions return a character from the screen or window. Because the type of
the return value is chtype attribute information is included. This information can
be extracted from the character using the A * constants (see table 8.4 on page 96).
• int instr(str)
int innstr(str, n)
int winstr(win, str)
int winnstr(win, str, n)
int mvinstr(y, x, str)
int mvinnstr(y, x, str, n)
int mvwinstr(win, y, x, str)
int mvwinnstr(win, y, x, str, n)
Return a character string from the screen or a window. (Note: not implemented yet.)
• int inchstr(chstr)
int inchnstr(chstr, n)
int winchstr(win, chstr)
int winchnstr(win, chstr, n)
int mvinchstr(y, x, chstr)
int mvinchnstr(y, x, chstr, n)
int mvwinchstr(win, y, x, chstr)
int mvwinchnstr(win, y, x, chstr, n)
Return a chtype string from the screen or window. In the string, attribute information
is included for every character. (Note: not implemented yet, lib inchstr not included
in the ncurses lib.)
8.8 Options
Output Options
• int nl()
int nonl()
Control the translation for newline. Turned on with nl() will translate a newline in
carriage return and line feed on output. nonl() will turn translation off. With disabled
translation ncurses can do faster cursor motion.
• int cbreak()
int nocbreak()
int crmode()
int nocrmode()
cbreak() and nocbreak() will turn the terminal CBREAK mode on or off. When
CBREAK is on, input from a read will be immediately available to the program,
when off the input will be buffered until newline occurs. (Note: crmode() and
nocrmode() are for upward compatibility, don’t use them.)
• int raw()
int noraw()
Turn RAW mode on or off. RAW is the same as CBREAK, except that in RAW
mode no special character processing will be done.
• int echo()
int noecho()
Set echo() to echo input typed by the user and noecho() to be silent about it.
8.8. OPTIONS 91
• int halfdelay(t)
As cbreak() with a delay of t seconds.
• int timeout(t)
int wtimeout(win, t)
It is recommended to use these functions instead of halfdelay(t) and node-
lay(win,bf). The result of getch() depends on the value of t. If t is positive, the
read is blocked for t milliseconds, if t is zero, no blocking is done, and when t is
negative the program blocks until input is available.
• int typeahead(fd)
If fd is -1 no typeahead check will be done, else ncurses will use the file descriptor
fd instead of stdin for these checks.
• void noqiflush()
void qiflush()
(Note: not implemented yet.)
• char erasechar()
Returns the current erase character.
• char killchar()
Returns the current kill character.
• char *longname()
The returned pointer gives access to the description of the current terminal.
• chtype termattrs()
(Note: not implemented yet.)
• char *termname()
Returns the contents of TERM from the users environment. (Note: not implemented
yet.)
92 CHAPTER 8. CHARACTER CELL GRAPHICS
keypad(stdscr,TRUE);
1. The program wants the user to enter a key and then will call a function depend on
this key. (For example, something like ”press ’q’ for quit” and wait for q)
2. The program wants a string of characters typed by the user in a mask on the screen.
For example: a directory or an address in a database.
For the first we use the following options and modes and the while loop will work
correctly.
char c;
noecho();
timeout(-1);
nonl();
cbreak();
keypad(stdscr,TRUE);
while(c=getch()){
switch(c){
case ’q’: your_quit_function();
default: break;
}
}
The program will hang until a key is pressed. If the key was q we call our quit function
else we wait for other input.
The switch statement can be expanded until we have an input function that fits our
wishes. Use the KEY * macros to check special keys, for instance
for the cursor keys on the keyboard. For a file viewer the loop can look like this:
int loop=TRUE;
char c;
enum{UP,DOWN,RIGHT,LEFT};
noecho();
timeout(-1);
nonl();
cbreak();
keypad(stdscr,TRUE);
while(loop==TRUE){
c=getch();
switch(c){
case KEY_UP:
case ’u’:
case ’U’: scroll_s(UP);
break;
8.9. CLEAR WINDOW AND LINES 93
case KEY_DOWN:
case ’d’:
case ’D’: scroll_s(DOWN);
break;
case KEY_LEFT:
case ’l’:
case ’L’: scroll_s(LEFT);
break;
case KEY_RIGHT
case ’r’:
case ’R’: scroll_s(RIGHT);
break;
case ’q’:
case ’Q’: loop=FALSE;
default: break;
}
}
For the second, we only need to set echo() and the characters typed by the user will be
printed to the screen. To have the characters printed on the position you want, use move(...)
or wmove(...).
Or, we could open a window with a mask in it (some other colors than those of the
window will do this) and ask the user to input a string:
WINDOW *maskwin;
WINDOW *mainwin;
char *ptr=(char *)malloc(255);
...
mainwin=newwin(3,37,9,21);
maskwin=newwin(1,21,10,35);
...
werase(mainwin);
werase(maskwin);
...
box(mainwin,0,0);
mvwaddstr(mainwin,1,2,"Inputstring: ");
...
wnoutrefresh(mainwin);
wnoutrefresh(maskwin);
doupdate();
...
mvwgetstr(maskwin,0,0,ptr);
...
delwin(maskwin);
delwin(mainwin);
endwin();
free(ptr);
defined other attributes then black on white so I wrote my own erase function (this is
a low level access to the WINDOW structure):
void NewClear(WINDOW *win)
{
int y,x;
The problem is, that ncurses sometimes makes no use of the window attributes when
blanking the screen. For instance, in lib clrtoeol.c, is BLANK defined as
#define BLANK ’ ’|A_NORMAL
so that the other window attributes get lost while the line is erased.
• int clear()
int wclear(win)
The same as erase(), but will also set clearok() (the screen will be cleared with the
next refresh).
• int clrtobot()
int wclrtobot(win)
Clearing the current cursor line (start is one character right from the cursor) and the
line below the cursor.
• int clrtoeol()
int wclrtoeol(win)
Clear the current line right from the cursor up to its end.
This will cause ncurses to update the terminal twice and slow down our execution.
With doupdate() we change changewin(win) and our main function and will get
better a performance.
• int redrawwin(win)
int wredrawln(win, bline, nlines)
Use these functions when some lines or the entire screen should thrown away before
writing anything new in it (may be when the lines are trashed or so).
• int touchwin(win)
int touchline(win, start, count)
int wtouchln(win, y, n, changed)
int untouchwin(win)
Tells ncurses that the whole window win or the lines from start up to
start+count have been manipulated. For instance, when you have some over-
lapping windows (as in the example type.c) a change to one window will not affect
the image from the other.
wtouchln(...) will touch n lines starting at y. If change is TRUE the lines are
touched, otherwise untouched (changed or unchanged).
untouchwin(win) will mark the window win as unchanged since the last call to
refresh().
has colors(). start color() will initialize COLORS, the maximum colors the terminal sup-
ports, and COLOR PAIR, the maximum number of color pairs you can define.
The attributes can be combined with the OR operator ’|’ so that you can produce bold
blinking output with
A_BOLD|A_BLINK
When you set a window to attribute attr, all characters printed to this window will
get this property until you change the window attribute. It will not get lost when you scroll
or move the window or anything else.
When you write programs for ncurses and BSD curses be careful with colors because
BSD curses has no color support. (Also, old SYS V versions of curses do not have color
support). So you have to use #ifdef operations when you compile for both libraries.
• int attroff(attr)
int wattroff(win, attr)
int attron(attr)
int wattron(win, attr)
Turn on or off the specified attribute attr without reflecting the other attributes in
a window (stdscr or win).
• int attrset(attr)
int wattrset(win, attr)
Set the attribute on stdscr or win to attr.
• int standout()
int standend()
int wstandout(win)
int wstandend(win)
Turn on standout attribute for the window (stdscr or win).
• chtype getattrs(win)
Return the current attributes for window win.
• bool has colors()
Returns TRUE if the terminal has colors. Before you use colors check the terminal
with has colors(), and before this initialize colors with start color()).
• bool can change color()
TRUE if the terminal can redefine colors.
• int start color()
Color initializing. This function has to be called before using colors!
• int init pair(pair, fg, bg)
When you use colors as attributes for windows you have first to define a color pair
with init pair(...). fg is the foreground color and bg the background color for pair.
This is a value from 1 to COLOR P AIRS − 1 (No fault, but 0 is reserved for black
on white). Once defined you can use pair like an attribute. For instance when you
want to have red characters on a blue screen do:
init_pair(1,COLOR_RED,COLOR_BLUE);
wattr(win,COLOR_PAIR(1));
wattr(win ,A_BOLD|COLOR_PAIR(1));
wattr(win1,A_STANDOUT|COLOR_PAIR(1));
The first will invoke the color pair and set the attribute BOLD and the second will
turn on standout mode, so that you get highlighted red on a blue screen.
• int pair content(pair, f, b)
Will return the foreground and background color from pair.
• int init color(color, r, g, b)
Change the color components r, g and b for color. r, g and b can have values
from 1 to COLORS − 1.
• int color content(color, r, g, b)
Get the color components r, g and b for color.
And how to combine attributes and colors? Some terminals, as the console in Linux,
have colors and some not (xterm, vs100 etc). The following code should solve the problem:
First, the function CheckColor initializes the colors with start color(), then the function
has colors() will return TRUE if the current terminal has colors. We check this and call
init pair(...) to combine foreground and background colors and wattrset(...) to set these
pairs for the specified window. Alternatively, we can use wattrset(...) alone to set attributes
if we have a black and white terminal.
To get colors in an xterm the best way I found out is to use the ansi xterm with the
patched terminfo entries from the Midnight Commander. Just get the sources of ansi xterm
and Midnight Commander (mc-x.x.tar.gz). Then compile the ansi xterm and use tic with
xterm.ti and vt100.ti from the mc-x.x.tar.gz archive. Execute ansi xterm and test it out.
For input/output functions, additional macros are defined which move the cursor
before the specified function is called.
• int curs set(bf)
This will turn the cursor visibility on or off, if the terminal has this capability.
• void getyx(win, y, x)
getyx(...) will return the current cursor position. (Note: this is a macro.)
• void getparyx(win, y, x)
When win is a sub window, getparyx(...) will store the window coordinates relative
to the parent window in y and x. Otherwise y and x are -1. (Note: not implemented
yet.)
• void getbegyx(win, y, x)
void getmaxyx(win, y, x)
int getmaxx(win)
int getmaxy(win)
Store the begin and size coordinates for win in y and x.
• int getsyx(int y, int x)
int setsyx(int y, int x)
Store the virtual screen cursor in y and x or set this cursor. When y and x are -1 and
you call getsyx(...) leaveok will be set.
8.13 Scrolling
• int scrollok(win, bf)
If TRUE, the text in the window win will be scrolled up one line when the cursor is
on the lower right corner and a character is typed (or newline). If FALSE, the cursor
is left in the same position.
When turned on the contents of a window can be scrolled with the following func-
tions. (Note: It would be also scrolled, if you print a new line in the last line of the
window. So, be careful with scrollok(...) or you will get unreasonable results.)
• int scroll(win)
This function will scroll up the window (and the lines in the data structure) one line.
• int scrl(n)
int wscrl(win, n)
These functions will scroll the window stdscr or win up or down depending on
the value of the integer n. If n is positive the window will be scrolled up n lines,
otherwise if n is negative the window will be scrolled down n lines.
• int setscrreg(t, b)
int wsetscrreg(win, t, b)
Set a software scrolling region.
The following code should explain how to get the effect of scrolling a text on the screen.
Look also in type.c in the example directory.
We have a window with 18 lines and 66 columns and want to scroll a text in it. S[] is
a character array with the text. Max s is the number of the last line in s[]. Clear line will
print blank characters from the current cursor position up to the end of the line using the
current attributes from the window (not A NORMAL as clrtoeol does). Beg is the last line
from s[] currently shown on the screen. Scroll is an enumerate to tell the function what to
do, show the NEXT or PREVious line from the text.
100 CHAPTER 8. CHARACTER CELL GRAPHICS
enum{PREV,NEXT)};
8.14 Pads
• WINDOW *newpad(nlines, ncols)
8.15 Soft-labels
• int slk init(int fmt)
8.16 Miscellaneous
• int beep()
• int flash()
• char *unctrl(chtype c)
• char *keyname(int c)
• int filter()
(Note: not implemented yet.)
• int flushinp()
• int resetty()
• int savetty()
• char *tparm(char *str, p1, p2, p3, p4, p5, p6, p7, p8,
p9)
p1 - p9 long int.
• char * traceattr(mode)
• void traceon()
• void traceoff()
8.22.2 Numbers
Variable Cap. Int. Description
Name Code
bit image entwining bitwin Yo Undocumented in SYSV
buffer capacity bufsz Ya numbers of bytes buffered before printing
columns cols co Number of columns in a line
dot vert spacing spinv Yb spacing of dots horizontally in dots per inch
dot horz spacing spinh Yc spacing of pins vertically in pins per inch
init tabs it it Tabs initially every # spaces
label height lh lh rows in each label
label width lw lw columns in each label
lines lines li Number of lines on screen or page
lines of memory lm lm Lines of memory if ¿ lines. 0 means varies
magic cookie glitch xmc sg Number of blank chars left by smso or rmso
max colors colors Co maximum numbers of colors on screen
max micro address maddr Yd maximum value in micro ... address
max micro jump mjump Ye maximum value in parm ... micro
max pairs pairs pa maximum number of color-pairs on the screen
micro col size mcs Yf Character step size when in micro mode
micro line size mls Yg Line step size when in micro mode
no color video ncv NC video attributes that can’t be used with colors
number of pins npins Yh numbers of pins in print-head
num labels nlab Nl number of labels on screen
output res char orc Yi horizontal resolution in units per line
output res line orl Yj vertical resolution in units per line
output res horz inch orhi Yk horizontal resolution in units per inch
output res vert inch orvi Yl vertical resolution in units per inch
padding baud rate pb pb Lowest baud where cr/nl padding is needed
virtual terminal vt vt Virtual terminal number (UNIX system)
width status line wsl ws No. columns in status line
(The following numeric capabilities are present in the SYSV term structure, but are not yet docu-
mented in the man page. Comments are from the term structure header.)
bit image type bitype Yp Type of bit-image device
buttons btns BT Number of mouse buttons
max attributes ma ma Max combined attributes terminal can handle
maximum windows wnum MW Max number of definable windows
print rate cps Ym Print rate in chars per second
wide char size widcs Yn Char step size in double wide mode
8.22.3 Strings
Variable Cap. Int. Description
Name Code
acs chars acsc ac Graphics charset pairs - def=vt100
alt scancode esc scesa S8 Alternate esc for scancode emulation
(default is vt100)
back tab cbt bt Back tab (P)
bell bel bl Audible signal (bell) (P)
106 CHAPTER 8. CHARACTER CELL GRAPHICS
key f0 kf0 k0 F00 function key key f32 kf32FM F32 function key
key f1 kf1 k1 F01 function key key f33 kf33FN F33 function key
key f2 kf2 k2 F02 function key key f34 kf34FO F34 function key
key f3 kf3 k3 F03 function key key f35 kf35FP F35 function key
key f4 kf4 k4 F04 function key key f36 kf36FQ F36 function key
key f5 kf5 k5 F05 function key key f37 kf37FR F37 function key
key f6 kf6 k6 F06 function key key f38 kf38FS F38 function key
key f7 kf7 k7 F07 function key key f39 kf39FT F39 function key
key f8 kf8 k8 F08 function key key f40 kf40FU F40 function key
key f9 kf9 k9 F09 function key key f41 kf41FV F41 function key
key f10 kf10k; F10 function key key f42 kf42FW F42 function key
key f11 kf11F1 F11 function key key f43 kf43FX F43 function key
key f12 kf12F2 F12 function key key f44 kf44FY F44 function key
key f13 kf13F3 F13 function key key f45 kf45FZ F45 function key
key f14 kf14F4 F14 function key key f46 kf46Fa F46 function key
key f15 kf15F5 F15 function key key f47 kf47Fb F47 function key
key f16 kf16F6 F16 function key key f48 kf48Fc F48 function key
key f17 kf17F7 F17 function key key f49 kf49Fd F49 function key
key f18 kf18F8 F18 function key key f50 kf50Fe F50 function key
key f19 kf19F9 F19 function key key f51 kf51Ff F51 function key
key f20 kf20FA F20 function key key f52 kf52Fg F52 function key
key f21 kf21FB F21 function key key f53 kf53Fh F53 function key
key f22 kf22FC F22 function key key f54 kf54Fi F54 function key
key f23 kf23FD F23 function key key f55 kf55Fj F55 function key
key f24 kf24FE F24 function key key f56 kf56Fk F56 function key
key f25 kf25FF F25 function key key f57 kf57Fl F57 function key
key f26 kf26FG F26 function key key f58 kf58Fm F58 function key
key f27 kf27FH F27 function key key f59 kf59Fn F59 function key
key f28 kf28FI F28 function key key f60 kf60Fo F60 function key
key f29 kf29FJ F29 function key key f61 kf61Fp F61 function key
key f30 kf30FK F30 function key key f62 kf62Fq F62 function key
key f31 kf31FL F31 function key key f63 kf63Fr F63 function key
(The following string capabilities are present in the SYSVr term structure, but are not documented in
the man page. Comments are from the term structure header.)
gettmode() x x mvinsstr(...) x x 86
getwch(...) x mvinstr(...) x x,n 89
getwin(...) x mvinswch(...) x
getwin(FILE *) x x,n 101 mvinswstr(...) x
getwstr(...) x mvinwch(...) x
getyx(...) x x x 99 mvinwchnstr(...) x
halfdelay(t) x x 91 mvinwchstr(...) x
has colors() x x 97 mvinwstr(...) x
has ic() x x,n 91 mvprintw(...) x x x 86
has il() x x,n 91 mvscanw(...) x x x 89
hline(...) x x 88 mvvline(...) x
idcok(...) x x,n 89 mvwaddbytes(...) x
idlok(...) x x x 89 mvwaddch(...) x x x 85
immedok(...) x x 89 mvwaddchnstr(...) x x 85
inch() x x x 88 mvwaddchstr(...) x x 85
inchnstr(...) x x,n 89 mvwaddnstr(...) x x 85
inchstr(...) x x,n 89 mvwaddnwstr(...) x
init color(...) x x 98 mvwaddstr(...) x x x 85
init pair(...) x x 97 mvwaddwch(...) x
initscr() x x x 82 mvwaddwchnstr(...) x
innstr(...) x x,n 89 mvwaddwchstr(...) x
innwstr(...) x mvwaddwstr(...) x
insch(c) x x x 86 mvwdelch(...) x x x 86
insdelln(n) x x 86 mvwgetch(...) x x x 88
insertln() x x x 86 mvwgetnwstr(...) x
insnstr(...) x x 86 mvwgetstr(...) x x x 88
insstr(str) x x 86 mvwgetwch(...) x
instr(str) x x,n 89 mvwgetwstr(...) x
inswch(...) x mvwhline(...) x
inswstr(...) x mvwin(...) x x x 84
intrflush(...) x x 91 mvwinch(...) x x x 88
inwch(...) x mvwinchnstr(...) x x,n 89
inwchnstr(...) x mvwinchstr(...) x x,n 89
inwchstr(...) x mvwinnstr(...) x x,n 89
inwchstr(...) x mvwinnwstr(...) x
inwstr(...) x mvwinsch(...) x x x 86
is linetouched(...) x x 95 mvwinsnstr(...) x x 86
is wintouched(win) x x 95 mvwinsstr(...) x x 86
isendwin() x x 82 mvwinstr(...) x x,n 89
keyname(c) x x 101 mvwinswch(...) x
keypad(...) x x 90 mvwinswstr(...) x
killchar() x x x 91 mvwinwch(...) x
leaveok(...) x x x 90 mvwinwchnstr(...) x
longname() x x x 91 mvwinwchstr(...) x
map button(long) x mvwinwstr(...) x
meta(...) x x 90 mvwprintw(...) x x x 86
mouse off(long) x mvwscanw(...) x x x 89
mouse on(long) x mvwvline(...) x
mouse set(long) x napms(ms) x x 102
move(...) x x x 98 newkey(...) x
movenextch() x newpad(...) x x 100
moveprevch() x newscreen(...) x
mvaddbytes(...) x newterm(...) x x 82
mvaddch(...) x x x 85 newwin(...) x x x 82
mvaddchnstr(...) x x 85 nl() x x x 90
mvaddchstr(...) x x 85 nocbreak() x x x 90
mvaddnstr(...) x x 85 nocrmode() x x x 90
mvaddnwstr(...) x nodelay(...) x x 91
mvaddstr(...) x x x 85 noecho() x x x 90
mvaddwch(...) x nonl() x x x 90
mvaddwchnstr(...) x noqiflush() x x,n 91
mvaddwchstr(...) x noraw() x x x 90
mvaddwstr(...) x notimeout(...) x x 91
mvcur(...) x x x 103 overlay(...) x x x 85
mvdelch(...) x x x 86 overwrite(...) x x x 85
mvderwin(...) x x,n 84 pair content(...) x x 98
mvgetch(...) x x x 88 pechochar(...) x x 100
mvgetnwstr(...) x pechowchar(...) x
mvgetstr(...) x x x 88 pnoutrefresh(...) x x 100
mvgetwch(...) x prefresh(...) x x 100
mvgetwstr(...) x printw(...) x x x 86
mvhline(...) x putp(char *) x x 103
mvinch(...) x x x 88 putwin(...) x x,n 101
mvinchnstr(...) x x,n 89 qiflush() x x,n 91
mvinchstr(...) x x,n 89 raw() x x x 90
mvinnstr(...) x x,n 89 redrawwin(win) x x 95
mvinnwstr(...) x refresh() x x x 94
mvinsch(...) x x x 86 request mouse pos() x
mvinsnstr(...) x x 86 reset prog mode() x x 102
mvinsnwstr(...) x reset shell mode() x x 102
114 CHAPTER 8. CHARACTER CELL GRAPHICS
To be continued...
Sven Goldt The Linux Programmer’s Guide
Chapter 9
Usually a PC at least has 2 serial and 1 parallel interfaces. These interfaces are special
devices and are mapped as follows:
• /dev/ttyS0 − /dev/ttySn
these are the RS232 serial devices 0-n where n depends on your hardware.
• /dev/cua0 − /dev/cuan
these are the RS232 serial devices 0-n where n depends on your hardware.
• /dev/lp0 − /dev/lpn
these are the parallel devices 0-n where n depends on your hardware.
• /dev/js0 − /dev/jsn
these are the joystick devices 0-n where 0 <= n <= 1.
The difference between the /dev/ttyS∗ and /dev/cua∗ devices is how a call to open()
is handled. The /dev/cua∗ devices are supposed to be used as callout devices and thus
get other default settings by a call to open() than the /dev/ttyS∗ devices which will be
initalized for incoming and outgoing calls. By default the devices are controlling devices
for the process that opened them. Normally ioctl() requests should handle all these special
devices, but POSIX preferred to define new functions to handle asynchronous terminals
heavily depending on the struct termios. Both methods require including < termios.h >.
1. method ioctl:
TCSBRK, TCSBRKP, TCGETA (get attributes), TCSETA (set attributes)
Terminal I/O control (TIOC) requests:
TIOCGSOFTCAR (set soft carrier), TIOCSSOFTCAR (get soft carrier), TIOC-
SCTTY (set controlling tty), TIOCMGET (get modemlines), TIOCMSET (set
modemlines), TIOCGSERIAL, TIOCSSERIAL, TIOCSERCONFIG, TIOCSERG-
WILD, TIOCSERSWILD, TIOCSERGSTRUCT, TIOCMBIS, TIOCMBIC, ...
2. method POSIX:
tcgetattr(), tcsetattr(), tcsendbreak(), tcdrain(), tcflush(), tcflow(), tcgetpgrp(),
tcsetpgrp()
cfsetispeed(), cfgetispeed(), cfsetospeed(), cfgetospeed()
3. other methods:
outb,inb for hardware near programming like using the printer port not for a printer.
115
116 CHAPTER 9. PROGRAMMING I/O PORTS
1. int acceleration
is the acceleration factor. If you move the mouse more than delta pixels, motion
becomes faster depending on this value.
2. int baud
is the bps rate your mouse uses (normally 1200).
3. int delta
this is the number of pixels that you have to move the mouse before the acceleration
starts.
4. char *device
is the name of your mouse device (e.g. /dev/mouse).
5. int toggle
toggle the DTR, RTS or both DTR and RTS mouse modem lines on initialization
(normally 0).
6. int sample
the resolution (dpi) of your mouse (normally 100).
8. int slack
amount of slack for wraparound which means if slack is -1 a try to move the mouse
over the screen border will leave the mouse at the border. Values >= 0 mean that the
mouse cursor will wrap to the other end after moving the mouse slack pixels against
the border.
9. int maxx
the resolution of your current terminal in x direction. With the default font, a char is
10 pixels wide, and therefore the overall x screen resolution is 10*80-1.
get ms event() just needs a pointer to a struct ms event. If get ms event() returns -1, an
error occured. On success, it returns 0, and the struct ms event will contain the actual
mouse state.
9.2. MODEM PROGRAMMING 117
Matt Welsh
[email protected] 26 January 1995
10.1 Introduction
Porting UNIX applications to the Linux operating system is remarkably easy. Linux, and
the GNU C library used by it, have been designed with applications portability in mind,
meaning that many applications will compile simply by issuing make. Those which don’t
generally use some obscure feature of a particular implementation, or rely strongly on
undocumented or undefined behavior of, say, a particular system call.
Linux is mostly compliant with IEEE Std 1003.1-1988 (POSIX.1), but has not actually
been certified as such. Similarly, Linux also implements many features found in the SVID
and BSD strains of UNIX, but again does not necessarily adhere to them in all cases. In
general, Linux has been designed to be compatible with other UNIX implementations,
to make applications porting easier, and in a number of instances has improved upon or
corrected behavior found in those implementations.
As an example, the timeout argument passed to the select system call is actually decre-
mented during the poll operation by Linux. Other implementations don’t modify this value
at all, and applications which aren’t expecting this could break when compiled under Linux.
The BSD and SunOS man pages for select warn that in a “future implementation”, the sys-
tem call may modify the timeout pointer. Unfortunately, many applications still assume
that the value will be untouched.
The goal of this paper is to provide an overview of the major issues associated with port-
ing applications to Linux, highlighting the differences between Linux, POSIX.1, SVID, and
BSD in the following areas: signal handling, terminal I/O, process control and information
gathering, and portable conditional compilation.
119
120 CHAPTER 10. PORTING APPLICATIONS TO LINUX
Under unreliable signal semantics, system calls are not restarted automatically when
interrupted by a signal. Therefore, in order for a program to account for all cases, the
program would need to check the value of errno after every system call, and reissue the
system call if its value is EINTR.
Along similar lines, unreliable signal semantics don’t provide an easy way to get an
atomic pause operation (put the process to sleep until a signal arrives). Because of the
unreliable nature of reinstalling signal handlers, there are cases in which a signal can arrive
without the program realizing this.
Under reliable signal semantics, on the other hand, the signal handler remains installed
when called, and the race condition for reinstallation is avoided. Also, certain system calls
can be restarted, and an atomic pause operation is available via the POSIX sigsuspend
function.
-I/usr/include/bsd -lbsd
to the compilation command line. When porting applications using signal, pay close atten-
tion to what assumptions the program makes about use of signal handlers, and modify the
code (or compile with the appropriate definitions) to get the right behavior.
• SIGEMT is not supported; it corresponds to a hardware fault under SVR4 and BSD.
• SIGINFO is not supported; it is used for keyboard information requests under SVR4.
• SIGSYS is not supported; it refers to an invalid system call in SVR4 and BSD. If
you link with libbsd, this signal is redefined to SIGUNUSED.
struct termio {
unsigned short c_iflag; /* Input modes */
unsigned short c_oflag; /* Output modes */
unsigned short c_cflag; /* Control modes */
unsigned short c_lflag; /* Line discipline modes */
char c_line; /* Line discipline */
unsigned char c_cc[NCC]; /* Control characters */
};
Under BSD, the sgtty structure is used with various ioctl calls, such as TIOCGETP,
TIOCSETP, and so forth.
Under POSIX, the termios struct is used, along with various functions defined by
POSIX.1, such as tcsetattr and tcgetattr. The termios structure is identical to
struct termio used by SVR4, but the types are renamed (such as tcflag t instead
of unsigned short), and NCCS is used for the size of the c cc array.
122 CHAPTER 10. PORTING APPLICATIONS TO LINUX
Under Linux, both POSIX.1 termios and SVR4 termio are supported directly by the
kernel. This means that if your program uses either of these methods for accessing terminal
I/O, it should compile directly under Linux. If you’re ever in doubt, it’s easy to modify code
using termio to use termios, using a bit of knowledge of both methods. Hopefully, this
shouldn’t ever be necessary. But, do pay attention if a program attempts to use the c line
field in the termio structure. For nearly all applications, this should be N TTY, and if the
program assumes that some other line discipline is available you might have trouble.
If your program uses the BSD sgtty implementation, you can link against libbsd.a as
described above. This will provide a replacement for ioctl which will resubmit the terminal
I/O requests in terms of the POSIX termios calls used by the kernel. When compiling
such a program, if symbols such as TIOCGETP are undefined, you will need to link against
libbsd.
and executable file, open file descriptors, and so forth. The kernel provides all of this in-
formation on the fly in response to read requests. This implementation is not unlike the
/proc filesystem found in Plan 9, but it does have its drawbacks—for example, for a tool
such as ps to list a table of information on all running processes, many directories must be
traversed and many files opened and read. By comparison, the kvm routines used on other
UNIX systems read kernel data structures directly with only a few system calls.
Obviously, each implementation is so vastly different that porting applications which
use them can prove to be a real task. It should be pointed out that the SVR4 /proc filesystem
is a very different beast than that found in Linux, and they may not be used in the same
context. Arguably, any program which uses the kvm routines or SVR4 /proc filesystem
is not really portable, and those sections of code should be rewritten for each operating
system.
The Linux ptrace call is nearly identical to that found in BSD, but there are a few
differences:
• The requests PTRACE PEEKUSER and PTRACE POKEUSER under BSD are named
PTRACE PEEKUSR and PTRACE POKEUSR, respectively, under Linux.
• Process registers can be set using the PTRACE POKEUSR request with offsets found
in /usr/include/linux/ptrace.h.
• The SunOS requests PTRACE {READ,WRITE}{TEXT,DATA} are not supported,
nor are PTRACE SETACBKPT, PTRACE SETWRBKPT, PTRACE CLRBKPT, or
PTRACE DUMPCORE. These missing requests should only affect a small number
of existing programs.
Linux does not provide the kvm routines for reading the kernel address space from a
user program, but some programs (most notably kmem ps) implement their own versions
of these routines. In general, these are not portable, and any code which uses the kvm
routines is probably depending upon the availability of certain symbols or data structures
in the kernel—not a safe assumption to make. Use of kvm routines should be considered
architecture-specific.
If you define BSD SOURCE yourself, the additional definition FAVOR BSD will be
defined for the library. This will cause BSD behavior for certain things to be selected over
POSIX or SVR4. For example, if FAVOR BSD is defined, setjmp and longjmp will save
and restore the signal mask, and getpgrp will accept a PID argument. Note that you must
still link against libbsd to get BSD-like behavior for the features mentioned earlier in
this paper.
Under Linux, gcc defines a number of macros automatically which you can use in your
program. These are:
• GNUC (major GNU C version, e.g., 2)
• unix
• i386
• linux
• unix
• i386
• linux
• unix
• i386
• linux
to surround Linux-specific code. Using these compile-time macros you can easily adapt
existing code to include or exclude changes necessary to port the program to Linux, Note
that because Linux supports more System V-like features in general, the best code base to
start from with a program written for both System V and BSD is probably the System V
version. Alternately, you can start from the BSD base and link against libbsd.
125
126 CHAPTER 11. SYSTEMCALLS IN ALPHABETICAL ORDER
Abbreviations
131