File I/O
Syscalls & StdLib
25-03-10 CMPT 201 Slides 9.1 © Dr. B. Fraser 1
Topics
●
What syscalls can we use to access files (like write())?
●
Why are there stdio functions, like fprintf(), in addition to
write()?
●
Why do writes sometimes not happen right away?
25-03-10 2
Basic I/O
System Calls
25-03-10 3
File Offset This means the offset is a number (like 42) that tells the OS:
“Start reading/writing from byte 42 in the file.”
●
File offset X
.. points
a points to
that let in the fils who e a
– Offset is where both read() and write() occur (one pointer). n
6
– Move it to an arbitrary position using lseek()
continu
– read() and write() automatically increments the offset:
.. subsequent calls um zet
i
i
next deta
lseek(fd, 0, SEEK_SET); // go to beginning of file
lseek(fd, 100, SEEK_CUR); // move 100 bytes ahead from current
So if you read 10 bytes, the offset moves forward 10 bytes.
lseek(fd, -10, SEEK_END); // go 10 bytes back from end
• Same for writing — it continues from where the last write ended.
25-03-10 4
IO Syscalls
●
5 basic system calls for file I/O
– open
-
– read -
– write -
-
– close
– fcntl - File control
-
25-03-10 5
open()
mode sits
●
open() receives 2 or 3 parameters: file permission
– int open(const char *pathname, int flags);
/ 0043 Cow-rof
to
– int open(const char *pathname, int flags, mode_t mode);
flags: ..
●
– Must be one of: O_RDONLY, O_WRONLY, or O_RDWR
open for Read only, write only, read/write
Flags can also be bitwise-or'd with others such as: to specifyaddition
●
our
– O_RDWR | O_APPEND: All write actions happen at end of file
-
- – O_WRONLF | O_CREAT: If file does not exist, then create it.
- – O_RDWR | O_TMPFILE: Create an unnamed temporary file
~ – O_WRONLY | O_TRUNC: Truncate file when opened for writing
●
Bitwise-or sets individual bits in a bit vector,
e.g., O_RDWR | O_CREAT
25-03-10 ↓ 6
00000001 (O_WRONLY) rumpe
| 00000100 (O_CREAT)
----------- int fd = open("[Link]", O_WRONLY | O_CREAT | O_TRUNC, 0644);
00000101 (Combined flags)
empe
open() cont int fd = open("[Link]", O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
●
Recall:
↑
– int open(const char *pathname, int flags);
– int open(const char *pathname, int flags, mode_t mode);
●
mode
– .. Sets the file permission when creating file
(flags O_CREAT or O_TMPFILE)
●
S_IRWXU: User can read/write/execute
●
S_IRUSR | S_IWUSR: .. User can read/write
●
Return Value a hanelle is just an
– .. File descriptor
:
abstraction or
reference
a handle for the file to read and write: to a resource him a
it’s a small non-negative integer (int) fete or window
– It could change every time you open the file.
Think of It Like This:
25-03-10 Imagine going to a cloakroom and giving them your jacket. They give you a token (handle). 7
That token doesn’t hold the jacket — it’s just a reference that lets you ask for your jacket later.
Common reserved FDs:
Similarly: • 0: Standard input (stdin)
• File = actual resource (like your jacket) • 1: Standard output (stdout)
• Handle (FD) = token that lets you access and manipulate it • 2: Standard error (stderr)
descripts
filte
to an
file
write()
M
ofn to a buffer
comp array
- to store
ssize_t write(int fd, const void *buf, size_t count); - data)
b
●
write() writes buf to a file descriptor and no
of
.. returns the no- of bytes written bytes you
wit
●
man 2 write important points: wart to
– .. no be bes than count because
of bytes written may
insufficient space on disk
-
●
●
call interrupted by a signal handler
– Writing takes place at the file offset, and
offset is incremented by the number of bytes actually written.
&
So will It sends the data from memory (buf) to the file/device identified by fd.
25-03-10 8
write(fd, buf, 10); // writes 10 bytes at offset 0
write(fd, buf2, 5); // writes 5 bytes starting at offset 10
read() Muffer
detta
pers
will Ne Stor
-
ssize_t read(int fd, void *buf, size_t count);
●
read() reads from a file descriptor and
.. returns the no-of bytes each
●
`man 2 read` important points:
– read operation commences
-
at the file offset,
which is incremented
- by the number of bytes read.
=
– If file offset is at or past the end of file,
.. no bytesarnotread & read() returns O
– Not an error if # bytes read < # bytes requested
-
- fewer bytes available right now (near end-of-file or
●
reading pipe/terminal)
●
-
-or read() was interrupted by a signal
25-03-10 9
read 1) -
It fills your buffer (buf) with data from the file/device linked to fd.
close()
it can be
releases it so
int close(int fd); - resed
- closes the file descriptor.
–
- Writes any remaining buffered data to file.
–
25-03-10 10
lseek() How many
lyte
to move
From where
to move
- -
off_t lseek(int fd, off_t offset, int whence);
●
Manually adjust the file offset.
– man lseek
●
whence to adjust the
which location he was
– .. from
fil offset
●
SEEK_SET: Start of file
●
SEEK_CUR: Current offset
●
SEEK_END: End of file (1st byte after end of file)
– offset is always added.
– Can seek past end of file:
next write will extend file with 0’s.
25-03-10 11
ABCD: lseek
●
Suppose a file has 6 bytes (i.e., EOF is at 6) and
the current file offset is 4: in ~
CEOF]
Index 0 1 2 3 4 5 6 7 ...
Hello !
Content H e l l o ! <EOF> --
↑
↑
-
1
SEEKEND
SEEK -
SET
– Note: <EOF> does not actually appear in the file.
●
What character is read when doing a read() of 1 byte after
the following calls (in isolation)?
HI
a) l
1) lseek(fd, SEEK_SET,0
4)
A
b) o
2) lseek(fd,-ISEEK_CUR,1)
-1)
-
c) !
3) lseek(fd, SEEK_END, -1)
() d) none
25-03-10 12
fcntl() file descriptor operation
code optional
argument
M - -
int fcntl(int fd, int op, ... /* arg */ );
●
File control
– man fcntl
– It can do many things, such as
●
modify flags and mode used when file was opened:
op = F_SETFL (set flag)
open() sets flags only once.
• fcntl() gives you flexibility at runtime.
25-03-10 13
Activity: Files
●
Write a program that:
– Creates a new file named “tmp” in current folder
– Writes X bytes to a file
●
Write a string like “Hello World!”; your choice!
– Moves the file offset backward by X/2 bytes
– Reads and prints out from the offset to EOF
– Closes the file
25-03-10 Solution: lseek_half.c 14
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
int main() {
// O_TRUNC If the file already exists, this flag will: Erase all existing
// content (truncate it to size 0) So the file becomes an empty file, as if it
// was just created.
int fd = open("tmp", O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd == -1) {
perror("open");
;
return 1;
}
char *msg = "Hello World!";
if (write(fd, msg, strlen(msg)) != strlen(msg)) {
output
perror("write");
close(fd); ./write_half
return 1; World!%
}
// 3. Move file offset backward by X/2
off_t offset = lseek(fd, (-1 * (strlen(msg) / 2)), SEEK_END);
if (offset == -1) {
perror("lseek");
close(fd);
return 1;
}
// 4. Read from the offset to EOF
const int MAX_SIZE = 1024;
char buffer[MAX_SIZE]; // Should be enough for this example
int size = read(fd, buffer, MAX_SIZE - 1);
if (size == -1) {
perror("read");
close(fd);
return 1;
}
buffer[size] = '\0'; // Null-terminate the string
write(STDOUT_FILENO, buffer, size);
// 6. Close the file
close(fd);
return 0;
}
Buffered I/O
25-03-10 15
Categories of File Functions
functions directly
communicies
-low level
●
Syscalls with the OS Kernel
– I/O functions that are system calls:
write(), read(), etc. (previous slides)
●
.. standard library (stdio) functions but
Y – All I/O functions that start with f: -not syscall
-
but on
night fprintf(), fscanf(), fputs(), fgets(), fput(), fget(), etc. them
wee – The same functions without f:
functions printf(), scanf(), puts(), gets(), etc.
●
What’s the difference?
– Let's look at write(), fprintf(), and printf()
25-03-10 16
write() vs fprintf()
●
write() directly sends data to the kernel,
fprintf() .. manages luffer in memory
a
to
& writes to the lupfer & occasionally fluchs write)
fels using
-
actual
– Uses write() under the hood. The
– Because of this,.. bbrintf is called buffred I o
– fprintf() generates fewer syscalls, which gives better
performance (less overhead).
●
File Descriptor vs FILE stream
– Syscalls like write() take..
ssize_t write(int fd, const void *buf, size_t count);
– Library functions like fprintf() take..
Y int fprintf(FILE *stream, const char *format, ...);
Zahl
FILE
Stream
25-03-10 17
Stream vs File Descriptor
●
What is a Stream
FILE *stream
– Convenient wrapper around a file descriptor.
Used by the stdio functions.
– Think of this as
.. a fils description plus Muffer
a
teaching it
up
●
Converting Stream <==> File Descriptor
– You can get the file stream from a file descriptor with:
.. fdopen()
– You can get the file descriptor from a file stream with:
..
filno()
25-03-10 18
Relationship
-(in
User program has data
●
User Program
memory) to write.
-It calls library function.
●
fprintf() fscanf() ...
stdlib
functions
User Space
Data written into
●
stdio buffer
-library’s buffer. (memory)
Later executes syscall
●
I/O
to write
-
Kernel
Space
write() read() ... system
to kernel. calls
Kernel will
-
●
write to disk. Disk
25-03-10 19
Activity: Kernel Write
●
Write a program that will:
– open() a file named tmp,
– write() a string (your choice) to tmp,
– infinite loop that calls sleep() for 30 seconds each loop.
●
Experiment
– Run it in the background
– Did it write to the file tmp. Check with cat.
(It should.)
●
When done, delete tmp from the command line.
Even though the process is still running in the background, the file was written immediately because write() is
unbuffered (it goes directly to the disk).
25-03-10 20
/
cat kernel_write.c
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
int main() {
int fd = open("tmp", O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
char *msg = "Hello World!";
int res = write(fd, msg, strlen(msg));
while (1) {
sleep(30);
}
}
~/units/05-lectures/09› cat tmp
Hello World!%
~/units/05-lectures/09› ./lib_print&
[2] 31449
~/units/05-lectures/09› cat lib_print.c
#include <stdio.h>
#include <unistd.h>
int main() {
FILE *fp = fopen("tmp", "w");
fprintf(fp, "Hello from stdio!\n"); then
>
-
// fclose(fp); I rnabled
Y
see
while (1) { we can
sleep(30);
} meg on
return 0; imp
}
~/units/05-lectures/09› cat tmp
~/units/05-lectures/09›
Activity: Library print
●
Write another program that will
– fopen() a file named tmp,
– fprintf() a string to tmp,
– infinite loop that calls sleep() for 30 seconds each iteration.
●
Run It
– Run it in the background
– Did it write to the file tmp. Check with cat.
(It should not!)
Even though the process is still running in the background, the file
was written immediately because write() is unbuffered (it goes
directly to the disk).
●
Experiment
– Change to close file after writing. Repeat running it.
It should write to file.
ver
ffluch) or fulose 1)
25-03-10 21
Standard library I/O is buffered. Always flush or close to ensure data is saved.
Buffering
●
Explain Behaviour
– Why did fprintf() not write to the file when the file is left open?
– Why did it write when we closed?
– Answer:.. Changes were differed
●
fflush() immediately sends the buffered data to the kernel.
– Calling setbuf() with NULL as the buffer automatically does
flushing.--
Y
-
– Read `man setbuf` for more details. vicl of
get
uuffering
25-03-10 22
Activity: fflush()
●
Change Previous Program with fprintf():
– Add fflush() call after printing
●
Run it and see if it writes to tmp. (It should.)
25-03-10 23
Kernel Buffering
User Program
●
Kernel has
read/write buffers
-
-
fprintf() fscanf() ... stdlib
too.
User Space
functions
-
[
●
Even kernel does stdio buffer
not immediately (memory)
write to disk.
Kernel Space I/O
write() read() ...
system
calls
Kernel buffer
(memory)
Disk
25-03-10 24
Kernel Buffering
●
Can force kernel to flush buffer with*
fsync()
–
-
-
Using O_SYNC when with open() automatically does fsync().
●
Parallel between user buffering and kernel buffering
–
-
fflush() and fsync(): both flush their buffer.
– setbuf() with a NULL buffer and O_SYNC:
.. both
automatically perform
buffering
no
.
25-03-10 25
Blocking vs Non-Blocking I/O
●
Blocking call
.. doesn't return until the operation can be done .
– E.g., a blocking read() call doesn't return until there's
something to read.
●
Non-blocking call
– O_NONBLOCK flag
(either with open() or with fcntl() & F_SETFL)
– If an operation can't be done immediately, then
.. the all returns error an , typically
EAGAIN .
25-03-10 26
Imagine calling someone and waiting for them to answer — you’re blocked until they pick up. >
-
Blocking
Non-Blocking
You knock once and walk away if there’s no answer — no waiting.
>
-
Summary
●
5 Syscalls for File Access
– int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
– ssize_t write(int fd, const void *buf, size_t count);
– ssize_t read(int fd, void *buf, size_t count);
– int close();
– off_t lseek(int fd, off_t offset, int whence);
●
Syscalls vs Library functions
– write() vs fprintf()
– Non-buffered vs buffered IO
– File descriptor (int) vs stream (FILE*)
25-03-10 27