Linux系统编程常用函数 (文件/目录)

Lunix系统编程函数多且复杂,本文记录一下Linux系统编程最最常用的哪些函数以及这些函数的常见用法。当然本文不可能囊括所有,所以在编程时候最好的学习方法还是查看manpage,多看多写,熟能生巧。

博主继续Linux编程不久,水平有限文章有错误还请各位不吝指出。废话不多说,开始咯。

文件操作:

Open函数:     所需头文件#include<sys/types.h>        #include<sys/stat.h>          #include<fcntl.h>

int open(const char *pathname, int flags, mode_t mode);     函数作用是打开一个文件,pathname是要打开文件的"路径+名称",flags是打开方式,mode是文件权限(配合新建文件时候使用,会受umask影响)。

其中flags的取值:O_RDONLY—只读打开、 Q_WRONLY—只写打开、O_RDWR—读、写打开 ;这三个变量只能指定一个 ;O_CREAT—若文件不存在,则创建它。需要使用mode(文件权限标志)选项,来指明新文件的访问权限 ;O_APPEND—追加写;O_TRUNC 如果此文件存在,而且为只读或只写成功打开,则将其长度截短为0。O_NONBLOCK 如果pathname指的是一个FIFO、一个块特殊文件或一个字符特殊文件,则此选择项为此文件的本次打开操作和后续的I/O操作设置非阻塞方式。O_NDELAY所产生的结果使I/O变成非阻塞模式(non-blocking),在读取不到数据或是写入缓冲区已满会马上return,而不会阻塞等待。

返回值:成功则返回打开文件的描述符,失败则返回-1。
文件标识符0、1、2分别代表标准输入、标准输出和标准错误输出,分别用常量STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO代替。

Close函数

int close(int fd);    与open相对应的,close是关闭文件函数,fd是想要关闭的文件描述符。这个函数几乎不会产生错误。

Read函数:#include<unistd.h>

ssize_t read(int fd, void *buf, size_t count);  函数作用是从文件中读取数据,fd是想从文件描述符为fd中读取,buf是要存放读入数据的缓冲区,count是缓冲区大小。

返回值>0,实际读到的字节数,=0读到文件末尾对端关闭了,-1产生错误,看errno:

EINTR 此调用被信号所中断。EAGAIN / EWOULDBLOCK 当使用不可阻断I/O 时(O_NONBLOCK),若无数据可读取则返回此值。EBADF 参数fd 非有效的文件描述词,或该文件已关闭。

Write函数:#include<unistd.h>

ssize_t write(int fd, const void *buf, size_t count);  与read相对于的,函数作用是写出数据。fd是写到文件描述符为fd的文件中,buf要把哪里缓冲区的数据写出,count是缓冲区大小。

返回值:如果顺利write()会返回实际写入的字节数。当有错误发生时则返回-1,错误代码存入errno中。

Perror函数:#include <stdio.h>

void perror(const char *s);  这个函数有关上面函数返回值时,多次提到的错误代码。它的作用是:先打印s指向的字符串,然后输出当前errno值所对应的错误提示信息,例如当前errno若为12,调用perror("ABC"),会输出"ABC: Cannot allocate memory"。

此函数通常用于做很多判断上诉函数错误后的错误信息打印。

相似功能的有:strerror 函数: char *strerror(int errnum);,它传入一个错误号errno,传出该错误号的对应错误详细。

Fcntl函数

int fcntl(int fd, int cmd, .../*arg*/....);  函数作用是:改变一个【已经打开】的文件的 访问控制属性。重点是F_GETFL和F_SETFL这两个的用法。

改变某个打开文件的权限:int flags=fcntl (fd, F_GETFL);   --->      flags |= (想要的权限);  --->    fcntl(fd,F_SETFL,flags);

Lseek函数

Linux 中可使用系统函数 lseek 来修改文件偏移量(读写位置),每个打开的文件都记录着当前读写位置,打开文件时读写位置是 0,表示文件开头,通常读写多少个字节就会将读写位置往后移多少个字节。但是有一个例外,如果以 O_APPEND方式打开,每次写操作都会在文件末尾追加数据,然后将读写位置移到新的文件末尾。lseek和标准 I/O 库的 fseek 函数类似,可以移动当前读写位置(或者叫偏移量)。

