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 为相应值。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
目录操作:
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 ;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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