DUP函数和DUP2函数的应用
#include <unistd.h>
int dup(int oldfd);
功能 :
复制 oldfd 文件描述符,并分配一个新的文件描述符,新的文件描述符是调用进程文件描述符表中最小可用的文件描述符。
参数:
要复制的文件描述符 oldfd。
返回值:
成功:新文件描述符。
失败:返回-1,错误代码存于 errno 中。
int dup2(int oldfd, int newfd);//dup2使用的比较多
功能:
复制一份打开的文件描述符 oldfd,并分配新的文件描述符 newfd, newfd 也标识 oldfd 所标识的文件。
注意:
newfd 是小于文件描述符最大允许值的非负整数, 如果 newfd 是一个已经打开的文件描述符,则首先关闭该文件,然后再复制。
参数:
要复制的文件描述符 oldfd分配的新的文件描述符 newfd
返回值:
成功:返回 newfd失败:
返回-1,错误代码存于 errno 中
DUP函数的作用
dup 和 dup2 是两个非常有用的系统调用,都是用来复制一个文件的描述符,使新的文件描述符也标识旧的文件描述符所标识的文件。
类似于fork函数,创建一个子进程,子进程是对父进程的复制,dup相当于对文件描述符的复制,复制出来的文件描述符,可以和原来的文件描述符一样操作同一个文件,dup也是一种进程间通信的方式。
dup的应用
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int newfd;
/*
#define STDIN_FILENO 0 //标准输入的文件描述符
#define STDOUT_FILENO 1 //标准输出的文件描述符
#define STDERR_FILENO 2 //标准错误的文件描述符
*/
// int dup(int oldfd);
newfd=dup(STDOUT_FILENO);
if(newfd==-1)
{
printf("复制文件描述符出错\n");
exit(-1);//return -1;
}
printf("newfd-->%d\n",newfd);
write(newfd,"hello world\n",13);
return 0;
}
dup2的应用
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
int newfd;
int oldfd;
/*
#define STDIN_FILENO 0 //标准输入的文件描述符
#define STDOUT_FILENO 1 //标准输出的文件描述符
#define STDERR_FILENO 2 //标准错误的文件描述符
*/
// int dup(int oldfd);
oldfd=open("file1.txt",O_CREAT | O_WRONLY | O_TRUNC,0644);
printf("oldfd-->%d\n",oldfd);
newfd=dup2(oldfd,5);
// newfd=dup(oldfd);
printf("newfd-->%d\n",newfd);
pid_t pid=fork();
if(pid==0)
{
printf("son.....\n");
//子进程
write(newfd,"this is son\n",12);
}
else
{
//子进程
printf("father.....\n");
write(newfd,"this is father\n",15);
close(oldfd);
wait(NULL);//回收任意一个子进程资源
}
close(newfd);
return 0;
}
管道:
fd[0]:用于读操作
fd[1]:用于写操作
也可以理解为,fd[0]和fd[1]就是管道的两个端
管道的特性:
1、半双工,数据在同一时刻只能在一个方向上流动。
2、数据只能从管道的一端写入,从另一端读出。
3、写入管道中的数据遵循先入先出的规则(FIFO)。
4、管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数据
的格式,如多少字节算一个消息等。
5、管道不是普通的文件,不属于某个文件系统,其只存在于内存中。
管道就是一个文件,存在于系统内部,不需要我们工程师创建
6、管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。
7、从管道读数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间以
便写更多的数据。
8、管道没有名字,只能在具有公共祖先的进程之间使用--无名管道。
进程之间都是独立的,但是他们在内核态都是相同的,用户态(也就是下图中用户空间--进程控制块)
无名管道 pipe函数
#include <unistd.h>
int pipe(int filedes[2]);
功能:经由参数 filedes 返回两个文件描述符
参数:
filedes 为 int 型数组的首地址,其存放了管道的文件描述符 fd[0]、 fd[1]。
filedes[0]为读而打开, filedes[1]为写而打开管道, filedes[0]的输出是
filedes[1]的输入。
返回值:
成功:返回 0
失败:返回-1
例程1:无名管道读写
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
int fd[2];
int main(void)
{
int ret;
//创建管道
ret=pipe(fd);
if(ret==-1)
{
perror("pipe:");
exit(-1);
}
printf("fd[0]:%d fd[1]:%d \n",fd[0],fd[1]);
pid_t pid=fork();
if(pid==0)
{
//子进程中对管道进行读操作
char buf[128]={0};
read(fd[0],buf,sizeof(buf));//阻塞性
printf("buf-->%s\n",buf);
close(fd[0]);
}
else
{
//父进程中对管道进行写操作
write(fd[1],"zhengzhou 2302\n",15);
close(fd[1]);
wait(NULL);
}
return 0;
}
例程2:写满管道 写端被阻塞
管道是一个内核中文件,大小是多少????
管道中一直写数据,不去读,在溢出的时候,将会阻塞
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
int fd[2];
int main(void)
{
int ret;
//创建管道
ret=pipe(fd);
if(ret==-1)
{
perror("pipe:");
exit(-1);
}
printf("fd[0]:%d fd[1]:%d \n",fd[0],fd[1]);
pid_t pid=fork();
if(pid==0)
{
//子进程中对管道进行读操作
// char buf[128]={0};
// read(fd[0],buf,sizeof(buf));//阻塞性
// printf("buf-->%s\n",buf);
getchar();//
close(fd[0]);
}
else
{
char str='A';
//父进程中对管道进行写操作
for(int i=0;i<70000;i++)
{
write(fd[1],&str,1);
printf("count-->%d\n",i);
}
close(fd[1]);
wait(NULL);
}
return 0;
}
不同的Ubuntu系统,管道的大小可能是不一样
1804版本--管道64KB
有名管道
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
有名管道特性:
命名管道(FIFO)和管道(pipe)基本相同,但也有一些显著的不同, 其特点是:
1、半双工,数据在同一时刻只能在一个方向上流动。
2、写入 FIFO 中的数据遵循先入先出的规则。
3、 FIFO 所传送的数据是无格式的,这要求 FIFO 的读出方与写入方必须事先约定好数
据的格式,如多少字节算一个消息等。
4、 FIFO 在文件系统中作为一个特殊的文件而存在,但 FIFO 中的内容却存放在内存(磁盘)中--可见的。
5、管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。
6、从 FIFO 读数据是一次性操作,数据一旦被读,它就从 FIFO 中被抛弃,释放空间以
便写更多的数据。
7、当使用 FIFO 的进程退出后, FIFO 文件将继续保存在文件系统中以便以后使用。
8、 FIFO 有名字,不相关的进程可以通过打开命名管道进行通信。
操作 FIFO 文件时的特点
系统调用的 I/O 函数都可以作用于 FIFO,如 open、 close、 read、 write 等。
特点一:
不指定 O_NONBLOCK(即 open 没有位或 O_NONBLOCK)
1、 open 以只读方式打开 FIFO 时,要阻塞到某个进程为写而打开此 FIFO
2、 open 以只写方式打开 FIFO 时,要阻塞到某个进程为读而打开此 FIFO。
mkfifo函数:
表头文件
#include<sys/types.h>
#include<sys/stat.h>
定义函数
int mkfifo(const char * pathname,mode_t mode);
参数:pathname--有名管道(文件)名字
mode--文件权限
1、当使用O_NONBLOCK 旗标时,打开FIFO 文件来读取的操作会立刻返回,但是若还没有其他进程打开FIFO 文件来读取,则写入的操作会返回ENXIO 错误代码。
2、没有使用O_NONBLOCK 旗标时,打开FIFO 来读取的操作会等到其他进程打开FIFO文件来写入才正常返回。同样地,打开FIFO文件来写入的操作会等到其他进程打开FIFO 文件来读取后才正常返回。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
int fd[2];
int main(void)
{
int ret;
char buf[128]={0};
ret=mkfifo("fifo_test",0666);
// if(ret!=0)
// {
// perror("mkfifo:");
// exit(-1);
// }
int fd=open("fifo_test",O_RDONLY);
// write(fd,"zhengzhou_gaoxinqu\n",19);
read(fd,buf,128);
printf("buf:%s\n",buf);
close(fd);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
int fd[2];
int main(void)
{
int ret;
//创建管道
ret=mkfifo("fifo_test",0666);
// if(ret!=0)
// {
// perror("mkfifo:");
// exit(-1);
// }
int fd=open("fifo_test",O_WRONLY);
write(fd,"zhengzhou_gaoxinqu\n",19);
close(fd);
return 0;
}
单机版本的聊天室功能
1.不同的进程之间可以互相传输数据
不同的进程--不同用户---给每一个用户起一个唯一的名字
名字---通过键盘输入
建议:发送数据和接受数据使用不同的进程实现
比如:父进程发送数据
子进程接收数据