Linux系统编程17 系统调用IO - dup,dup2和原子操作

原子操作:不可分割的操作
原子操作的作用:解决竞争和冲突。
如 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!");
}

猜你喜欢

转载自blog.csdn.net/LinuxArmbiggod/article/details/105905999