#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\n", buf.st_ino);
    printf("#link:      %d\n", buf.st_nlink);
    printf("UID:        %d\n", buf.st_uid);
    printf("GID:        %d\n", buf.st_gid);
    printf("Size        %ld\n", 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("打开文件出错 \n"); return 0; } dstid = open("/home/mm/open.c.bak",O_WRONLY|O_CREAT); if(dstid<0) { printf("打开文件出错 \n"); return 0; } pos = lseek(srcid,0,SEEK_CUR); printf("文件当前位置 %d \n",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\n"
             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\n",
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;
}
原文:https://www.cnblogs.com/wddx5/p/12444297.html