当前位置: 首页 > news >正文

柳市建设网站自己能做网站吗

柳市建设网站,自己能做网站吗,iis7.0搭建网站,wordpress 首页显示摘要文章目录1 基于文件指针的文件操作1.1 文件的创建#xff0c;打开和关闭1.2 文件读写操作2 基于文件描述符的文件操作2.1 打开、创建和关闭文件2.2 文件读写2.3 改变文件大小2.4 文件映射2.5 文件定位2.6 获取文件信息2.7 复制文件描述符2.8 文件描述符和文件指针2.9 标准输入… 文章目录1 基于文件指针的文件操作1.1 文件的创建打开和关闭1.2 文件读写操作2 基于文件描述符的文件操作2.1 打开、创建和关闭文件2.2 文件读写2.3 改变文件大小2.4 文件映射2.5 文件定位2.6 获取文件信息2.7 复制文件描述符2.8 文件描述符和文件指针2.9 标准输入输出文件描述符2.10 管道3 Linux的目录操作3.1 获取和切换当前目录3.2 创建和删除目录3.3 目录的存储原理3.4 目录相关操作4 I/O 多路转接模型4.1 读取文件的阻塞4.2 I/O多路转接模型和select4.3 select的退出机制4.4 select函数的超时处理机制4.5 写集合的原理写在最后这篇文章作者写了好久其中的内容完全是知识盲区希望读者仔细阅读持续关注下一篇内容讲解Linux的进程。使用环境Ubuntu18.04 使用工具VMWare workstations xshell作者在学习Linux的过程中对常用的命令进行记录通过思维导图的方式梳理知识点并且通过xshell连接vmware中ubuntu虚拟机进行操作并将练习的截图注解每句话对应相应的命令读者可以无障碍跟练。第四次练习的重点在于Linux的目录操作和文件操作。1 基于文件指针的文件操作 Linux 中对目录和设备的操作都是文件操作文件分为普通文件目录文件链接文件和设备文件。在Linux中对文件的操作是使用文件指针来访问文件的方法是由标准 C 规定的基于文件指针的文件操作函数是 ANSI 标准函数库的一部分。所以本次练习基本是复习C语言操纵文件的功能。其中mmap的内容先按下不表讲完目录操作后继续。 1.1 文件的创建打开和关闭 #include stdio.h //头文件包含 FILE* fopen(const char* path, const char* mode);//文件名 模式 int fclose(FILE* stream);fopen 创建的文件的访问权限将以 0666 与当前的 umask 结合来确定。 下面案例中使用了wb方式创建了文件并且关闭文件。 1.2 文件读写操作 数据块读写操作 #include stdio.h size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream);fread 从文件流 stream 中读取 nmemb 个元素写到 ptr 指向的内存中每个元素的大小为 size 个字节 fwrite 从 ptr 指向的内存中读取 nmemb 个元素写到文件流 stream 中每个元素 size 个字节 格式化读写操作 #include stdio.h int printf(const char *format, ...); //相当于 fprintf(stdout,format,…); int scanf(const char *format, …); int fprintf(FILE *stream, const char *format, ...); int fscanf(FILE *stream, const char *format, …); int sprintf(char *str, const char *format, ...); //eg:sprintf(buf,”the string is;%s”,str); int sscanf(char *str, const char *format, …); f 开头和s 开头的区别 fprintf 将格式化后的字符串写入到文件流 stream 中 sprintf 将格式化后的字符串写入到字符串 str 中 注意fopen的参数需要改成wb支持读和写否则什么都读不出来作者测了半小时才发现是这个问题… 单个字符读写操作 #include stdio.h int fgetc(FILE *stream); int fputc(int c, FILE *stream); int getc(FILE *stream);//等同于 fgetc(FILE* stream) int putc(int c, FILE *stream);//等同于 fputc(int c, FILE* stream) int getchar(void);//等同于 fgetc(stdin); int putchar(int c);//等同于 fputc(int c, stdout);字符串读写操作 char *fgets(char *s, int size, FILE *stream); int fputs(const char *s, FILE *stream); int puts(const char *s);//等同于 fputs(const char *s,stdout); char *gets(char *s);//等同于 fgets(const char *s, int size, stdin);文件定位操作 rewind函数已经使用过了其余的请读者自行练习 #include stdio.h int feof(FILE * stream); //通常的用法为 while(!feof(fp))没什么太多用处 int fseek(FILE *stream, long offset, int whence); //设置当前读写点到偏移 whence 长度为 offset 处 long ftell(FILE *stream); //用来获得文件流当前的读写位置 void rewind(FILE *stream); //把文件流的读写位置移至文件开头 fseek(fp, 0, SEEK_SET);2 基于文件描述符的文件操作 POSIX标准支持另一类不带缓冲区的IO。使用文件描述符描述文件文件描述符是一个非0整数。原理上来说每次打开文件进程地址空间内核部分会维护一个已经打开的文件的数组文件描述符就是这个数组的索引。因此文件描述符可以实现进程和打开文件之间的交互。 2.1 打开、创建和关闭文件 使用open函数可以打开或者创建一个并打开一个文件使用creat函数可以创建一个文件 #include sys/types.h//头文件#include sys/stat.h#include fcntl.hint open(const char *pathname, int flags);//文件名 打开方式int open(const char *pathname, int flags, mode_t mode);//文件名 打开方式 权限int creat(const char *pathname, mode_t mode);//文件名 权限//creat 现在已经不常用了它等价于open(pathname,O_CREAT|O_TRUNC|O_WRONLY,mode);#include unistd.hint close(int fd);//fd 表示文件描述词,是先前由 open 或 creat 创建文件时的返回值。从上述man手册中可以看到函数的返回值都是int类型也就是说函数执行成功后会返回一个文件描述符表示已经打开的文件执行失败会返回-1并设置相应的errno。flags表示打开或创建的方式mode表示文件的访问权限。 掩码含义O_RDONLY以只读的方式打开O_WRONLY以只写的方式打开O_RDWR以读写的方式打开O_CREAT如果文件不存在则创建文件O_EXCL仅与 O_CREAT 连用如果文件已存在则 open 失O_APPEND已追加的方式打开文件每次调用 write 时文件指针自动先移到文件尾用于多进程写同一个文件的情况。O_NONBLOCK非阻塞方式打开无论有无数据读取或等待都会立即返回进程之中O_NODELAY非阻塞方式打开O_SYNC同步打开文件只有在数据被真正写入物理设备设备后才返回文件使用后要记得使用close关闭文件。close关闭后该进程队文件所加的锁全部被释放并且是文件的打开索引计数-1只有文件的打开引用计数变为0后文件才会被真正的关闭。使用ulimit -a 命令可以查看单个进程能同事打开文件的上限。下面展示open函数创建文件的操作其他函数和参数读者自行尝试。 2.2 文件读写 使用read函数和write函数他们统称为不带有缓冲区的IO。读取完了返回0出错返回-1其余情况返回读写的字符数。 #include unistd.h ssize_t read(int fd, void *buf, size_t count);//文件描述符 缓冲区 长度 ssize_t write(int fd, const void *buf, size_t count);注意 作者在测试读写案例时发现file1文件忘记加权限导致这个案例花费了好久读者可以自行修改上面的main.c源文件添加权限参数也可以使用如下命令修改文件权限。 修正案例如下 2.3 改变文件大小 ftruncate 函数可以改变文件大小必须以写入模式 打开文件如果文件大小比参数length大就会删除超过的部分实际上是修改了文件的inode信息。成功返回0否则返回-1 #include unistd.h int ftruncate(int fd, off_t length);2.4 文件映射 使用 mmap 接口可以实现直接将一个磁盘文件映射到存储空间的一个缓冲区上 面无需使用 read 和 write 进行 IO #include sys/mman.h void *mmap(void *adr, size_t len, int prot, int flag, int fd, off_t offset);adr参数用于指定映射存储区的起始地址。设为NULL由操作系统自动分配通常在堆空间。fd参数是一个文件描述符必须是打开的状态。prot参数表示权限PROT_READ,PROT_WRITE 表示可读可写flag表示这片空间是否可以反映到磁盘上的参数MAP_SHARED、MAP_PRIVATE。offset参数需是 4k 的整数倍。 使用 mmap 函数经常配合函数 ftruncate 来扩大文件大小原因是分配的缓冲区大小和偏移量大小是有限制的必须是虚拟内存页大小的整数倍。如果文件较小那么超过文件大小返回的缓冲区操作将不会修改文件。如果文件大小为0还会出现Bus error异常。 2.5 文件定位 函数lseek 将文件指针设定到相对于whence偏移值为offset的位置。他的返回值是读写点距离文件开始的距离。前面使用过了这里不再做演示了。利用lseek函数可以实现文件空洞即一个空文件可以定位到便宜文件开始1024字节的地方再写入一个字符相当于给该文件分配了1025个字节形成文件空洞。通常用于多进程之间通信的共享内存。 #include sys/types.h #include unistd.h off_t lseek(int fd, off_t offset, int whence);//fd 文件描述词 //whence 可以是下面三个常量的一个 //SEEK_SET 从文件头开始计算 //SEEK_CUR 从当前指针开始计算 //SEEK_END 从文件尾开始计算2.6 获取文件信息 通过fstat和stat函数获取文件信息调用后文件信息被填充到结构体struct stat变量中。 #include sys/types.h #include sys/stat.h #include unistd.h int stat(const char *file_name, struct stat *buf); //文件名 stat 结构体指针 int fstat(int fd, struct stat *buf); //文件描述词 stat 结构体指针struct stat { dev_t st_dev; /* ID of device containing file -文件所在设备的ID*/ ino_t st_ino; /* inode number -inode节点号*/ mode_t st_mode; /* protection -保护模式?*/ nlink_t st_nlink; /* number of hard links -链向此文件的连接数(硬连接)*/ uid_t st_uid; /* user ID of owner -user id*/ gid_t st_gid; /* group ID of owner - group id*/ dev_t st_rdev; /* device ID (if special file) -设备号针对设备文件*/ off_t st_size; /* total size, in bytes -文件大小字节为单位*/ blksize_t st_blksize; /* blocksize for filesystem I/O -系统块的大小*/ blkcnt_t st_blocks; /* number of blocks allocated -文件所占块数*/ time_t st_atime; /* time of last access -最近存取时间*/ time_t st_mtime; /* time of last modification -最近修改时间*/ time_t st_ctime; /* time of last status change - */ };同时对于struct stat结构体st_node,有一组宏可以进行文件类型的判断 宏描述S_ISLNK(mode)判断是否是符号链接S_ISREG(mode)判断是否是普通文件S_ISDIR(mode)判断是否是目录S_ISCHR(mode)判断是否是字符型设备S_ISBLK(mode)判断是否是块设备S_ISFIFO(mode)判断是否是命名管道S_ISSOCK(mode)判断是否是套接字2.7 复制文件描述符 系统调用函数dup函数参数是一个旧的文件描述符返回一个新的文件描述符这个新的文件描述符是旧文件描述符的拷贝。系统调用函数dup2函数参数是一个旧的文件描述符和一个新的文件描述符函数成功后新的文件描述符编程旧的文件描述符的拷贝。 #include unistd.h int dup(int oldfd); int dup2(int oldfd, int newfd);正常情况下如果直接用整型变量拷贝文件描述符使得两个变量都指向一个打开的文件但是内核中文件打开的引用计数还是1无论是close哪一个都会导致文件的关闭。而如果使用dup或者dup2函数则不会出现这种情况。 int fd open(argv[1],O_RDWR); int fd1 fd; close(fd);//会导致文件关闭 char buf[128] {0}; int ret read(fd1, buf, sizeof(buf)); //读取失败dup的原理当使用文件时进程地址空间应当分配一篇空间存放打开文件的inode信息此时文件已经调入内存Linux使用链表的方式管理inode信息即inode表。inode表中如果该文件的引用计数为0则从inode表中删除该文件的inode表项。dup操作正是拷贝了inode表项使得inode的引用计数1所以close其中一个拷贝时不会导致文件关闭。 使用dup函数重定向序首先打开了一个文件返回一个文件描述符因为默认的就打开了 0,1,2 表示标准输入标准输出标准错误输出。用 close(STDOUT_FILENO);则表示关闭标准输出此时文件描述符 1 就空着然后dup(fd);则会复制一个文件描述符到当前未打开的最小描述符此时这个描述符为 1。后面关闭 fd 自身然后在用标准输出的时候发现标准输出重定向到你指定的文件了。那么 printf所输出的内容也就直接输出到文件因为 printf 的原理就是将内容输入到描述符为 1 的文件里面。 #include func.h int main(int argc, char *argv[]) {ARGS_CHECK(argc,2);int fd open(argv[1],O_RDWR);ERROR_CHECK(fd,-1,open);printf(\n);close(STDOUT_FILENO);int fd1 dup(fd);printf(fd1 %d\n, fd1);close(fd);printf(the out of stdout\n);return 0; }2.8 文件描述符和文件指针 fopen 函数实际在运行的过程中也获取了文件的文件描述符。使用 fileno 函数可以得到文件指针的文件描述符。当使用 fopen 获取文件指针以后依然是可以使用文件描述符来执行 IO。 printf(fd %d\n, fd); char buf[128] {0}; read(fd, buf, 5); printf(buf %s\n, buf); //使用 read 接口也是能够正常读取内容的fopen原理fopen执行时会先调用open函数打开文件并且获取文件的信息然后fopen函数会在用户态申请一块空间作为缓冲区。fopen的好处因为read和write是系统调用函数需要频繁在用户态和核心态切换耗时间多。而借助用户态缓冲区可以先将文件内容读入缓冲区后续再对文件进行操作。fdopen函数可以根据文件描述符fd 生成用户态缓冲区mode包括r、w、a、r、w、a几种类型。 #include stdio.h FILE *fdopen(int fd, const char *mode);注意如果获取了文件指针不要使用文件描述符的方式关闭文件如下操作 FILE* fp fopen(argv[1],rb); close(fileno(fp));//如果使用 fdfileno(fp),那么 close 以后 fd 的数值不会发生改变 //出现报错 fgets: Bad file descriptor2.9 标准输入输出文件描述符 与标准的输入输出流对应在更底层的实现是用标准输入、标准输出、标准错误文件描述符表示的。它们分别用 STDIN_FILENO、STDOUT_FILENO 和STDERR_FILENO 三个宏表示值分别是 0、1、2 三个整型数字 2.10 管道 管道文件用于数据通信的一种文件半双工通信它在 ls -l 命令中显示为p管道文件无法存储数据。 传输方式含义全双工双方可以同时向另一方发送数据半双工某个时刻只能有一方向另一方发送数据其他时刻的传输方向可以相反单工永远只能由一方向另一方发送数据 linux命令操作管道 $ mkfifo [管道名字] 使用 cat 打开管道可以打开管道的读端 $ cat [管道名字] 打开另一个终端向管道当中输入内容可以实现写入内容 $ echo “string” [管道名字] 此时读端也会显示内容注意禁止使用vim打开编译管道文件 3 Linux的目录操作 3.1 获取和切换当前目录 getcwd函数将目前的工作目录绝对路径复制到buf所指的内存空间中参数size为buf的空间大小。若buf为NULLgetcwd会根据size的大小自动配置内存如果size也为0getcwd会根据目录字符串大小来分配相应大小的空间进程使用完字符串后会自动free释放空间。最常用的形式getcwd(NULL, 0); #include unistd.h //头文件 char *getcwd(char *buf, size_t size); //获取当前目录相当于 pwd 命令 char *getwd(char *buf); char *get_current_dir_name(void); int chdir(const char *path); //修改当前目录即切换目录相当于 cd 命令chdir函数用来修改当前工作目录修改成参数path所指的目录读者自行尝试。 #includeunistd.h int main() { chdir(“/tmp”); printf(“current working directory: %s\n”,getcwd(NULL,0)); }3.2 创建和删除目录 创建和删除目录的函数和Linux下创建和删除目录的命令一样 #include sys/stat.h #include sys/types.h #include unistd.h int mkdir(const char *pathname, mode_t mode); //创建目录,mode 是目录权限 int rmdir(const char *pathname); //删除目录如何修改环境变量 查看环境变量 $ echo $PATH 修改环境系统路径变量只对本次生效 $ export PATH$PATH:新目录3.3 目录的存储原理 为了定位文件在磁盘中的位置文件系统使用专门的索引结构来管理所有的文件。索引结构的基本单位是索引结点其中包含了文件的位置、文件类型、权限、修改时间等。文件系统将所有索引结点用数组存储起来并利用一个位图实现高效管理文件信息。Linux中目录是一种特殊文件目录的大小总是固定的。目录的数据块中吧很多文件的文件名和索引结点存放在一起。因为文件名大小不一所以采取链式结构。链式结构的结点就是dirent结点定义如下 struct dirent{ino_t d_ino; //该文件的 inodeoff_t d_off; //到下一个 dirent 的偏移unsigned short d_reclen;//文件名长度unsigned char d_type; //所指的文件类型char d_name[256]; //文件名 };3.4 目录相关操作 #include sys/types.h #include dirent.h DIR *opendir(const char *name); //打开一个目录 struct dirent *readdir(DIR *dir); //读取目录的一项信息并返回该项信息的结构体指针 void rewinddir(DIR *dir); //重新定位到目录文件的头部 void seekdir(DIR *dir,off_t offset);//用来设置目录流目前的读取位置 off_t telldir(DIR *dir); //返回目录流当前的读取位置 int closedir(DIR *dir); //关闭目录文件读取目录信息的步骤 用opendir函数打开目录获得DIR指针。DIR称为目录流类似于标准输入输出每次使用readdir后它会将位置移动到下一个文件。使用readdir函数迭代读取目录的内容用closedir函数关闭目录 inode索引结点描述了文件在磁盘上的具体位置信息。在ls命令中添加 -i参数可以查看文件的inode信息。那么所谓的硬链接就是指inode相同的文件。一个inode的节点上的硬链接个数就成为引用计数。软链接不计。 $ ls -ial 查看所有文件的 inode 信息 $ ln 当前文件 目标 建立名为“目标”的硬链接当inode计数为0时才会将磁盘内容移出文件管理系统即断开和目录的链接。为了避免引起死锁普通用户不能使用ln命令为目录建立硬链接。看一个深度优先遍历访问目录的例子 seekdir()函数用来设置目录流目前的读取位置再调用 readdir()函数时便可以从此新位置开始读取。参数 offset 代表距离目录文件开头的偏移量使用 readddir()时如果已经读取到目录末尾又想重新开始读则可以使用rewinddir 函数将文件指针重新定位到目录文件的起始位置telldir()函数用来返回目录流当前的读取位置 4 I/O 多路转接模型 4.1 读取文件的阻塞 阻塞在目前的模式下read函数如果不能从文件中读取内容就将进程的状态切换到阻塞状态不再继续执行 //在写端写入时添加 sleep(10) ... sleep(10); write ... //再次测试的时候发现读端会明显延迟实现即时聊天 管道文件是半双工通信可以使用2个管道文件实现全双工通信即两个进程分别监听两个管道文件一边读一边写。有读者就比较疑惑了为什么用两个管道文件就能实现两个普通文件无法实现通信呢原因就在于管道文件是半双工通信其中没有内容的时候可以阻塞进程。代码如下 //1 号进程 #include header.h #include stdio.hint main(int argc, char *argv[]) { ARGS_CHECK(argc,3); int fdr open(argv[1],O_RDONLY);//管道打开的时候必须要先将读写端都打开之后才能继续 int fdw open(argv[2],O_WRONLY); printf(I am chat1\n); char buf[128] {0}; while(1) { memset(buf,0,sizeof(buf));//将buf缓冲区清0 read(STDIN_FILENO, buf, sizeof(buf));//将标准输入的字符串写入buf缓冲区 write(fdw, buf, strlen(buf)-1);//写入2号管道文件 memset(buf,0,sizeof(buf)); read(fdr, buf, sizeof(buf));//读取1号管道文件 printf(buf %s\n, buf); } return 0; }//2 号 #include header.h #include stdio.h int main(int argc, char *argv[]) { ARGS_CHECK(argc,3); int fdw open(argv[1],O_WRONLY);//管道打开的时候必须要先将读写端都打开之后才能继续 int fdr open(argv[2],O_RDONLY); printf(I am chat2\n); char buf[128] {0}; while(1) { memset(buf,0,sizeof(buf)); read(fdr, buf, sizeof(buf));//读取1号管道文件 printf(buf %s\n, buf); memset(buf,0,sizeof(buf)); read(STDIN_FILENO, buf, sizeof(buf));//从标准输入读取字符串到buf缓冲区 write(fdw, buf, strlen(buf)-1);//写入2号管道文件 } return 0; } //这里经常会有阻塞使用管道文件达成的实时聊天是有很明显的缺陷必须两个进程一人说一句话不能连续说多句话总一方总是在等待后序将会继续完善通信的操作。这里拓展一点内容我在408考研中了解的进程通信。进程通信的方式有三种第一种是共享内存即通信的进程共享一块内存空间。第二种通信方式是消息传递两个进程使用操作系统提供的消息传递方法实现进程通信。进程通过系统提供的发送消息和接受消息两个原语进行数据交换。第三种就是上面演示的管道通信管道实际上就是一个缓冲区其大小在linux中设定为4KB管道的读操作比写操作要快当管道文件中的数据被读取后系统会调用read()阻塞等待数据的输入。 4.2 I/O多路转接模型和select I/O多路转接模型如果请求的I/O操作阻塞且它不是真正的阻塞I/O而是让其中一个函数等待在这期间I/O 还能进行其他操作。接下来要介绍的 select()函数就是属于这种模型。使用select函数的原因select可以完成非阻塞方式工作的程序它能够监视我们需要监视的文件描述符的变化情况—— 读写或是异常 。 #include sys/select.h #include sys/time.h int select(int maxfd, fd_set *readset,fd_set *writeset, fd_set *exceptionset, struct timeval * timeout); //返回值为0 代表超时返回值为-1 代表出错 /* select函数的参数解释 maxfd最大的文件描述符其值应该为最大的文件描述符字 1 readset内核读操作的描述符字集合 writeset内核写操作的描述符字集合 exceptionset内核异常操作的描述符字集合 timeout等待描述符就绪需要多少时间。NULL 代表永远等下去一个固定值代表等待固定时间0 代表根本不等待检查描述字之后立即返回 *///readset、writeset、exceptionset 都是 fd_set 集合 //集合的相关操作如下 void FD_ZERO(fd_set *fdset); /* 将所有 fd 清零 */ void FD_SET(int fd, fd_set *fdset); /* 增加一个 fd */ void FD_CLR(int fd, fd_set *fdset); /* 删除一个 fd */ int FD_ISSET(int fd, fd_set *fdset); /* 判断一个 fd 是否有设置 */一般情况下使用select之前要使用 FD_ZERO 和 FD_SET 来初始化文件描述符集在使用 select 函数时可循环使用 FD_ISSET 测试描述符集在执行完对相关文件描述符之后使用 FD_CLR 来清除描述符集。 //chat1.c //编译后运行 //$ ./chat1 1.pipe 2.pipe #include func.h int main(int argc, char *argv[]) {ARGS_CHECK(argc,3);int fdr open(argv[1],O_RDONLY);//管道打开的时候必须要先将读写端都打开之后才能继续int fdw open(argv[2],O_WRONLY);//对管道2只读打开printf(I am chat1\n);char buf[128] {0};//设置缓冲区int ret;fd_set rdset;//rdset文件描述符while(1){FD_ZERO(rdset);//清空fdset中的fdFD_SET(STDIN_FILENO,rdset);//将标准输入添加到集合中FD_SET(fdr,rdset);//将只读的管道文件1添加到集合中ret select(fdr1, rdset, NULL, NULL, NULL);//设置最大文件操作符将rdset设为读区的描述字集合if(FD_ISSET(STDIN_FILENO, rdset)){ //判断rdset集合中是否设置了标准输入输出memset(buf,0,sizeof(buf)); //清空缓冲区read(STDIN_FILENO, buf, sizeof(buf)); //读取标准输入的内容到buf缓冲区中write(fdw, buf, strlen(buf)-1); //将buf缓冲区的内容写入管道文件2即fdw打开的只写文件}if(FD_ISSET(fdr, rdset)){ //判断rdset集合中是否设置了只读文件的描述字memset(buf,0,sizeof(buf)); //清空缓冲区read(fdr, buf, sizeof(buf)); //将fdr对应的只读文件内容读取到缓冲区bufprintf(buf %s\n, buf); //打印buf内容}}return 0; }//chat2.c //编译后运行(注意管道建立连接的顺序) //$ ./chat2 1.pipe 2.pipe #include func.h int main(int argc, char *argv[]) {ARGS_CHECK(argc,3);int fdw open(argv[1],O_WRONLY);//管道打开的时候必须要先将读写端都打开之后才能继续int fdr open(argv[2],O_RDONLY);printf(I am chat2\n);char buf[128] {0};int ret;fd_set rdset;while(1){FD_ZERO(rdset);FD_SET(STDIN_FILENO,rdset);FD_SET(fdr,rdset);ret select(fdr1, rdset, NULL, NULL, NULL);if(FD_ISSET(STDIN_FILENO, rdset)){memset(buf,0,sizeof(buf));read(STDIN_FILENO, buf, sizeof(buf));write(fdw, buf, strlen(buf)-1);}if(FD_ISSET(fdr, rdset)){memset(buf,0,sizeof(buf));read(fdr, buf, sizeof(buf));printf(buf %s\n, buf);}}return 0; }注意 作者在练习这段代码的时候发现chat2.c中的fdr和fdw定义语句不能交换否则会进程会一直等待必须使用CtrlZ终止。原因就是如果两个文件同时以只读的方式打开管道文件都在等待一个写进程进入管道就会导致互相等待。fdset 实际上是一个文件描述符的位图采用数组的形式来存储。下面是一个简化版本的实现方法 //fd_set 的成员是一个长整型的结构体 typedef long int __fd_mask; //将字节转化为位 #define __NFDBITS (8 * (int) sizeof (__fd_mask)) //位图-判断是否存在文件描述符 d #define __FD_MASK(d) ((__fd_mask) (1UL ((d) % __NFDBITS))) //select 和 pselect 的 fd_set 结构体 typedef struct {//成员就是一个长整型的数组用来实现位图__fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS]; } fd_set; // fd_set 里面文件描述符的数量可以使用 ulimit -n 进行查看 #define FD_SETSIZE __FD_SETSIZEmaxfd 是最大描述符加 1 的原因 当传入 fdmax 的时候select 会监听0~fdmax-1 的文件描述符。 4.3 select的退出机制 管道写端先关闭的时候读端的read会返回一个0操作系统会将管道状态设置为可读这个可读状态会导致select函数不会阻塞进入死循环。如果读端先关闭写端会直接崩溃。后续网络编程会讲 //将写端的程序修改为如此 #include func.h int main(int argc, char *argv[]) {ARGS_CHECK(argc,2);int fdw open(argv[1],O_WRONLY);ERROR_CHECK(fdw,-1,open);printf(fdw %d\n,fdw);close(fdw);//这里将写端直接关闭sleep(10);//然后睡眠 10sreturn 0; }为了避免死循环需要对退出读端继续兼容处理就是当read的返回值为0的时候就退出程序 if(FD_ISSET(STDIN_FILENO, rdset)){ //判断集合中是否有标准输入memset(buf,0,sizeof(buf)); //清空缓冲区read_ret read(STDIN_FILENO, buf, sizeof(buf)); //将标准输入的内容读取到buf缓冲区中if(read_ret 0){ //判断read是否为0如果为0就break退出循环printf(chat is broken!\n);break;}write(fdw, buf, strlen(buf)-1); //否则就将读入的内容写入缓冲区中 }if(FD_ISSET(fdr, rdset)){memset(buf,0,sizeof(buf));read_ret read(fdr, buf, sizeof(buf));if(read_ret 0){printf(chat is broken!\n);break;} printf(buf %s\n, buf);} ... #使用 ctrlc 终止程序会导致程序的返回值不为 0 #可以改用 ctrld 来终止 stdin相当于输入了 EOF #$?代表了上个执行程序的返回值 $echo $?4.4 select函数的超时处理机制 使用 timeval 结构体可以设置超时时间。传入 select 函数中的 timeout 参数是一个 timeval 结构体指针timeval 结构体的定义如下 struct timeval {long tv_sec;//秒long tv_usec;//微秒 };//用法 ... struct timeval timeout; //定义一个timeval的变量 while(1){bzero(timeout, sizeof(timeout));timeout.tv_sec 3;//设置等待时间如果是NULL则永远等待如果是0则不等待ret select(fdr1, rdset, NULL, NULL, timeout);//传入参数if(ret 0){...}else printf(time out!\n); }使用的超时判断的时候要注意每次调用 select 之前需要重新为 timeout 赋值因为调用 select 会修改 timeout 里面的内容。 4.5 写集合的原理 写阻塞和写就绪当管道的写端向管道中写入数据达到上限后4KB后序的写入操作就会导致进程进入一个阻塞态等待进程将数据从管道中读出称为写阻塞。如果管道中的数据被读出写端可以继续写入管道就称为写就绪。select 也可以设置专门的写文件描述符集合select 可以监听处于写阻塞状态下的文件一旦文件转为写就绪就可以将进程转换为就绪态。 #include func.h int main(int argc, char* argv[]) {ARGS_CHECK(argc, 2); //用同一个管道进行测试iint fdr open(argv[1],O_RDWR);int fdw open(argv[1],O_RDWR);//可以一次性打开管道的读写端fd_set rdset,wrset; int ret;char buf[128];while(1){ FD_ZERO(rdset); //清空写集合FD_ZERO(wrset); //清空读集合FD_SET(fdr, rdset);//将读操作放入读集合FD_SET(fdw, wrset);//将写操作放入写集合ret select(fdw1, rdset, wrset, NULL, NULL);//设置select监听的读写集合位置if(FD_ISSET(fdr, rdset)){ //如果返回值不为0即没有超时可以读bzero(buf, sizeof(buf));//清空缓冲区bufread(fdr, buf, sizeof(buf));//读取管道内容到缓冲区中puts(buf);usleep(250000); //进程挂起0.25秒} if(FD_ISSET(fdw, wrset)){ //如果返回值不为0即可以写write(fdw,helloworld, 10);usleep(500000);//写后进程挂起0.5秒给读操作的时间} } }写在最后这篇文章作者写了好久其中的内容完全是知识盲区希望读者仔细阅读持续关注下一篇内容讲解Linux的进程。
http://www.dnsts.com.cn/news/151505.html

