原子操作:不可分割的操作
原子操作的作用:解决竞争和冲突。
如 tmpnam
程序中的重定向:dup dup2
文件描述符的特点是 优先使用当前可用范围内最小的描述符
实验一:用最土的方法实现 程序的重定向
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FNAME "file_echo"
int main()
{
puts("hello!");//向 文件描述符1 对应的 文件写
}
结果就是向标准输出打印 hello!
那么在不改动最后一行代码 puts(“hello!”); 的前提下,将hello 输出到 指定文件中,该怎么做?
即 将标准输出关闭,即空出 文件描述1的位置,再重新创建一个文件,根据文件描述符的特点,优先使用可用范围内最下的描述符,即新创建的文件的文件描述符为1,即替换了标准输出,那么 puts(“hello!”); 就会输出到目标文件了。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FNAME "file_echo"
int main()
{
int fd;
close(1);//关闭标准输出
fd = open(FNAME,O_WRONLY|O_CREAT|O_TRUNC,0600);//生成文件,使用文件描述符1
if(fd < 0)
{
perror("open()");
exit(1);
}
puts("hello!");//向 文件描述符1 对应的 文件写
}
mhr@ubuntu:~/work/linux/sysio/16$ gcc echo.c
mhr@ubuntu:~/work/linux/sysio/16$
mhr@ubuntu:~/work/linux/sysio/16$ ./a.out
mhr@ubuntu:~/work/linux/sysio/16$ cat file_echo
hello!
mhr@ubuntu:~/work/linux/sysio/16$
NAME
dup, dup2, dup3 - duplicate a file descriptor 复制一个文件描述符
SYNOPSIS
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
DESCRIPTION
The dup() system call creates a copy of the file descriptor oldfd, using the lowest-numbered unused descriptor for the new descriptor.
复制文件描述符,使用一个可用范围内最小的文件描述符作为新的文件描述符。
dup2()
newfd 作为 oldfd的副本,如果 newfd被占用的话,会先把newfd关闭,再执行文件描述符的拷贝。即 dup2 就相当于 如下两句的原子操作
close(1);
dup(fd);
Note the following points:
// 如果 oldfd 不合法,则调用失败,也不会关闭newfd
* If oldfd is not a valid file descriptor, then the call fails, and newfd is not closed.
//如果 oldfd == newfd 则啥也不做
* If oldfd is a valid file descriptor, and newfd has the same value as oldfd, then dup2() does nothing, and returns newfd.
实验2 使用dup() 实现重定向,缺点:不原子
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FNAME "file_echo"
int main()
{
int fd;
fd = open(FNAME,O_WRONLY|O_CREAT|O_TRUNC,0600);
if(fd < 0)
{
perror("open()");
exit(1);
}
close(1); //关闭标准输出 1
dup(fd); // 已知关闭了标准输出,则当前没被使用的最小文件描述符为1,则复制一份文件描述符到 1
close(fd); //关闭 fd,文件属性结构体中计数器减一,则目前就只剩下 文件描述符1 与 FNAME文件关联了。
puts("hello!");
}
mhr@ubuntu:~/work/linux/sysio/16$ gcc dup.c
mhr@ubuntu:~/work/linux/sysio/16$ ll
total 28
drwxrwxr-x 2 mhr mhr 4096 May 3 02:36 ./
drwxrwxr-x 7 mhr mhr 4096 May 3 02:18 ../
-rwxrwxr-x 1 mhr mhr 8856 May 3 02:36 a.out*
-rw-rw-r-- 1 mhr mhr 329 May 3 02:36 dup.c
-rw-rw-r-- 1 mhr mhr 7 May 3 02:22 file_echo
mhr@ubuntu:~/work/linux/sysio/16$ ./a.out
mhr@ubuntu:~/work/linux/sysio/16$ cat file_echo
hello!
mhr@ubuntu:~/work/linux/sysio/16$
出现已成的情况:
情况1:
一个进程默认有0 1 2 文件描述符,即标准输入 输出 错误,但是也可以没有。如果当前进程默认就没有标准输出,那么上述程序,第一次open 的时候 fd 就会是1 后续程序就是不正确的
情况2:
假如还有一个兄弟进程在跑,当我们 close(1) 的时候,还没 dup(fd)的时候,如果此时其他与本线程共用一个 文件属性结构体的指针数组 的进程 open 一个文件,则文件描述符1位置 会被其他文件占用,则我们这个程序的输出 会输出到其他文件,这种情况的原因是如下这两步操作不原子。
close(1);
dup(fd);
实验3 dup2 原子
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FNAME "file_echo"
int main()
{
int fd;
fd = open(FNAME,O_WRONLY|O_CREAT|O_TRUNC,0600);
if(fd < 0)
{
perror("open()");
exit(1);
}
//close(1);
//dup(fd);
dup2(fd,1);
close(fd);
puts("hello!");
}
mhr@ubuntu:~/work/linux/sysio/16$
mhr@ubuntu:~/work/linux/sysio/16$ gcc dup2.c
mhr@ubuntu:~/work/linux/sysio/16$ ./a.out
mhr@ubuntu:~/work/linux/sysio/16$ cat file_echo
hello!
mhr@ubuntu:~/work/linux/sysio/16$
但是 fd 如果本身就是1,上述程序还是不对
改
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FNAME "file_echo"
int main()
{
int fd;
fd = open(FNAME,O_WRONLY|O_CREAT|O_TRUNC,0600);
if(fd < 0)
{
perror("open()");
exit(1);
}
//close(1);
//dup(fd);
dup2(fd,1);
if(fd != 1)
close(fd);
puts("hello!");
}