I/O Systems
I/O Hardware
· Incredible variety of I/O devices
· Common concepts
o Port
o Bus
o Controller
· I/O instructions control devices
· Devices have addresses, used by
o Direct I/O instructions
o Memory-mapped I/O
A Typical PC Bus Structure
Device I/O Port Locations on PCs (partial)
Polling
· Determines state of device
o ready
o busy
o Error
· Busy-wait cycle to wait for I/O from device
Interrupts
· CPU Interrupt- triggered by I/O device
· Interrupt handler receives interrupts
· Maskable to ignore or delay some interrupts
· Interrupt vector to dispatch interrupt to correct handler
o Based on priority
o Some nonmaskable
· Interrupt mechanism also used for exceptions
Direct Memory Access
· Used to avoid programmed I/O for large data movement
· Requires DMA controller
· Bypasses CPU to transfer data directly between I/O device and memory
Application I/O Interface
· I/O system calls encapsulate device behaviors in generic classes
· Device-driver layer hides differences among I/O controllers from kernel
· Devices vary in many dimensions:
§ Character-stream or block
§ Sequential or random-access
§ Sharable or dedicated
§ Speed of operation
§ read-write, read only, or write only
A Kernel I/O Structure
Block and Character Devices
· Block devices include disk drives
§ Commands include read(), write(), seek()
§ Raw I/O or file-system access
§ Memory-mapped file access possible
· Character devices include keyboards, mice, serial ports
§ Commands include get(), put()
· Libraries layered on top allow line editing.
Network Devices
· Varying enough from block and character to have own interface
· Unix and Windows include socket interface
Separates network protocol from network operation.
vClocks and Timers
· Provide current time, timer
· Programmable interval timer used for timings, periodic interrupts.
ü Example1: timeout.c
main(int argc, char
*argv[])
{
char buf[BUFSIZ];
int timeout;
timeout = atoi(argv[1]);
while (1) {
signal(SIGALRM, alarmhandler);
alarm(timeout);
printf("Enter a string (withen %d seconds): ",
timeout);
if ( gets(buf)!= NULL) {
alarm(0);
printf("got %s\n", buf);
}
}
}
alarmhandler(int
sig)
{
printf("Timeout, Hurry Up!\n");
}
Usage Example:
% timeout 3
ü Example2: ChatClientAlarm.c
int
main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
struct hostent *hp, *gethostbyname();
char buffer[512];
int nread;
sockfd = socket(AF_INET, SOCK_STREAM,
0); bzero(&servaddr, sizeof(servaddr));
hp = gethostbyname(argv[1]);
bcopy(hp->h_addr, &(servaddr.sin_addr.s_addr), hp->h_length); servaddr.sin_family
= AF_INET;
servaddr.sin_port = htons(atoi(argv[2]));
if (connect(sockfd, (SA *) &
servaddr, sizeof(servaddr)) < 0) {
perror("connect error");
exit(-1);
}
AlarmChat
(sockfd);
}
AlarmChat(int sockfd)
{
int nread;
char buffer[512];
siginterrupt(SIGALRM, 1);
signal(SIGALRM,
handle_ALARM);
alarm(1);
for (;;) {
nread = read(0, buffer, 512);
if (nread == 0)
exit(0);
write(sockfd, buffer, nread);
nread = read(sockfd, buffer, 512);
if (nread == 0)
exit(0);
write(1, buffer, nread);
}
}
handle_ALARM(int signo)
{
signal(SIGALRM, handle_ALARM);
alarm(1);
}
Usage Example:
% ChatServerSelect 10990 &
% ChatClientAlarm localhost 10990 1
vBlocking and Nonblocking I/O
Ø Blocking - process suspended until I/O completed
Ø Nonblocking - Returns quickly with count of bytes read or written
Ø Asynchronous - I/O subsystem signals process when I/O completed
Ø Blocking:
ü Example 3: block.c
int
main(int argc, char *argv[])
{
char buffer[512];
int nread;
while (1) {
printf("Enter a string: ");
nread = read(0, buffer, 512);
if (nread > 0) {
printf("\ngot: ");
write(1, buffer, nread);
}
}
}
Usage Example:
% block
Ø Nonblocking:
int
main(int argc, char *argv[])
{
char buffer[512];
int sleeptime;
int nread;
int val;
sleeptime = atoi(argv[1]);
val = fcntl(0, F_GETFL, 0);
fcntl(0, F_SETFL, val | O_NONBLOCK);
while (1) {
printf("Enter a string: ");
nread = read(0, buffer, 512);
if (nread == -1) {
if (errno == EWOULDBLOCK) {
printf("WouldBlock, sleeping: %d\n", sleeptime);
sleep(sleeptime);
} else {
perror("read error");
exit(-1);
}
} else if (nread == 0) {
printf("EOF\n");
exit(0);
} else {
printf("\nGot: ");
write(1, buffer, nread);
}
}
}
Usage Example:
% nonblockRead 3
char
buffer[128];
void fillbuf();
int
main(int argc, char *argv[])
{
int sleeptime;
int nwrite;
int val;
int Total = 0;
sleeptime = atoi(argv[1]);
fillbuf();
val = fcntl(1, F_GETFL, 0);
fcntl(1, F_SETFL, val | O_NONBLOCK);
while (1) {
nwrite = write(1, buffer, sizeof(buffer));
printf("..%d..", nwrite);
if (nwrite)
Total += nwrite;
if (nwrite == -1) {
if (errno == EWOULDBLOCK) {
sleep(sleeptime);
printf("\nwrote %d, slept %d\n", Total, sleeptime);
Total = 0;
sleep(sleeptime);
}
}
}
}
fillbuf()
{
int i;
for (i = 0; i <= sizeof(buffer); i++)
buffer[i] = NULL;
}
Usage Example:
% nonblockWrite 3
ü Example 6: ChatClientNonBlock.c
int
main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
struct hostent *hp, *gethostbyname();
char buffer[512];
int nread;
sockfd = socket(AF_INET, SOCK_STREAM,
0);
bzero(&servaddr, sizeof(servaddr));
hp = gethostbyname(argv[1]);
bcopy(hp->h_addr, &(servaddr.sin_addr.s_addr), hp->h_length);
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(atoi(argv[2]));
if (connect(sockfd, (SA *) &
servaddr, sizeof(servaddr)) < 0) {
perror("connect error");
exit(-1);
}
ChatNonBlock
(sockfd);
}
ChatNonBlock(int sockfd)
{
int nread;
char buffer[512];
int val;
val = fcntl (sockfd, F_GETFL, 0);
fcntl (sockfd, F_SETFL, val |
O_NONBLOCK);
val = fcntl (0, F_GETFL, 0);
fcntl (0, F_SETFL, val | O_NONBLOCK);
for (;;) {
nread = read(0, buffer, 512);
if (nread == 0)
exit(0);
write(sockfd, buffer, nread);
nread = read(sockfd, buffer, 512);
if (nread == 0)
exit(0);
write(1, buffer, nread);
sleep(1);
}
}
Usage Example:
% ChatServerSelect 10990 &
% ChatClientNonBlock localhost 10990
ü Example 7: ChatClientSelect.c
int
main(int argc, char **argv)
{
int sockfd;
int pid;
struct sockaddr_in servaddr;
struct hostent *hp, *gethostbyname();
sockfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
hp = gethostbyname(argv[1]);
bcopy(hp->h_addr, &(servaddr.sin_addr.s_addr), hp->h_length);
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(atoi(argv[2]));
if (connect(sockfd, (SA *) & servaddr,
sizeof(servaddr)) < 0) {
perror("connect error");
exit(-1);
}
AsynchIO(0,
sockfd);
}
AsynchIO(int fd1, int fd2)
{
fd_set readset;
char buffer[512];
int nread;
int i;
for (;;) {
FD_SET(fd1, &readset);
FD_SET(fd2, &readset);
bzero(buffer, sizeof(buffer));
select(MAXFD, &readset, NULL, NULL,
NULL);
if (FD_ISSET(fd1, &readset)) {
nread = read(fd1, buffer, 512);
if (nread == 0) exit(0);
if (write(fd2, buffer, nread) == -1) exit(0);
}
if (FD_ISSET(fd2, &readset)) {
nread = read(fd2, buffer, 512);
if (nread == 0) exit(0);
if (write(fd1, buffer, nread) == -1) exit(0);
}
}
}
Usage Example:
% ChatServerSelect 10990 &
% ChatClientSelect localhost 10990
ü Example 8: ChatServerSelect.c
int main(int argc, char **argv)
{
int i;
strcpy(ServerPort, argv[1]);
TCPServer.sin_family = AF_INET;
TCPServer.sin_addr.s_addr = htonl(INADDR_ANY);
TCPServer.sin_port = htons(atoi(ServerPort));
for (i = 0; i <= MAXTCP; i++) connfd[i] = -1;
TCPSocket = socket(AF_INET, SOCK_STREAM, 0);
if (bind(TCPSocket, (SA *) & TCPServer,
sizeof(TCPServer)) < 0) {
close(TCPSocket);
perror("binding TCPSocket socket");
exit(-1);
}
listen(TCPSocket, 4);
FD_ZERO(&allset);
FD_SET(TCPSocket, &allset);
for (;;) {
readset = allset;
select(MAXCLIENTS, &readset, NULL,
NULL, NULL);
if (FD_ISSET(TCPSocket, &readset))
{
for (i = 0; i <= MAXTCP; i++) {
if (connfd[i] == -1) {
connfd[i] = accept(TCPSocket, 0,
0);
FD_SET(connfd[i], &allset);
break;
}
}
}
for (i = 0; i <= MAXTCP; i++) {
if ((connfd[i]) == -1) continue;
if (FD_ISSET(connfd[i], &readset))
handleTCP(i);
}
}
}
handleTCP(int
index)
{
int nread;
char buffer[BUFSIZE];
int i;
int myIndex = index;
bzero(buffer, sizeof(buffer));
if ((nread = read(connfd[myIndex],
buffer, sizeof(buffer))) < 0) {
if (errno == EINTR) return;
else perror("read connfd");
}
if (nread == 0) {
connfd[myIndex] = -1;
return;
}
for (i = 0; i <= MAXTCP; i++)
if (connfd[i] != -1)
write(connfd[i], buffer,
nread);
}
v Error Handling
System calls return an error number or code when I/O request fails.
In Unix we use the C-lib function perror to translate the error number into text message.
ü Example 9: error.c
main(int
argc, char *argv[])
{
int fd;
char *fname;
fname = argv[1];
if ((fd = open(fname, O_RDWR)) == -1)
{
printf("errno: %d\n",
errno);
perror(fname);
exit(-1);
} else
printf( "fd is %d\n", fd);
printf("opened %s\n", fname);
}
Usage Example:
% error /tmp/testfile
I/O Protection
User process may accidentally or purposefully attempt to disrupt normal operation via illegal I/O instructions
· All I/O instructions defined to be privileged
· I/O must be performed via system calls
Kernel Data Structures
Kernel keeps state info for I/O components, including open file tables, network connections, and character device state
UNIX I/O Kernel Structure
Example 10: fdTypeTest.c
Usage Example:
% fdTypeTest 101990 &
% telnet localhost 10990