相关文章:

  • 创业给别人做网站怎么样网上购物商城首页
  • 做微商那个网站好常德自助建站seo
  • 做a免费视频在线观看网站事件营销定义
  • 抖音营销网站建设价格wordpress 类似的博客系统
  • 免费软件下载网站入口正能量广州个人网页制作
  • 乐清网站设计制作化妆品网站建设推广方案
  • 外贸网址导航优化设计官方网站
  • 建设网站行业云简述seo对各类网站的作用
  • 建设网站公司怎么建站5118站长网站
  • 建设一个外贸网站.系统之家官网
  • 可以做设计兼职的网站有哪些工作私人承包小工程哪里有
  • 建设银行网站的特点分析温州建筑信息平台
  • 百度网站提交入口网址怎么做网站海报轮播图
  • 做网站水印网络推广专员百度百聘
  • 上海网站建设联系电wordpress 页面显示文章
  • 万州建设工程信息网站网页浏览器证书失效怎么修复
  • 男女做羞羞羞的网站优化是什么
  • gta手机网站大全去什么网站做推广
  • HTML电影订票网站开发网站 备案 注销 影响
  • 开购物网站需要多少钱360免费建站为什么注册不了
  • 百度做网站吗网站内容更新外包
  • 万家灯火网站建设辽宁建设工程信息网官网查不良行为
  • 阿里云网站备案要多久美丽深圳公众号二维码原图
  • 建设人才库网站网站设计公司排名知乎
  • 微信小程序可以做视频网站吗做今网站
  • mui做网站的好处做个网站要钱吗
  • 做软装找产品上哪个网站怎么做内网网站
  • 漂亮的设计类图片网站必须做网站等级保护
  • 网站建设价格一览表石家庄住房与城乡建设官网
  • windows做网站的工具行业网站推广怎么做