文章目录
linux下文件夹操作
相关API函数
opendir(3)
closedir(3)
readdir(3)
代码示例:
文件重定向的实现原理
相关API函数
dup(2)
dup2(2)
代码示例
文件锁
相关使用方式
代码示例
库函数和系统调用函数之间的关系
具体说明
代码示例
linux进程基础
相关API
代码示例
linux下文件夹操作
系统提供了一下库函数来操作文件夹:
相关API函数
opendir(3)
DIR *opendir(const char *name); #include <sys/types.h> #include <dirent.h> DIR *opendir(const char *name); 功能:打开一个文件夹 参数: name:指定了要打开的文件夹的名字 返回值: NULL 错误 errno被设置 返回一个具体的地址
closedir(3)
int closedir(DIR *dirp); #include <sys/types.h> #include <dirent.h> int closedir(DIR *dirp); 功能:关闭一个文件夹 参数: dirp:指定了要关闭的文件夹(opendir(3)的返回值) 返回值: 0 成功 -1 失败 errno被设置
readdir(3)
struct dirent *readdir(DIR *dirp);
#include <dirent.h> struct dirent *readdir(DIR *dirp); 功能:读取文件夹的内容 参数: dirp:指定文件夹。opendir(3)的返回值 返回值: 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; not supported by all file system types */ char d_name[256]; /* filename */ };
代码示例:
- diros.c
#include <stdio.h> #include <sys/types.h> #include <dirent.h> int main(int argc,char *argv[]){ struct dirent *p; //打开指定的文件夹 DIR *dirp=opendir(argv[1]); if(dirp==NULL){ perror("opendir"); return 1; } printf("directory open success..\n"); while((p=readdir(dirp))!=NULL){ printf("%s\t%lu\n",\ p->d_name,p->d_ino); } //关闭文件夹 closedir(dirp); return 0; }
- 执行结果
文件重定向的实现原理
文件重定向就是重新定位文件的流向。要完成文件重定向的功能,需要将文件描述符进行拷贝。
相关API函数
dup(2)
int dup(int oldfd); #include <unistd.h> int dup(int oldfd); 功能:复制一个文件描述符 参数: oldfd:拷贝的样本 返回值: -1 失败 errno被设置 新的文件描述符
dup2(2)
int dup2(int oldfd, int newfd);
功能:复制一个文件描述符 参数: oldfd:拷贝的样本 newfd:指定新的文件描述符 返回值: -1 失败 errno被设置 新的文件描述符
代码示例
- direct.c
#include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(void){ int fd,s_fd; char msg[]="this is a test\n"; fd=open("somefile",O_RDWR|O_CREAT,\ 0664); if(fd==-1){ perror("open"); return 1; } //复制标准输出文件描述符到s_fd s_fd=dup(STDOUT_FILENO); //将fd文件描述符复制到标准输出 dup2(fd,STDOUT_FILENO); close(fd); //向标准输出文件描述符中输出字符串 write(STDOUT_FILENO,msg,strlen(msg)); dup2(s_fd,STDOUT_FILENO); write(STDOUT_FILENO,msg,strlen(msg)); close(s_fd); return 0; }
- 执行结果
文件锁
文件锁分为两种类型:
- 强制锁
- 建议锁
根据锁的互斥性又可以分为读锁(共享锁)、写锁(互斥锁)。
相关使用方式
使用fcntl完成文件锁的功能:
#include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, ... /* arg */ ); 功能:操作文件描述符 参数: fd:指定要操作的文件描述符 cmd:对文件描述符的操作命令 F_GETLK 获取文件上的锁 F_SETLK 设置锁到文件。出现互斥锁的时候,非阻塞。 F_SETLKW 设置锁到文件,出现互斥锁的时候,阻塞。 返回值: 0 成功 -1 错误 errno被设置
struct flock { short l_type; /* Type of lock: F_RDLCK, F_WRLCK, F_UNLCK */ short l_whence; /* How to interpret l_start: SEEK_SET, SEEK_CUR, SEEK_END */ off_t l_start; /* Starting offset for lock */ off_t l_len; /* Number of bytes to lock */ pid_t l_pid; /* PID of process blocking our lock (F_GETLK only) */ };
代码示例
两个进程同时给一个文件添加读锁。
- processA.c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main(void){ int fd; //以读写方式打开文件 fd=open("hello",O_RDWR); if(fd==-1){ perror("open"); return 1; } //定义锁变量 struct flock lock; //初始化锁变量的字段内容 lock.l_type=F_RDLCK; lock.l_whence=SEEK_SET; lock.l_start=0; lock.l_len=6; //给hello文件加读锁 int f=fcntl(fd,F_SETLK,&lock); if(f==-1){ perror("fcntl"); return 2; } printf("添加读锁成功...\n"); getchar(); close(fd); return 0; }
- processB.c
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main(void){ int fd; //以读写方式打开文件 fd=open("hello",O_RDWR); if(fd==-1){ perror("open"); return 1; } //定义锁变量 struct flock lock; //初始化锁变量的字段内容 lock.l_type=F_WRLCK; lock.l_whence=SEEK_SET; lock.l_start=0; lock.l_len=6; //给hello文件加读锁 int f=fcntl(fd,F_SETLKW,&lock); if(f==-1){ perror("fcntl"); return 2; } printf("B添加读锁成功...\n"); getchar(); close(fd); return 0; }
执行结果:
读锁结果
修改B进程为写锁尝试:
发现B进程会在A进程使用期间阻塞等待。
库函数和系统调用函数之间的关系
库函数:(缓冲文件)fopen、fclose、fputc、fgetc。
系统调用:(非缓冲文件)open、close、write、read。
具体说明
fopen(3)
分配结构体FILE的一块内存,在FILE类型中有一个成员变量_fileno,用于保存文件描述符。另外分配一块缓存,后边库函数对文件的读写操作是针对这块缓存的;调用open(2)打开相应的文件。
fgetc(3)
调用fgetc的时候,主要是针对缓存,从缓存中获取数据,如果缓存有数据,立即返回获取到的数据。如果缓存中没有数据,调用read(2),文件描述符有FILE类型的参数的_fileno传入。从系统中获取数据到缓存,然后fgetc(3)返回获取到的数据。
fputc(3)
调用fputc的时候,如果写缓存有空间,直接将字符写入到缓存;如果写缓存已经满,调用write(2)将写缓存中的内容写到文件,清楚缓存,然后将字符写入到写缓存。
fclose(3)
先清除缓存中的内容到文件,然后调用close(2)关闭文件描述符,然后,释放fopen(3)开辟的缓存空间。
代码示例
- file.c
#include <stdio.h> int main(void){ FILE *fp; fp=fopen("hello","r"); if(fp==NULL){ perror("fopen"); return 1; } printf("file open success...\n"); fclose(fp); return 0; }
- 执行结果
linux进程基础
程序和进程的区别:
- 程序是静态的,存放在磁盘上,是指令的集合。
- 进程是程序运行的实例,一个程序运行一次会产生一个进程。
每个进程都有自己的pid,每个进程都有自己的PCB。进程是资源分配的基本单位。
在linux操作系统下,进程直接的关系是斧子关系或者兄弟关系。
所有的用户级进程形成了一颗树,使用pstree可以查看这个树,init是这棵树的树根,也就是1号进程(用户进程的第一个进程)。
相关API
如果创建一个新的进程?
使用系统调用fork(2)创建新进程。
pid_t fork(void);
#include <unistd.h> pid_t fork(void); 功能:创建一个子进程 参数: void 返回值: -1错误,在父进程里返回,子进程不被创建。errno被设置。 成功: 在父进程里返回子进程的pid。 在子进程中0被返回。
代码示例
- fork.c
#include <stdio.h> #include <unistd.h> int main(void){ pid_t pid; //创建子进程 pid=fork(); if(pid ==-1){ perror("fork"); return 1; } if(pid==0){//子进程执行的代码 printf("child\n"); }else{//福进程执行的代码 printf("parent\n"); } return 0; }
- 执行结果