文件描述符
0,1,2对应STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO三个标准输入,输出,出错
open函数
#include <fcntl.h>
int open(const char* path, int oflag, ...)'
path就是文件名字
oflag参数
首先必须5项选1:O_RDONLY只读 O_RDONLY只写 O_RDWR读写 O_EXEX只执行 O_SEARCH只搜索
下面是可选的:选几个常用的
O_APPEND 每次写时追加到文件尾
O_CREATE 文件不存在的时候创建它
O_SYNC 每次write等待物理I/O操作完成
O_TRUNC 如果文件存在 而且是只写或者读写打开,会清空文件内容
create函数
其实等于
open(path, O_WRONLY|O_CREAT|O_TRUNC,mode);
close函数
只是将打开文件的计数减1,计数为0才会关闭文件。进程终止就会关闭所以其打开的文件。
lseek函数
off_t lseek(int fd, off_t offset, int whence);
off_t currpos;
currpos = lseek(fd, 0, SEEK_CUR);
如果这个返回-1,代表文件描述符指向一个管道,FIFO,socket等。最好用-1来判断,不要用小于0来判断,因为有的设备返回会有负数。
原子操作
多步组成的一个操作,不会被其他进程打断。
pread,pwrite函数,就是将lseek和write,read组成一个操作。成为原子操作,分开可能会被其他进程打断。
dup dup2函数
这两个函数都是对fd重定向,在另一篇转载的博客有比较详细的说明
dup函数 等效 fcntl_fd, F_DUPFD, 0);
dup2(fd, fd2)等效
close(fd2);
fcntl_fd, F_DUPFD, fd2);
但是dup2是原子操作
下面举个adbd中的dup2函数例子
static int create_subproc_raw(const char *cmd, const char *arg0, const char *arg1, pid_t *pid)
{
// 0 is parent socket, 1 is child socket
int sv[2];
if (adb_socketpair(sv) < 0) {
printf("[ cannot create socket pair - %s ]\n", strerror(errno));
return -1;
}
*pid = fork();
if (*pid < 0) {
printf("- fork failed: %s -\n", strerror(errno));
adb_close(sv[0]);
adb_close(sv[1]);
return -1;
}
if (*pid == 0) {
adb_close(sv[0]);//子进程关闭0端
init_subproc_child();
dup2(sv[1], STDIN_FILENO);
dup2(sv[1], STDOUT_FILENO);
dup2(sv[1], STDERR_FILENO);
adb_close(sv[1]);//关闭原来的sockpair的1端,无用了。因为有3个标准的fd指向了它
execl(cmd, cmd, arg0, arg1, NULL);
fprintf(stderr, "- exec '%s' failed: %s (%d) -\n",
cmd, strerror(errno), errno);
exit(-1);
} else {
adb_close(sv[1]);//父进程关闭1端
return sv[0];
}
}
这里主进程创建了一对socketpair,然后fork一个子进程。子进程复制了父进程的文件描述符表。因此这对sockpair被打开了两次。主进程返回了0端,1端
0-> shell 1-> shell 2-> shell dup2之后
0-> sockpair 1端 1-> sockpair 1端 2-> sockpair 1端
而原来sockpair1端的fd其实没用了,就可以关闭了。
函数fcntl
int fcntl(int fd , int cmd, ...);
设置fd的属性
常用cmd
F_SETFL 设置文件的状态标志比如 O_SYNC
F_GETFL 获取文件的状态标志
F_DUPFD fd重定向
F_SETFD 设置fd标志,比如FD_CLOEXEC代表不能使用execl开启这个fd