目录
前言
fopen,fclose,fread,fwrite,fseek;是C语言标准库的,方便移植;
文件的创建,打开,关闭,读,写,光标:
creat,open,close,read,write,lseek;
什么是文件描述符?
文件描述符是Unix特有的,其为一个非负整数,取值范围是0-NR_OPEN,对于Linux,NR_OPEN=255;也就是每个程序只能打开256 个文件;当使用open或者creat打开或者创建一个文件的时候,如果成功则将返回一个文件描述符,在进行读写操作时(read/write),文件描述符作为参数传递给(read/write);
文件描述符0代表标准输入文件,一般就是键盘,1代表标准输出文件,一般就是显示器,2代表标准错误输出,一般也是显示器;
1, open()
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int open(const char *pathname,int flags);
int open (const char *pathname,int flags,mode_t mode);
pathname,含路径的文件名
flags文件打开方式:
这三种只能三选一。
O_SYNC,O_NONBLOCK,O_NDELAY同步异步阻塞非阻塞等等(待解决)
当使用O_CREAT时,才使用参数mode,用来说明新文件的权限(权限实际上是mode||umask),mode的取值:
2,creat()
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int creat(const char *pathname , mode_t mode);
创建或者覆盖原有文件
pathname,含路径的文件名
mode与open的相同
3, close()
#include<unistd.h>
int close(int fd);
fd:文件描述符。
调用成功返回0,失败返回-1;
不保证数据全部存回硬盘。
4,自己写创建文件的命令(可以附加上权限设置,期待后续)
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
int main(int argc,char ** argv)
{
int fd;
char *path;
if(argc<2)
{
printf("%s<mode num><target file>\n",argv[0]);
_exit(0);
}
path=argv[1];
if((fd=open(path,O_CREAT|O_EXCL,S_IRUSR|S_IWUSR))==-1)
{
perror("open");
_exit(1);
}
else
{
printf("creat a file success");
}
close(fd);
return 0;
}
5, read()
#include<unistd.h>
ssize_t read(int fd, void *buf,size_t count);
从文件描述符所指向的文件中读取count个字符到buf所指向的缓存中。
当count=0,read不读取数据,只返回0;
读取成功返回读取到的字节数,与count作比较,如果返回字节数目比count少,则是读到了文件末尾或者读取过程中信号被中断了等等。读取失败则返回-1;存到errno中;
文件读写指针随着读写移动;
6, write()
#include<unistd.h>
ssize_t write(int fd, void *buf,size_t count);
将buf指向的缓冲区中的count个字节写到fd所指示的文件当中。
返回写入的字节数
错误返回-1,存到errno中;
7, lseek()
#include<sys/types.h>
#include<unistd.h>
off_t lseek(int fildes,off_t offset,int whence);
lseek()用来控制该文件的读写文件;
参数fildes为已经打开的文件的文件描述符;
参数offset为根据whence来移动读写位置的位移数;
参数whence:
lseek允许文件指针的值设置到文件结束符(EOF)之后,但并不会改变文件大小,在此位置写入数据之后,此段数据与之前的(EOF)之间是数据0;
有些设备文件不能使用lseek,比如Linux的tty文件,用lseek会返回错误代码ESPIPE;
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
//报错函数
void my_err(const char *err_string,int line)
{
fprintf(stderr,"line:%d",line);
perror(err_string);
_exit(1);
}
//读数据函数
int my_read(int fd)
{
int len;
int ret;
int i;
char read_buf[64];
//指针移到最后
if(lseek(fd,0,SEEK_END)==-1)
{
my_err("lseek",__LINE__);
}
//最后位置到文件开始位置字节数
if((len=lseek(fd,0,SEEK_CUR))==-1)
{
my_err("lseek",__LINE__);
}
//指针回到文件开始处,下一步方便读取数据
if(lseek(fd,0,SEEK_SET)==-1)
{
my_err("lseek",__LINE__);
}
printf("len:%d\n",len);
//读取数据,返回读取字节数
if((ret=read(fd,read_buf,len))<0)
{
my_err("read",__LINE__);
}
//打印
for(i=0;i<len;i++)
{
printf("%c",read_buf[i]);
}
printf("\n");
return ret;
}
int main()
{
int fd;
char write_buf[32]="Hello World";
char write_buf2[32]="Q";
if((fd=open("test.txt",O_CREAT|O_RDWR|O_TRUNC,S_IRUSR|S_IWUSR))==-1)
{
my_err("open",__LINE__);
}
else
{
printf("creat the file success\n");
}
if(write(fd,write_buf,strlen(write_buf))!=strlen(write_buf))
{
my_err("write",__LINE__);
}
printf("---------------------------------\n");
if(lseek(fd,10,SEEK_END)==-1)
{
my_err("lseek",__LINE__);
}
if(write(fd,write_buf2,strlen(write_buf2))!=strlen(write_buf2))
{
my_err("write",__LINE__);
}
my_read(fd);
close(fd);
return 0;
}
__LINE__内置宏,提示错误位置
类似还有__TIME__,__FUNCTION__,__FILE__等等;
8, dup、dup2
都是用于复制文件描述符(意义不同于平时的复制文本)
#include<unistd.h>
int dup(int oldfd);
int dup2(int oldfd,int newfd);
dup成功返回最小尚未被使用的文件描述符,失败返回-1;新文件描述符等同于旧文件描述符;
dup2返回指定的文件描述符newfd;newfd被占用时先释放newfd,再复制,当oldfd=newfd时,不释放。成功返回newfd,失败返回-1。
输入输出重定向?
9, fcntl
对已经打开的文件描述符进行操作改变其各种属性
#include<unistd.h>
#include<funtl.h>
int fcntl(int fd,int cmd);
int fcntl(int fd,int cmd,long arg);
int fcntl(int fd,int cmd,struct flock *lock);
参数cmd:
(a)F_DUPFD。同dup
(b)F_GETFD。获取文件描述符的close-on-exec标志,成功返回标志值,若其最后一位为0,则表示执行exec相关函数之后文件描述符韩式打开的,不然则是已经该关闭文件描述符;失败返回-1。
(c)F_SETFD。将close-on-exec标志为arg的最后一位,成功返回0,失败返回-1;
(d)F_GETFD。获取文件打开方式。成功返回标志值失败返回-1。见open的参数flags。
(e)F_SETFD。设置打开方式为arg的指定方式(O_APPEND,O_NONBLOCK与O_ASYNC之一)。见open参数flags。
例子:
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
void my_err(const char *err_string,int line)
{
fprintf(stderr,"line:%d",line);
perror(err_string);
_exit(1);
}
int main()
{
int ret;
int access_mode;
int fd;
if((fd=open("test.txt",O_RDONLY))==-1)
{
my_err("open",__LINE__);
}
if((ret=fcntl(fd,F_GETFL,0))<0)
{
my_err("fcntl",__LINE__);
}
access_mode=ret&O_ACCMODE;
if(access_mode==O_RDONLY)
{
printf("test.txt access mode:read only");
}
else if(access_mode==O_WRONLY)
{
printf("test.txt access mode:read only");
}
else if(access_mode==O_RDWR)
{
printf("test.txt access mode:read + write");
}
if(ret&O_APPEND)
{
printf(" ,append");
}
if(ret&O_NONBLOCK)
{
printf(" ,nonblock");
}
if(ret&O_SYNC)
{
printf(" ,sync");
}
printf("\n");
return 0;
}
测试结果:
(f)F_GETLK。参数lock指向一个希望设置的锁的结构体,如果目标位置的锁能够被设置,那么修改结构体的i_type为U_UNLCK,然后返回。如果目标位置有锁且冲突,那么返回一个冲突的锁的结构。
(g)F_SETLK。设置或者释放锁。i_type为F_RDLCK或者F_WRLCK时,指定区域设置锁,当i_type时释放锁。当锁被其他进程占用时,返回-1,且设置errno为EACCES或者EAGAIN。注意文件的打开方式要相应,例如设置读锁要以可读方式打开
(h)F_SETLKW。功能与F_SETLK相似,不过当有其他锁存在而被阻值时,会等待冲突的锁被释放
(i)F_GETOWN
(j)F_SETOWN
(k)F_SETSIG
(l)F_GETSIG
10, ioctl
用来控制设备
#include<sys/ioctl.h>
int ioctl(int fd,int request,...);
第三个参数一般为char *argp,其随reques不同而不同,request决定argp是向ioctl传递数据还是获取数据。
#include<stdio.h>
#include<unistd.h>
#include<sys/ioctl.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<net/if.h>
#include<string.h>
unsigned char g_macaddr[6];
unsigned int g_subnetmask;
unsigned int g_ipaddr;
unsigned int g_broadcast_ipaddr;
void init_net(void)
{
int i;
int sock;
struct sockaddr_in sin;
struct ifreq ifr;
char g_eth_name[16];
sock=socket(AF_INET,SOCK_DGRAM,0);
if(sock==-1)
{
perror("socket");
}
strcpy(g_eth_name,"ens33");//注意ens33,应该ifconfig一下在决定是什么,未知原因,没学到网络编程,后续补充
strcpy(ifr.ifr_name,g_eth_name);
printf("eth name:\t%s\n",g_eth_name);
if(ioctl(sock,SIOCGIFHWADDR,&ifr)<0)
{
perror("ioctl");
}
memcpy(g_macaddr,ifr.ifr_hwaddr.sa_data,6);
printf("local mac:\t");
for(i=0;i<5;i++)
{
printf("%.2x:",g_macaddr[i]);
}
printf("%.2x:\n",g_macaddr[i]);
if(ioctl(sock,SIOCGIFADDR,&ifr)<0)
{
perror("ioctl");
}
memcpy(&sin,&ifr.ifr_addr,sizeof (sin));
g_ipaddr=sin.sin_addr.s_addr;
printf("local eth0:\t%s\n",inet_ntoa(sin.sin_addr));
if(ioctl(sock,SIOCGIFBRDADDR,&ifr)<0)
{
perror("ioctl");
}
memcpy(&sin,&ifr.ifr_addr,sizeof (sin));
g_broadcast_ipaddr=sin.sin_addr.s_addr;
printf("broadcast:\t%s\n",inet_ntoa(sin.sin_addr));
if(ioctl(sock,SIOCGIFNETMASK,&ifr)<0)
{
perror("ioctl");
}
memcpy(&sin,&ifr.ifr_addr,sizeof (sin));
g_broadcast_ipaddr=sin.sin_addr.s_addr;
printf("subnetmask:\t%s\n",inet_ntoa(sin.sin_addr));
close(sock);
}
int main()
{
init_net();
return 0;
}