off_t lseek(int fd, off_t offset, int whence);  fd要偏移的文件描述符;offest偏移量;whence要从哪开始偏移,可以的取值:SEEK_SET:从文件头部开始偏移offset个字节。SEEK_CUR:从文件当前读写的指针位置开始,增加offset个字节的偏移量。SEEK_END文件偏移量设置为文件的大小加上偏移量字节。

返回值:失败返回-1; 成功:返回的值是较文件起始位置向后的偏移量。

两个特别注意:①lseek 允许超过文件结尾设置偏移量,文件会因此被拓展。②注意文件“读”和“写”使用同一偏移位置。

lseek常见两个应用场景:①通过 lseek 获取文件的大小:len=lseek(fd, 0, SEEK_END);    ②使用 lseek 拓展文件:write 操作才能实质性的拓展文件。单 lseek 是不能进行拓展的。一般:write(fd, "a", 1);

Stat函数

int stat(const char *path, struct stat *buf);   获取文件属性,(从文件的inode 结构体中获取)。path是"路径名+文件名",stat是传出参数,返回path文件的文件属性结构体(这个可是编程是看manpage)。

struct stat {
    dev_t         st_dev;       //文件的设备编号
    ino_t         st_ino;       //节点
    mode_t        st_mode;      //文件的类型和存取的权限
    nlink_t       st_nlink;     //连到该文件的硬连接数目,刚建立的文件值为1
    uid_t         st_uid;       //用户ID
    gid_t         st_gid;       //组ID
    dev_t         st_rdev;      //(设备类型)若此文件为设备文件,则为其设备编号
    off_t         st_size;      //文件字节数(文件大小)
    unsigned long st_blksize;   //块大小(文件系统的I/O 缓冲区大小)
    unsigned long st_blocks;    //块数
    time_t        st_atime;     //最后一次访问时间
    time_t        st_mtime;     //最后一次修改时间
    time_t        st_ctime;     //最后一次改变时间(指属性)
};

返回值:      执行成功则返回0,失败返回-1,错误代码存于errno。

Lstat函数

与stat函数一样,区别在于stat会符号穿透,而lstat不会穿透(即传入一个链接,不会解析原文件,而会解析成链接)

Link函数

int link(const char *oldpath, const char *newpath);   可以为已经存在的文件创建目录项(硬链接),path都是“路径+文件名”

成功:0;失败:-1 设置 errno 为相应值

Unlink函数

int unlink(const char *pathname);  与link相对的,删除一个文件的目录项

成功:0;失败:-1 设置 errno 为相应值

注意 Linux 下删除文件的机制:不断将 st_nlink -1,直至减到 0 为止。无目录项对应的文件,将会被操作系统择机释放。(具体时间由系统内部调度算法决定)

因此,我们删除文件,从某种意义上说, 只是让文件具备了被释放的条件。

unlink 函数的特征:清除文件时,如果文件的硬链接数到 0 了,没有 dentry 对应,但该文件仍不会马上被释放。要等到所有打开该文件的进程关闭该文件,系统才会挑时间将该文件释放掉。

Dup/Dup2函数

这两个函数功能:都是文件描述符拷贝。重定向文件描述符指向。比较常用的是dup2:

int dup2(int oldfd, int newfd);    文件描述符其实是一个bitmap,那么此函数的功能就是把bitmap中newfd位置指向oldfd位置,实现重定向。

成功:返回一个新文件描述符;如果 oldfd 有效,则返回的文件描述符与 oldfd 指向同一文件。

失败:如果 oldfd 无效,调用失败,关闭 newfd。返回-1,同时设置 errno 为相应值。

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 #include<fcntl.h>
 5 #include<unistd.h>
 6 
 7 void error_handling(char *message);
 8 
 9 //实现把argv[1]文件内容copy到argv[2]文件中
