#include <stdio.h>
#include <stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, char **argv)
{
struct stat buf;
if(argc != 2) {
printf("Usage: stat ");
exit(-1);
}
if(stat(argv[1], &buf) != 0) {
printf("stat error.");
exit(-1);
}
printf("#i-node: %ld
", buf.st_ino);
printf("#link: %d
", buf.st_nlink);
printf("UID: %d
", buf.st_uid);
printf("GID: %d
", buf.st_gid);
printf("Size %ld
", buf.st_size);
exit(0);
}
1.实现简单的文件内容复制的功能
memset函数按字节对内存块进行初始化,所以不能用它将int数组初始化为0和-1之外的其他值,一个字符一个字节的,方便初始化。
如果用memset(a,1,20),就是对a指向的内存的20个字节进行赋值,每个都用数1去填充,转为二进制后,1就是00000001,占一个字节。一个int元素是4字节,合一起是0000 0001,0000 0001,0000 0001,0000 0001,转化成十六进制就是0x01010101,就等于16843009,就变成了对一个int元素的赋值了。
#include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int srcid,dstid; unsigned char buf[128]; int readlen,pos; memset(buf,0x00,128);//对于字符指针类型的,剩余的部分通常是不会为0的,防止出现野值 srcid = open("/home/mm/open.c",O_RDONLY); if(srcid<0) { printf("打开文件出错 "); return 0; } dstid = open("/home/mm/open.c.bak",O_WRONLY|O_CREAT); if(dstid<0) { printf("打开文件出错 "); return 0; } pos = lseek(srcid,0,SEEK_CUR); printf("文件当前位置 %d ",pos); while(readlen = read(srcid,buf,128)>0)//大于0说明文件还没有读完 { write(dstid,buf,readlen); pos +=readlen;//修改读写指针位置 lseek(srcid,pos,SEEK_SET);//设置读写指针位置 } close(srcid); close(dstid); }
针对上面例子的Makefile文档,上面文件目录为 /home/wxd/study/open.c
open:open.o
gcc -o open open.o
.PHONY:clean
clean:
sudo rm -f *.o open open.c.bak ~*
read
read()是一个系统调用函数。用来从一个文件中,读取指定长度的数据到 buf 中。
使用read()时需要包含的头文件: <unistd.h>
函数原型:
ssize_t read(int fd, void *buf, size_t count);
ssize_t是系统头文件中 typedef 定义的数据类型,相当于 signed int。
参数:
fd:要从中读取内容的文件的文件描述符。
count:期望读取的文件字节数。
返回值:
成功 返回读到的字节数; 已读到文件结尾返回 0; 出错返回 -1。
Write
write()是一个系统调用函数,作用是将buf 中的内容写入到文件中。
使用write()函数时要包含头文件 <unistd.h>。
函数原型:
ssize_t write(int fd, char *buf, size_t count)
参数:
fd:要从中读取内容的文件的文件描述符。
count:期望读取的文件字节数。
返回值:
写入成功返回实际写入的字节数,出错返回-1。
不得不提的是,返回-1的常见原因是:磁盘空间已满,超过了一个给定进程的文件长度。
lseek()的作用是,设置文件内容的读写位置。
每个打开的文件都有一个“当前文件偏移量”,是一个非负整数,用以度量从文件开始处计算的字节数。通常,读写操作都是从当前文件偏移量处开始,并使偏移量增加所读或写的字节数。默认情况下,你打开一个文件(open),除非指定O_APPEND参数,不然位移量被设为0。
使用lseek()需要包含的头文件:<sys/types.h>,<unistd.h>
函数原型:
off_t lseek(int fd, off_t offset, int whence);
off_t是系统头文件定义的数据类型,相当于signed int
参数:
fd:是要操作的文件描述符
whence:是当前位置基点。
SEEK_SET,以文件的开头作为基准位置,新位置为偏移量的大小。
SEEJ_CUR,以当前文件指针的位置作为基准位置,新位置为当前位置加上偏移量。
SEEK_END,以文件的结尾为基准位置,新位置位于文件的大小加上偏移量。
offset:偏移量,要偏移的量。可正可负(向后移、向前移);
返回值:
成功返回相对于文件开头的偏移量。 出错返回-1
使用lseek获取文件大小:
stat 的基本使用
stat:返回一个与此命
需要包含的头文件: <sys/types.h>,<sys/stat.h>,<unistd.h>
函数原型:
int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);
参数:
对于stat() & lstat()来说path,是要查看属性的文件或目录的全路径名称
对于fstat,fd 是要查看属性文件的文件描述符
buf:指向用于存放文件属性的结构体,函数成功调用后,buf各个字段存放各个属性。
返回值 成:
功返回0; 错误返回 -1;
给定一个文件:
stat 函数获得一个与此命名文件有关的信息(到一个struct stat 类型的buf中) 。
fstat 函数获得文件描述符 fd 打开文件的相关信息(到一个struct stat 类型的buf中) 。
lstat 函数类似于 stat,但是当命名文件是一个符号连接时, lstat 获取该符号连接的有关信息,而不是由该符号连接引用文件的信息。
struct stat在系统头文件<stat.h>中,
fcntl和select函数详解
第一、fcntl函数详细使用
fcntl有强大的功能,它能够复制一个现有的描述符,获得/设置文件描述符标记,获得/设置文件状态标记,获得/设置异步I/O所有权,获得/设置纪录锁。
当多个用户共同使用,操作一个文件的情况,Linux通常采用的方法就是给文件上锁,来避免共享资源产生竞争的状态。
fcntl文件锁有两种类型:建议性锁和强制性锁
建议性锁是这样规定的:每个使用上锁文件的进程都要检查是否有锁存在,当然还得尊重已有的锁。内核和系统总体上都坚持不使用建议性锁,它们依靠程序员遵守这个规定。
强制性锁是由内核执行的。当文件被上锁来进行写入操作时,在锁定该文件的进程释放该锁之前,内核会阻止任何对该文件的读或写访问,每次读或写访问都得检查锁是否存在。
使用fcntl文件锁进行I/O操作必须小心:进程在开始任何I/O操作前如何去处理锁,在对文件解锁前如何完成所有的操作,是必须考虑的。如果在设置锁之前打开文件,或者读取该锁之后关闭文件,另一个进程就可能在上锁/解锁操作和打开/关闭操作之间的几分之一秒内访问该文件。当一个进程对文件加锁后,无论它是否释放所加的锁,只要文件关闭,内核都会自动释放加在文件上的建议性锁(这也是建议性锁和强制性锁的最大区别), 所以不要想设置建议性锁来达到永久不让别的进程访问文件的目的(强制性锁才可以)^_^;强制性锁则对所有进程起作用。
可以用fcntl 函数改变一个已打开的文件的属性,可以重新设置读、写、追加、非阻塞等标志(这些标志称为File StatusFlag),而不必重新open 文件。
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
这个函数和open 一样,也是用可变参数实现的,可变参数的类型和个数取决于前面的cmd 参数。
文件锁包括了 建议性锁 和 强制性锁。
建议性锁要求每个上锁的文件的进程都要检查是否有锁存在,并且尊重已有的锁,在一般情况下,内核和系统都不使用建议性锁。
强制性锁是由内核执行的锁,当一个文件被上锁进行读写操作的时候,内核将阻止其他任何文件对其进行读写操作。每次读写操作都要检查是否有锁存在。
在Linux中实现上锁的函数有lock()和fcntl()。
lock()用于对文件施加建议性锁
fcntl()用于对文件施加建议性锁和强制性锁都行。同时还可以对文件某一条纪录进行上锁,也就是记录锁。
记录锁分为 读取锁(共享锁,它能够使多个进程都能在文件的同一部分建立读取锁) 和 写入锁(排斥锁,在任何时刻只能有一个进程在文件的某部分建立写入锁。)。
fcntl函数原型
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
int fcntl(int fd, //文件描述符
int cmd , //不同的命令
struct flock *lock) //设置记录锁的具体状态
cmd取值:
F_DUPFD 复制文件描述符
F_GETFD 获得fd的close-on-exec标志
F_SETFD 设置close-on-exec标志
F_GETFL 获得open设置标志
F_SETFL 设置lock描述的标志
F_GETLK 测试该锁是否被另外一把锁排斥
F_SETLKW 如果存在其他锁,则调用进程睡眠,如果捕捉到信号则睡眠中断
F_GETOWN 检索收到的SIGIO和SIGURG信号的进程号或者进程组号
F_SETOWN 设置进程号或进程组号
这里的lock结构体如下:
struct flock
{
short l_type; /*F_RDLCK(读取锁),F_WRLCK(写入锁),F_UNLCK(解锁)*/
off_t l_start; /*相对偏移量(字节)*/
short l_whence; /*SEEK_SET ,SEEK_CUR ,SEEK_END */
off_t l_len; /*加锁区域长度*/
pid_t l_pid;
}
成功:0
出错:-1
提示:如果加锁整个文件通常的方法是将l_start设置为0,l_whence设置为SEEK_SET, l_len设置为0。
下面的例子使用F_GETFL和F_SETFL这两种fcntl 命令改变STDIN_FILENO的属性上O_NONBLOCK 选项,实现非阻塞读终端的功能。
用fcntl改变File Status Flag
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#define MSG_TRY "try again
"
int
main(void)
{
char buf[10];
int n;
int flags;
flags = fcntl(STDIN_FILENO, F_GETFL);
flags |= O_NONBLOCK;
if (fcntl(STDIN_FILENO, F_SETFL, flags) == -1)
{
perror("fcntl");
exit(1);
}
tryagain:
n = read(STDIN_FILENO, buf, 10);
if (n < 0)
{
if (errno == EAGAIN)
{
sleep(1);
write(STDOUT_FILENO, MSG_TRY,strlen(MSG_TRY));
goto tryagain;
}
perror("read stdin");
exit(1);
}
write(STDOUT_FILENO, buf, n);
return 0;
}
第二、select函数详细使用
select系统调用是用来让我们的程序监视多个文件句柄(file descriptor)的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有某一个或多个发生了状态改变。
文件在句柄在Linux里很多,如果你man某个函数,在函数返回值部分说到成功后有一个文件句柄被创建的都是的,如man socket可以看到“On success, a file descriptor
for the new socket is returned.”而man 2 open可以看到“open() and creat() return the new file
descriptor”,其实文件句柄就是一个整数,看socket函数的声明就明白了:
int socket(int domain, int type, int protocol);
当然,我们最熟悉的句柄是0、1、2三个,0是标准输入,1是标准输出,2是标准错误输出。0、1、2是整数表示的,对应的FILE *结构的表示就是stdin、stdout、stderr,0就是stdin,1就是stdout,2就是stderr。
比如下面这两段代码都是从标准输入读入9个字节字符:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char ** argv)
{
char buf[10] = "";
read(0, buf, 9); /* 从标准输入 0 读入字符 */
fprintf(stdout, "%s
",
buf); /* 向标准输出 stdout 写字符 */
return 0;
}
/* **上面和下面的代码都可以用来从标准输入读用户输入的9个字符** */
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char ** argv)
{
char buf[10] = "";
fread(buf, 9, 1, stdin); /* 从标准输入 stdin 读入字符 */
write(1, buf, strlen(buf));
return 0;
}
继续上面说的select,就是用来监视某个或某些句柄的状态变化的。select函数原型如下:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout);
函数的最后一个参数timeout显然是一个超时时间值,其类型是struct
timeval *,即一个struct timeval结构的变量的指针,所以我们在程序里要申明一个struct timeval tv;然后把变量tv的地址&tv传递给select函数。struct
timeval结构如下:
struct timeval { |
第2、3、4三个参数是一样的类型: fd_set *,即我们在程序里要申明几个fd_set类型的变量,比如rdfds, wtfds, exfds,然后把这个变量的地址&rdfds, &wtfds, &exfds 传递给select函数。这三个参数都是一个句柄的集合,第一个rdfds是用来保存这样的句柄的:当句柄的状态变成可读的时系统就会告诉select函数返回,同理第二个wtfds是指有句柄状态变成可写的时系统就会告诉select函数返回,同理第三个参数exfds是特殊情况,即句柄上有特殊情况发生时系统会告诉select函数返回。特殊情况比如对方通过一个socket句柄发来了紧急数据。如果我们程序里只想检测某个socket是否有数据可读,我们可以这样:
fd_set rdfds; /* 先申明一个 fd_set 集合来保存我们要检测的 socket句柄 */ |
注意select函数的第一个参数,是所有加入集合的句柄值的最大那个值还要加1。比如我们创建了3个句柄:
/************关于本文档********************************************
*filename: Linux网络编程一步一步学-select详解
*purpose: 详细说明select的用法
*********************************************************************/
int sa, sb, sc;
sa = socket(...); /* 分别创建3个句柄并连接到服务器上 */
connect(sa,...);
sb = socket(...);
connect(sb,...);
sc = socket(...);
connect(sc,...);
FD_SET(sa, &rdfds);/* 分别把3个句柄加入读监视集合里去 */
FD_SET(sb, &rdfds);
FD_SET(sc, &rdfds);
在使用select函数之前,一定要找到3个句柄中的最大值是哪个,我们一般定义一个变量来保存最大值,取得最大socket值如下:
int maxfd = 0; |
然后调用select函数:
ret = select(maxfd + 1, &rdfds, NULL, NULL, &tv); /* 注意是最大值还要加1 */
同样的道理,如果我们要检测用户是否按了键盘进行输入,我们就应该把标准输入0这个句柄放到select里来检测,如下:
FD_ZERO(&rdfds); |
使用文件标准I/O进行复制文件:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#define SRC_FILE_NAME "/home/mm/copy.c" /* 源文件名 */
#define DEST_FILE_NAME "/home/mm/copy.c.bak" /* 目标文件名文件名 */
int cp_file(char *sfile,char *dfile,u_int32_t uLen)
{
FILE *sFile = NULL,*dFile = NULL;
char *line = NULL;
int tmpNO;
if((sFile= fopen(sfile,"rb+")) == (FILE *)NULL) //打开原文件
{
return -1;
}
if((dFile=fopen(dfile,"wb+")) == (FILE *)NULL) //打开新文件
{
return -1;
}
line = (char *)malloc(uLen);
if(line == NULL)
{
return -1;
}
memset(line,0,uLen);
if(fread(line,sizeof(char),uLen,sFile) != uLen) //读取原文件内容,如果文件很大,请分块读取
{
printf("updatefile:fopen error");
fclose(sFile);
free(line);
return -1;
}
if(fwrite(line,sizeof(char),uLen,dFile) != uLen) //写入新文件
{
printf("updatefile:fopen error");
fclose(dFile);
free(line);
return -1;
}
tmpNO = fileno(dFile);
fsync(tmpNO); //刷新内核的块缓存
fclose(sFile);
fclose(dFile);
free(line);
return 0;
}
int main(void)
{
struct stat buf;
stat(SRC_FILE_NAME, &buf);
if(cp_file(SRC_FILE_NAME,DEST_FILE_NAME,buf.st_size)<0)
printf("copy file error");
return 0;
}
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- int main()
- {
- int srcid,dstid;
- unsigned char buf[128];
- int readlen,pos;
- memset(buf,0x00,128);
- srcid = open("/home/mm/open.c",O_RDONLY);
- if(srcid<0)
- {
- printf("打开文件出错 ");
- return 0;
- }
- dstid = open("/home/mm/open.c.bak",O_WRONLY|O_CREAT);
- if(dstid<0)
- {
- printf("打开文件出错 ");
- return 0;
- }
- pos = lseek(srcid,0,SEEK_CUR);
- printf("文件当前位置 %d ",pos);
- while(readlen = read(srcid,buf,128)>0)
- {
- write(dstid,buf,readlen);
- pos +=readlen;
- lseek(srcid,pos,SEEK_SET);
- }
- close(srcid);
- close(dstid);
- }