Demo 1 open.c 创建文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(void)
{
int fd;
fd = open("hello", O_CREAT | O_RDWR, 0644);
printf("fd = %d\n", fd);
close(fd);
return 0;
}
--------------------------------------------------
$ ./a.out
fd = 3
O_CREAT | O_RDWR: 按位或
O_CREAT 八进制:00000100 二进制 001 000 000
O_RDWR 八进制: 00000002 二进制 000 000 010
按位或 前面零省略 001 000 010
注意:
- 如果创建一个新文件的时候没有mode位,系统会自动填充乱码,但不会报错;当打开一个已有文件的时候,不用mode参数.
Demo 2 error.c 出错码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
extern int errno; //外部变量引用
int main(int argc, char *argv[])
{
int fd;
if(argc < 2) {
printf("./a.out filename\n");
return -1;
}
printf("begin errno = %d\n", errno);
fd = open(argv[1], O_RDWR);
if(fd < 0) {
printf("after errno = %d\n", errno);
perror("open");
exit(0);
}
printf("fd = %d\n", fd);
close(fd);
return 0;
}
---------------------------------------
$ ./a.out
./a.out filename
$ ./a.out null
begin errno = 0
after errno = 2
open: No such file or directory
$ ./a.out hello
begin errno = 0
fd = 3
注意:
- errno这个出错时设置相应的值,但是成功是不会置为0,所以要注意:
fd1 = open("hello", O_RDWR);
fd2 = open("world", O_RDWR);
if(fd < 0) {
printf("errno = %d\n", errno);
perror("open");
exit(0);
}
在上面的函数中,如果hello打开失败,world打开成功,打印出来的是errno是打开”hello”出错时对应的值,而不会因为打开”world”成功置为0;所以在使用perror的时候一定要紧跟要你想打印出错原因的函数,中间不要有其他可以设置errno的函数;
Demo 3 输出重定向 re_printf.c
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
int fd;
close(STDOUT_FILENO);
fd = open("hello", O_CREAT|O_RDWR, 0644);
if(fd < 0){
perror("open");
exit(0);
}
printf("Nice to meet you!\n");
fflush(stdout);
close(fd);
return 0;
}
-----------------------------------------
$ ./a.out
$ ls
a.out hello re_printf.c
$ cat hello
Nice to meet you!
程序运行流程:开始把stdout(标准输出)这个文件关掉了,打开了“hello”,那这个时候“hello”的文件描述符就是1,fd = 1;但是printf不知道1这个文件描述符对应的文件改变了,他还是把“Nice to meet you\n”写到了C标准库的缓冲区,然后等缓冲区刷新的时候写到1这个文件描述符对应的磁盘文件当中去
注意:
- fflush的作用:如果不使用fflush,文件缓冲区不刷新,但是close(fd)已经把1这个文件描述符关闭掉了,那数据就写不到1所对应的磁盘文件当中了
- 如果把fflush和close都去掉,那当程序运行到return 0的时候,系统还是会刷新缓冲区,把文件写到磁盘当中去,exit(0)也是可以的,或者main没有返回值,return和什么都不写也都是可以的。
Demo 4 测试文件描述符的最大值 maxfile_no.c
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int fd, i = 3;
char filename[100];
while(1) {
printf("i = %d\n", i);
sprintf(filename, "file%d", i);
fd = open(filename, O_CREAT|O_RDWR, 0644);
if(fd < 0){
perror("open");
exit(-1);
}
i++;
}
}
Demo 5 mycp.c 复制文件
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#define BUFFER_SIZE 4096
void sys_err(const char *buf, int status)
{
perror(buf);
exit(status);
}
int main(int argc, char *argv[])
{
int fdsrc, fddest, len;
char buf[BUFFER_SIZE] = "0";
if(argc < 3) {
printf("./a.out fdsrc fddest\n");
exit(0);
}
fdsrc = open(argv[1], O_RDONLY);
if(fdsrc < 0)
sys_err("open fdsrc", 1);
fddest = open(argv[2], O_CREAT | O_WRONLY, 0664);
if(fddest < 0)
sys_err("open fdsrc", 2);
while((len = read(fdsrc, buf, sizeof(buf))) > 0) {
if(write(fddest, buf, len) < 0)
sys_err("write", 3);
}
if(len < 0)
sys_err("read", 4);
close(fdsrc);
close(fddest);
return 0;
}
-----------------------------
$ ./a.out hello world
注意:在write的时候count一定要写read的返回值,而不是buf的大小;避免如果读的文件长度小于buf的长度的时候,把垃圾数据写入到文件中,read的返回值就是每次读到数据的大小
Demo 6 block.c 阻塞的情况
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
int len;
char buf[10];
len = read(STDIN_FILENO, buf, 10);
write(STDOUT_FILENO, buf, len);
return 0;
}
Demo 7 noblock.c 非阻塞情况
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#define MSG_TRY "try again\n"
int main(void)
{
int len;
char buf[10];
int fd = open("/dev/tty", O_RDWR|O_NONBLOCK);
//int fd = open(STDIN_FILENO, O_RDWR|O_NONBLOCK);
if(fd < 0) {
perror("open /dev/tty");
exit(0);
}
tryagain:
len = read(fd, buf, 10);
if(len < 0) {
if(errno == EAGAIN) {
write(STDOUT_FILENO, MSG_TRY,strlen(MSG_TRY));
sleep(1);
goto tryagain;
}
perror("read");
exit(1);
}
if(write(STDOUT_FILENO, buf, len) < 0) {
perror("write");
exit(2);
}
close(fd);
return 0;
}
注意:
- 这次调用的不是STDIN_FILENO这个文件了,而是“/dev/tty”这个文件,因为我们要给文件重新修改flags属性,但是STDIN_FILENO已经打开过了,没有办法在修改flags,“/dev/tty”这个文件是当前终端,也可以接收终端的输入;
- 当文件是非阻塞文件的时候,去读这个文件,读不到值的出错码就是EAGAIN,所以当errno等于EAGAIN的时候,就需要再次去读这个文件,知道读到内容或出现其他出错码
- 阻塞和非阻塞属性是跟文件属性绑定的。
Demo 7 noblock_timeout.c 非阻塞读终端加上等待超时
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#define MSG_TRY "try again\n"
#define MSG_TIMEOUT "timeout\n"
void sys_err(const char *buf, int status)
{
perror(buf);
exit(status);
}
int main(void)
{
int fd, i, len;
char buf[10];
fd = open("/dev/tty", O_RDWR|O_NONBLOCK);
if(fd < 0)
sys_err("open", 1);
for(i = 0; i < 5; i++) {
len = read(fd, buf, 10);
if(len >= 0)
break;
if(errno == EAGAIN) {
write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));
sleep(1);
continue;
}
sys_err("read", 2);
}
if(i == 5) {
write(STDOUT_FILENO, MSG_TIMEOUT, strlen(MSG_TIMEOUT));
}
else
write(STDOUT_FILENO, buf, len);
close(fd);
return 0;
}
注意:
- 一定要把判断len>=0;放到判断errno == EAGAIN前面;因为errno的值不会因为read返回一个大于0的值而重置,会一直是上一次的错误值;因为errno的值不会因为read返回一个大于0的值而重置,会一直是上一次的错误值;因为errno的值不会因为read返回一个大于0的值而重置;
重要事情说三遍!!!
Demo 8 lseek.c 指针调用
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER "hello world\n"
void sys_err(const char *buf, int status)
{
perror(buf);
exit(status);
}
int main(void)
{
int fd, len;
char buf[100];
fd = open("hello", O_CREAT|O_RDWR, 0644); //打开文件
if(fd < 0)
sys_err("open", 0);
if(write(fd, BUFFER, strlen(BUFFER)) < 0) //把数据写入文件
sys_err("write1", 1);
if(lseek(fd, 0, SEEK_SET) < 0) //把文件指针重新直到文件开头
sys_err("lseek", 0);
len = read(fd, buf, sizeof(buf)); //读文件
if(len < 0)
sys_err("read", 2);
if(write(STDOUT_FILENO, buf, len) < 0)
sys_err("write2", 3);
return 0;
}
Demo 9 ex_lseek.c 生成大文件
#include <sys/stat.h>
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int fd;
if(argc < 2) {
printf("./a.out file");
return 0;
}
fd = open(argv[1], O_CREAT|O_RDWR, 0644);
if(fd < 0) {
perror("open");
exit(0);
}
lseek(fd, 1024*10, SEEK_SET); // 向后值1yi024*10个Byte
write(fd, "a", 1); //然后写一个数据
close(fd);
return 0;
}
注意:
- 指针向后指完之后,一定要写入一个数据,这样文件才会保存成相应大小,如果不写数据,文件还是为空。
Demo 10 ex2_lseek.c 得到文件的大小
#include <sys/stat.h>
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int fd, len;
if(argc < 2) {
printf("./a.out file");
return 0;
}
fd = open(argv[1], O_CREAT|O_RDWR, 0644);
if(fd < 0) {
perror("open");
exit(0);
}
len = lseek(fd, 0, SEEK_END);
printf("len = %d\n", len);
close(fd);
return 0;
}
lseek的返回值是移动后指针距离文件开头多少个Byte,由此可以得出文件的大小
Demo 10 fcntl.c 用fcntl重新实现非阻塞
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#define MSG_TRY "try again\n"
#define MSG_TIMEOUT "timeout\n"
extern int errno;
void sys_err(const char *buf, int status)
{
perror(buf);
exit(status);
}
int main(void)
{
int fd, len, flags, i;
char buf[100];
flags = fcntl(STDIN_FILENO, F_GETFL);
flags |= O_NONBLOCK;
if(fcntl(STDIN_FILENO, F_SETFL, flags) < 0)
sys_err("fcntl", 1);
for (i = 0; i < 5; i++) {
len = read(STDIN_FILENO, buf, sizeof(buf));
if(len >= 0)
break;
if(errno == EAGAIN) {
write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));
sleep(1);
continue;
}
sys_err("read", 1);
}
if(i == 5)
write(STDOUT_FILENO, MSG_TIMEOUT, strlen(MSG_TIMEOUT));
else
write(STDOUT_FILENO, buf, len);
return 0;
}
注意:
- 读取文件的flags用F_GETFL,写入用F_SETFL,修改flags用位或操作