10 int main(int argc,char *argv[])
11 {
12     char buf[1];
13     int n=0;
14     
15     //open函数打开argv[1]文件并返回文件描述符
16     int fd1=open(argv[1],O_RDONLY);
17     if (fd1==-1) error_handling("open argv1 error");
18 
19     //同上
20     int fd2=open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0664);
21     if (fd2==-1) error_handling("open argv2 error");
22 
23     //copy主体
24     while ((n=read(fd1,buf,1024))!=0) {    //read函数从fd1读取文件到buf,并返回读取大小
25         if (n<0) {    //读完了
26             perror("read error");
27             break;
28         }
29         write(fd2,buf,n);    //把读取到的buf的内容写到fd2中
30     }
31 
32     close(fd1);    //关闭fd1
33     close(fd2);    //关闭fd2
34 
35     return 0;
36 }
37 
38 void error_handling(char *message) {
39     perror(message);
40     exit(1);
41 }
上诉函数完成copy功能

目录操作:

Getcwd函数

char *getcwd(char *buf, size_t size);  获取进程当前工作目录 (卷 3,标库函数)

成功:buf 中保存当前进程工作目录位置。失败返回 NULL。

Chdir函数

int chdir(const char *path);  改变当前进程的工作目录

成功:0;失败:-1 设置 errno 为相应值

Opendir函数

DIR *opendir(const char *name);  根据传入的目录名打开一个目录 (库函数) 

成功返回指向该目录结构体指针,失败返回 NULL

Readdir函数:

struct dirent *readdir(DIR *dirp); 成功返回目录项结构体指针;失败返回NULL设置errno为相应值.

struct dirent {
  ino_t d_ino; /* inode number */   重点 
  off_t d_off; /* offset to the next dirent */
  unsigned short d_reclen; /* length of this record */
  unsigned char d_type; /* type of file */
  char d_name[256]; /* filename */  重点
};

Closedir函数

int closedir(DIR *dirp);  关闭打开的目录  

成功:0;失败:-1 设置 errno 为相应值

在linux下遍历某一目录下内容LINUX下历遍目录的方法一般是这样的:打开目录->读取->关闭目录,相关函数是opendir -> readdir -> closedir ;

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include <sys/types.h>
 4 #include <sys/stat.h>
 5 #include <unistd.h>
 6 #include<string.h>
 7 #include<dirent.h>
 8 
 9 void read_dir(char *dir);
10 
11 void isFile(char *name) {
12     int ret=0;
13     struct stat sb;        //stat结构体
14 
15     ret = stat(name,&sb);
16     if (ret==-1) {
17         perror("stat error");
18         return;
19     }
20 
21     if (S_ISDIR(sb.st_mode)) {    //判断是否目录,是的话继续向下递归
22         read_dir(name);
23     }
24 
25     //不是目录,是文件,打印
26     printf("%10s		%ld
",name,sb.st_size);
27 
28     return;
29 }
30 
31 void read_dir(char *dir) {
32     char path[256];
33     DIR* dp;    //目录指针
34     struct dirent *sdp;    // 目录项结构体
35 
36     dp=opendir(dir);    //open()函数打开目录,返回目录结构体
37     if (dp==NULL) {
38         perror("opendir error");
39         return;
40     }
41 
42     while ((sdp=readdir(dp))!=NULL) {
43         if (strcmp(sdp->d_name,".")==0 || strcmp(sdp->d_name,"..")==0) continue;    //目录里有这两个东西,忽略避免死循环
44 
45 
46         //拼接目录字符串并继续递归旧:目录+名字
47         sprintf(path,"%s/%s",dir,sdp->d_name);
48         isFile(path);
49     }
50 
51     closedir(dp);    //open了一定记得close
52 }
53 
54 
55 int main(int argc,char *argv[])
56 {
57     if (argc==1)
58         isFile(".");
59     else
60         isFile(argv[1]);
61     return 0;
62 }
统计目录及其子目录中的普通文件的个数

进程相关,进程间通信,线程相关,线程同步在另一篇文章:https://www.cnblogs.com/clno1/p/12935702.html

原文地址:https://www.cnblogs.com/clno1/p/12935626.html