目录:
内容:
- 系统调用与标准IO
- 文件描述符
系统调用:
open函数
int open(const char *pathname, int flags);
或
int open(const char *pathname,int flags, mode_t mode);
作用:打开文件
参数:
pathname:文件路径
flags:以什么方式打开
mode:若需要创建文件,则文件的权限是什么
返回值:失败返回-1,成功返回文件描述符
flags取值:
取值 | 含义 |
---|---|
O_RDONLY | 以只读的方式打开 |
O_WRONLY | 以只写的方式打开 |
O_RDWR | 以可读、可写的方式打开 |
O_CREAT | 文件不存在则创建文件,使用此选项时需使用mode说明文件的权限 |
O_EXCL | 如果同时指定了O_CREAT,且文件已经存在,则出错 |
O_TRUNC | 如果文件存在,且为只读或只写的方式打开,则清空文件内容 |
O_APPEND | 写文件时,数据添加到文件末尾 |
O_NONBLOCK | 当打开的文件是FIFO、字符文件、块文件时,此选项为阻塞标志位 |
示例:
fd1 = open("test0402",O_CREAT|O_WRONLY,0777);
fd_src = open(argv[1],O_RDONLY);
close函数
int close(int fd);
作用:关闭文件
参数:文件描述符
返回值:成功返回0,失败返回-1
示例:
close(fd_src);
write函数
ssize_t write(int fd, const void *addr,size_t count);
作用:把数据写到文件
参数:
fd:文件描述符
addr:要写入的数据的内存首地址
conte:写入的字节个数
返回值:成功返回实际写入的字符个数,失败返回-1
示例:
write(fd_dest,buf,ret);
read函数
ssize_t read(int fd, void *addr, size_t count);
作用:把数据从文件读出
参数:
fd:文件描述符
addr:准备存放数据的内存首地址
conte:读入的字节个数
返回值:成功返回实际读入的字符个数,失败返回-1
示例:
ret = read(fd_src,buf,sizeof(buf));
标准IO:
fopen函数
FILE* fopen(const char *pathname,const char *mode);
作用:打开文件
参数:
pathname:文件路径
mode:流的打开方式
返回值:成功返回指向改流的指针,失败返回NULL
mode取值:
模式 | 功能 |
---|---|
r或rb | 以只读方式打开一个文本文件(不创建文件) |
w或wb | 以写方式打开文件(使文件长度截断为0字节,创建一个文件) |
a或ab | 以添加方式打开文件,即在末尾添加内容,当文件不存在时,创建文件用于写 |
r+或rb+ | 以可读、可写的方式打开文件(不创建新文件) |
w+或wb+ | 以可读、可写的方式打开文件(使文件长度为0字节,创建一个文件) |
a+或ab+ | 以添加方式打开文件,打开文件并在末尾更改文件(如果文件不存在,则创建文件) |
示例:
fp_src = fopen(filename,"w");
fclose函数
int fclose(FILE *stream);
作用:关闭文件
参数:文件描述符
返回值:成功返回0,失败返回EOF
示例:
fclose(fp_src);
fwrite函数
size_t fwrite(const void *ptr, size_t size,size_t nobj, FILE *stream);
作用:把数据写到文件
参数:
ptr:要写入的数据
size:要写入的数据的大小
nobj:写入的数据块的个数
stream:要操作的数据流
返回值:成功返回实际写入的字符个数
示例:
fwrite(" ",4,1,fp_src);
fread函数
size_t fread(void *ptr, size_t size,size_t nobj, FILE *stream);
作用:把数据从文件读出
参数:
ptr:要存放数据的首地址
size:要读入的数据的大小
nobj:读出的数据块的个数
stream:要操作的数据流
返回值:成功返回实际读出的字符个数
示例:
ret = fread(arg,10,1,fp_src);
文件描述符:
描述
在Linux中万事万物皆是文件,而想要访问文件,就需要用到文件描述符。每个进程都有一张文件描述符的表,其中0 1 2 分别是标准输入、标准输出、标准错误。在进程中打开其他文件的时候,系统会返回文件描述符表中最小可用的文件描述符表,然后进行记录。这里有两个复制文件描述符的函数,分别是dup()和dup2(),下面我们进行介绍;
dup函数
int dup(int oldfd);
作用:拷贝一个文件描述符
参数:旧的文件描述符
返回值:成功返回新的文件描述符,失败返回-1
注意:新的文件描述符和旧的文件描述符指向同一个文件,共享旧文件描述符所有的锁定、读写位置、权限。
示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
void main(){
int fd1,fd2;
char buf[] = "helloworld";
char buf1[] = "hi everyone";
fd2 = dup(1);
printf("fd2 is %d\n",fd2);
write(1,buf,sizeof(buf));
write(fd2,buf,sizeof(buf1));
fd1 = open("test0402",O_CREAT|O_RDWR,0777);
printf("fd1 is %d\n",fd1);
if(fd1<0)
{
perror("fd1 error \n");
exit(-1);
}
close(1);
dup(fd1);
printf("glq20200402\n");
close(1);
dup(fd2);
printf("abcdefgh\n");
}
我们对其文件描述符的变化过程进行分析(下图中的“=”指变化过程,例如“3=1”指3复制成了1):
dup2函数
int dup2(int oldfd, int newfd);
作用:拷贝一个文件描述符
参数:旧的文件描述符,新的文件描述符
返回值:成功返回新的文件描述符,失败返回-1
注意:如果新的文件描述符已经被使用,则会先关闭,再复制。
实例代码(文件描述符代码在注释):
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd1;
int fd2 = 3;
int err = 0;
//默认打开 0 1 2
err = dup2(1,fd2); //将3 复制成1 同时返回3 || 0 1 2 3=1
if(err<0)
{
perror("dup2");
}
printf("fd2=%d,err=%d\n", fd2, err);//fd2=3,err=3
fd1 = open("test", O_CREAT|O_RDWR, S_IRWXU);//生成了一个新的4 || 0 1 2 3->1 4(fd1)
dup2(fd1,1);//1复制成4(fd1) 0 1->4 2 3=1 4(fd1)
printf("hello world\n");//1->4(test文件) 所以写到文件内
dup2(fd2,1);//1->fd2(3)->1 || 0 1->4->1 2 3->1 4(fd1)
printf("I love you \n");//此时1是标准输出,所以输出到屏幕
return 0;
命名管道(FIFO):
描述:
命名管道和管道(pipe)类似,都是用来实现不同进程之间通信的,但是FIFO是以文件形式存放在文件系统的,可以通过ls命令实实在在的查看到。FIFO文件存放在文件系用中,但是内容存放在内存中。pipe只能是有子父关系的进程才可以通信,但是FIFO不是,任何进程之间都可以进行通信。FIFO是有名字的,不同的进程通过管道的名字进行通信,FIFO的缓冲区大小为4KB。它和pipe一样,传送的数据是无格式的,一但被读取就会自动释放。
FIFO的创建
int mkfifo( const char *pathname, mode_t mode);
依赖的头文件:
#include <sys/types.h>
#include <sys/stat.h>
参数:
pathname:FIFO的路径
mode:以何种方式打开
返回值:成功返回0,失败返回-1
FIFO的读写
因为FIFO也是文件的一种,所以IO函数都可以作用于FIFO。 下面我们讨论几种情况:
情况一:不指定O_NONBOLCK时只读和只写open的情况
以只读的方式打开:若系统中没有以写方式打开的进程,则该打开一直阻塞等待,直到其他进程以写的方式打开的时候,该只读才会继续执行、正常打开文件。
以只写方式打开:若系统中没有以读方式打开的进程,则该打开一直阻塞等待,直到其他进程以读的方式打开的时候,该只写才会继续执行、正常打开文件。
注意:这里只是讨论的打开的情况还不涉及下面的操作,即代码中的 open(“my_fifo”, O_WRONLY); 这一句。
下面我们用图片进行详解。先执行read和先执行write出现的现象一样,我们以先执行read为例。首先先附上代码:
write文件:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd;
int ret;
char send[100] = "Hello I love you";
ret = mkfifo("my_fifo", S_IRUSR|S_IWUSR);
if(ret != 0)
{
perror("mkfifo");
}
printf("before open\n");
fd = open("my_fifo", O_WRONLY);
if(fd<0)
{
perror("open fifo");
}
printf("after open and before read\n");
write(fd, send, strlen(send));
printf("write to my_fifo buf=%s\n",send);
return 0;
}
read文件:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd;
int ret;
char recv[100];
ret = mkfifo("my_fifo", S_IRUSR|S_IWUSR);
if(ret != 0)
{
perror("mkfifo");
}
printf("before open\n");
fd = open("my_fifo", O_RDONLY);
if(fd<0)
{
perror("open fifo");
}
printf("after open and before read\n");
bzero(recv, sizeof(recv));
read(fd, recv, sizeof(recv));
printf("read from my_fifo buf=[%s]\n",recv);
return 0;
}
首先我们先运行read,运行结果如t图1所示,此时只输出了“before open”,并没有输出“after open and before read”,所以程序阻塞在了“ fd = open(“my_fifo”, O_RDONLY);”这一句,原因是此时没有以写的方式打开的进程。
然后我们运行write,运行了此程序以后,系统中就有了以写方式打开的进程,read就会继续运行,并且获取到管道的内容进行输出。运行结果如图二所示:
情况二:不指定O_NONBOLCK时read的情况
小情况1:FIFO没有数据时read()会进入阻塞
当FIIFO中没有数据,read会进入阻塞状态,这里我们进行实验,为了保证两个程序都能运行,我们这里先打开write程序,然后打开read程序,二者以只读或者只写的方式打开,然后再write中我们先不发送数据,人为的延迟10秒,这时我们会看到read程序进入阻塞装态,十秒延迟以后就变成运行态的。write的代码如下,read的代码不变。
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void main()
{
int fd,ret;
ret = mkfifo("my_fifo", S_IRUSR|S_IWUSR);
if(ret != 0)
{
perror("mkfifo err\n");
}
printf("打开写文件之前\n");
fd = open("my_fifo",O_WRONLY);
if(fd<0)
{
perror("open error \n");
}
printf("打开写文件之后,进入10秒延迟\n");
sleep(10);
char send[100] = "helloworld";
write(fd,send,sizeof(send));
}
运行结果:
小情况2:write程序退出以后read()变为执行
通信过程中若写进程先退出了,此时read()不阻塞,但是若写进程又重新执行,那么read又会阻塞。
实验的代码如下:
write
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd;
char send[100] = "Hello I love you";
fd = open("my_fifo", O_WRONLY);
if(fd<0)
{
perror("open fifo");
}
write(fd, send, strlen(send));
printf("write to my_fifo buf=%s\n",send);
while(1);
return 0;
}
read:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd;
int ret;
ret = unlink("my_fifo"); //删除上次运行后残留的fifo
if(ret<0)
{
perror("unlink fifo");
}
ret = mkfifo("my_fifo", S_IRUSR|S_IWUSR);
if(ret != 0)
{
perror("mkfifo");
}
fd = open("my_fifo", O_RDONLY);
if(fd<0)
{
perror("open fifo");
}
while(1)
{
char recv[100];
bzero(recv, sizeof(recv));
read(fd, recv, sizeof(recv));
printf("read from my_fifo buf=[%s]\n",recv);
sleep(1);
}
return 0;
}
1.首先我们将两个程序都跑起来,可以看到read中第一次可以正确读取,但是读取之后FIFO变为空,read()就会变为阻塞状态。
2.然后将write程序关闭,此时read()变为执行状态,但是或取得数据为空。
3.再将write程序启动,正确读取一次以后又会变成阻塞状态。
情况三:指定O_NONBLOCK
1.先以只读方式打开,若没有其他进程为写而打开,则打开成功且不阻塞
2.先以只写方式打开,若没有其他进程为读而打开,则打开失败,返回-1,说明我们要先打开读方式,再打开写方式。
3.read(),write()方法运行时都不阻塞。
write:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
void main()
{
int fd,isTrue;
char arg []="helliworld";
isTrue = mkfifo("my_fifo",0777);
if(isTrue != 0){
perror("管道创建失败\n");
}
fd = open("my_fifo",O_WRONLY|O_NONBLOCK);
if(fd < 0)
{
perror("open fifo\n");
exit(-1);
}
write(fd,arg,sizeof(arg));
printf("发送成功\n");
while(1);
}
read:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
void main()
{
int fd,isTrue;
char arg [100];
isTrue = mkfifo("my_fifo",0777);
if(isTrue != 0){
perror("管道创建失败\n");
}
fd = open("my_fifo",O_RDONLY|O_NONBLOCK);
if(fd < 0)
{
perror("open fifo\n");
exit(-1);
}
while(1)
{
read(fd,arg,sizeof(arg));
printf("获取到的内容[%s]\n",arg);
sleep(1);
}
}
我们先运行read,再运行write,运行结果如下,可以看到没发送数据的时候read()也不会进行阻塞,只不过读到的数据是空。
作业
1.调用pipe()函数,实现从子进程发送“hello world”到父进程,父进程打印输出的功能。
解析:只需要创建pipe管道,然后创建一个子进程,在子进程中向管道写,父进程中从管道读
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/fcntl.h>
#include <stdlib.h>
void main()
{
int filedes[2];
pid_t pid;
char buff2[20],buff1[]="helloworld";
if(pipe(filedes)<0){
perror("pipe error\n");
exit(-1);
}
pid = fork();
if(pid<0)
{
perror("fork error \n");
exit(-1);
}
if(pid == 0)
{
printf("子进程向管道写入数据\n");
write(filedes[1],buff1,strlen(buff1));
}else{
wait(NULL);
memset(buff2,0,sizeof(buff2));
read(filedes[0],buff2,sizeof(buff2));
printf("父进程读出数据[%s]\n",buff2);
}
}
2.调用dup()函数,通过printf()函数将字符串“hello world”写入到指定文本文件。
解析:文件描述符表中默认的0 1 2 都是打开的,此时我们打开文件文件,就会生成文件描述符3,此时再把1关掉,表中就剩余0 2 3 ,再通过dup(),返回一个最小的即 1 ,就把1重定向到了3即文件。
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/fcntl.h>
#include <stdlib.h>
void main()
{
int fd;
fd=open("test",O_CREAT|O_RDWR,0777);
if(fd<0){
perror("open error \n");
exit(-1);
}
close(1);
dup(fd);
printf("hello world\n");
}
3.通过命名管道,以非阻塞的方式,实现两个进程间收发字符串“hello world”的功能。
解析:只需要指定O_NONBLOCK打开,然后read中读取数据,write中写“hellowoord”就可以了。程序见上面【指定O_NONBLOCK】部分。
谢谢大家的观看,如有错误请指正,谢谢!CSDN记录成长!