您的位置:首页 > 房产 > 家装 > 123logo设计_软考证书含金量排名_中央新闻直播今天_周口网络推广公司

123logo设计_软考证书含金量排名_中央新闻直播今天_周口网络推广公司

2025/4/19 16:32:18 来源:https://blog.csdn.net/2403_82436914/article/details/147305807  浏览:    关键词:123logo设计_软考证书含金量排名_中央新闻直播今天_周口网络推广公司
123logo设计_软考证书含金量排名_中央新闻直播今天_周口网络推广公司

目录

文件IO的其他常用函数总结

1、设备驱动程序的通用接口

函数原型:

参数说明:

返回值:

常见用途:

示例代码1:

示例代码2:

示例代码3:

常见的请求代码:

注意事项:

2、文件描述符进行各种控制操作

函数原型:

参数说明:

常见命令:

返回值:

示例代码 1:

设置文件描述符为非阻塞模式

示例代码 2:

复制文件描述符

示例代码 3:

设置文件描述符标志(FD_CLOEXEC)

常见用途:

3、多路复用 I/O ,让进程监视多个文件描述符1

函数原型:

参数说明:

返回值:

示例代码:

4、多路复用 I/O ,监视多个文件描述符2

函数原型:

参数说明:

返回值:

示例代码:

使用 poll 函数监视标准输入和一个套接字文件描述符是否可读

5、高效的 I/O 多路复用机制

主要特点:

主要函数:

示例代码:

监视标准输入和一个套接字文件描述符是否可读:


文件IO的其他常用函数总结

1、设备驱动程序的通用接口

在 Linux 系统中,ioctl(Input/Output Control)函数是一种用于设备驱动程序的通用接口,允许用户空间程序对设备进行低级控制。它通常用于执行那些无法通过标准 I/O 操作(如 readwrite)完成的特殊操作。

函数原型:
int ioctl(int fd, unsigned long request, ...);
参数说明:
  • fd:文件描述符,表示要操作的设备或文件。通常是通过 open 函数打开设备文件后获得的。

  • request:请求代码,用于指定要执行的具体操作。请求代码通常由设备驱动程序定义,并在头文件中声明。

  • ...:可选参数,具体取决于请求代码。它可以是一个指针、一个整数或其他类型的数据,用于传递或接收与请求相关的数据。

返回值:
  • 成功时返回 0。

  • 失败时返回 -1,并设置 errno 以指示错误原因。

常见用途:

ioctl 函数的用途非常广泛,以下是一些常见的应用场景:

  1. 设备配置:设置或获取设备的参数,例如波特率、数据位、停止位等。

  2. 特殊操作:执行设备特有的操作,如清空缓冲区、复位设备等。

  3. 硬件控制:直接控制硬件设备,例如设置 GPIO 引脚状态、读取硬件寄存器等。

  4. 网络设备:配置网络接口参数,如设置 IP 地址、启用/禁用接口等。

示例代码1:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <termios.h>
​
int main() {int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);if (fd == -1) {perror("open");return -1;}
​struct termios options;if (tcgetattr(fd, &options) != 0) {perror("tcgetattr");close(fd);return -1;}
​cfsetispeed(&options, B9600); // 设置输入波特率为 9600cfsetospeed(&options, B9600); // 设置输出波特率为 9600
​if (tcsetattr(fd, TCSANOW, &options) != 0) {perror("tcsetattr");close(fd);return -1;}
​printf("Serial port baud rate set to 9600\n");close(fd);return 0;
}
示例代码2:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
​
#define DEVICE_FILE "/dev/my_device" // 替换为你的字符设备文件路径
#define SET_DEVICE_PARAM _IOW('k', 1, int)
#define GET_DEVICE_PARAM _IOR('k', 2, int)
​
int main() {int fd;int param;
​// 打开字符设备文件fd = open(DEVICE_FILE, O_RDWR);if (fd == -1) {perror("open");return -1;}
​// 设置设备参数param = 42; // 假设我们想设置的参数值为 42if (ioctl(fd, SET_DEVICE_PARAM, &param) == -1) {perror("ioctl SET_DEVICE_PARAM");close(fd);return -1;}printf("Device parameter set to %d\n", param);
​// 获取设备参数if (ioctl(fd, GET_DEVICE_PARAM, &param) == -1) {perror("ioctl GET_DEVICE_PARAM");close(fd);return -1;}printf("Device parameter retrieved: %d\n", param);
​// 关闭设备文件close(fd);return 0;
}
示例代码3:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
​
#define INTERFACE "eth0" // 替换为你的网络接口名称,例如 "eth0" 或 "wlan0"
​
int main() {int sockfd;struct ifreq ifr;
​// 创建一个套接字sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0) {perror("socket");return -1;}
​// 初始化 ifreq 结构memset(&ifr, 0, sizeof(ifr));strncpy(ifr.ifr_name, INTERFACE, IFNAMSIZ);
​// 获取当前 IP 地址if (ioctl(sockfd, SIOCGIFADDR, &ifr) < 0) {perror("ioctl SIOCGIFADDR");close(sockfd);return -1;}
​// 打印当前 IP 地址struct sockaddr_in *addr = (struct sockaddr_in *)&ifr.ifr_addr;printf("Current IP address of %s: %s\n", INTERFACE, inet_ntoa(addr->sin_addr));
​// 设置新的 IP 地址struct sockaddr_in new_addr;memset(&new_addr, 0, sizeof(new_addr));new_addr.sin_family = AF_INET;new_addr.sin_addr.s_addr = inet_addr("192.168.1.100"); // 替换为你想要设置的 IP 地址
​memcpy(&ifr.ifr_addr, &new_addr, sizeof(new_addr));
​if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) {perror("ioctl SIOCSIFADDR");close(sockfd);return -1;}
​printf("New IP address of %s set to: %s\n", INTERFACE, inet_ntoa(new_addr.sin_addr));
​close(sockfd);return 0;
}
常见的请求代码:

不同的设备驱动程序会定义自己的请求代码,以下是一些常见的请求代码示例:

  • 串口设备:

    • TCGETS:获取当前终端的配置。

    • TCSETS:设置终端的配置。

    • TIOCGWINSZ:获取窗口大小。

    • TIOCSWINSZ:设置窗口大小。

  • 网络设备:

    • SIOCGIFADDR:获取网络接口的 IP 地址。

    • SIOCSIFADDR:设置网络接口的 IP 地址。

    • SIOCGIFFLAGS:获取网络接口的标志。

    • SIOCSIFFLAGS:设置网络接口的标志。

  • 文件系统:

    • BLKGETSIZE:获取块设备的大小。

    • BLKFLSBUF:刷新块设备的缓冲区。

注意事项:
  1. 设备依赖性:ioctl 的请求代码和参数通常依赖于具体的设备驱动程序,因此在使用时需要参考设备的文档或头文件。

  2. 安全性:由于 ioctl 操作通常涉及低级硬件控制,不当使用可能导致系统不稳定或设备损坏。

2、文件描述符进行各种控制操作

在 Linux 系统中,fcntl(File Control)函数用于对文件描述符进行各种控制操作。它提供了比标准 I/O 函数更灵活的文件描述符管理功能,可以用于设置文件状态标志、获取和修改文件描述符的属性等。

函数原型:
int fcntl(int fd, int cmd, ...);
参数说明:
  • fd:文件描述符,表示要操作的文件或设备。通常是通过 open 函数打开文件或设备后获得的。

  • cmd:指定要执行的命令类型。根据不同的命令类型,可能需要额外的参数。

  • ...:可选参数,具体取决于命令类型。它可以是一个整数、一个指针或其他类型的数据,用于传递或接收与命令相关的数据。

常见命令:

fcntl 支持多种命令类型,以下是一些常用的命令及其用途:

  1. F_DUPFD

    • 创建一个指向同一文件的新文件描述符,新文件描述符的值大于或等于 arg

    • 参数:arg(整数)。

    • 返回值:新文件描述符。

  2. F_GETFD

    • 获取文件描述符标志(FD_CLOEXEC)。

    • 参数:无。

    • 返回值:文件描述符标志。

  3. F_SETFD

    • 设置文件描述符标志(FD_CLOEXEC)。

    • 参数:标志值(整数)。

    • 返回值:0(成功)或 -1(失败)。

  4. F_GETFL

    • 获取文件状态标志(如 O_RDONLYO_WRONLYO_NONBLOCK 等)。

    • 参数:无。

    • 返回值:文件状态标志。

  5. F_SETFL

    • 设置文件状态标志。

    • 参数:标志值(整数)。

    • 返回值:0(成功)或 -1(失败)。

  6. F_GETLK``、F_SETLKF_SETLKW`

    • 用于文件锁定操作。

    • 参数:struct flock *lock,表示锁定信息。

    • 返回值:0(成功)或 -1(失败)。

返回值:
  • 成功时返回 0 或新文件描述符(如 F_DUPFD)。

  • 失败时返回 -1,并设置 errno 以指示错误原因。

示例代码 1:
设置文件描述符为非阻塞模式
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
​
int main() {int fd = open("/dev/null", O_RDWR);if (fd == -1) {perror("open");return -1;}
​int flags = fcntl(fd, F_GETFL); // 获取当前文件状态标志if (flags == -1) {perror("fcntl F_GETFL");close(fd);return -1;}
​flags |= O_NONBLOCK; // 设置非阻塞模式if (fcntl(fd, F_SETFL, flags) == -1) {perror("fcntl F_SETFL");close(fd);return -1;}
​printf("File descriptor set to non-blocking mode\n");close(fd);return 0;
}
示例代码 2:
复制文件描述符
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
​
int main() {int fd = open("/dev/null", O_RDWR);if (fd == -1) {perror("open");return -1;}
​int new_fd = fcntl(fd, F_DUPFD, 10); // 创建一个新文件描述符,值大于或等于 10if (new_fd == -1) {perror("fcntl F_DUPFD");close(fd);return -1;}
​printf("New file descriptor: %d\n", new_fd);close(fd);close(new_fd);return 0;
}
示例代码 3:
设置文件描述符标志(FD_CLOEXEC
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
​
int main() {int fd = open("/dev/null", O_RDWR);if (fd == -1) {perror("open");return -1;}
​if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {perror("fcntl F_SETFD");close(fd);return -1;}
​printf("File descriptor set to close-on-exec\n");close(fd);return 0;
}
常见用途:
  1. 设置文件描述符为非阻塞模式

    • 通过设置文件状态标志 O_NONBLOCK,可以将文件描述符设置为非阻塞模式,这在处理 I/O 操作时非常有用,尤其是在多线程或异步编程中。

  2. 复制文件描述符

    • 使用 F_DUPFD 命令可以创建一个指向同一文件的新文件描述符,这在需要将文件描述符传递给其他进程或线程时非常有用。

  3. 设置文件描述符标志

    • 通过设置文件描述符标志 FD_CLOEXEC,可以确保文件描述符在执行 exec 系统调用时不会被继承,从而提高程序的安全性。

  4. 文件锁定

    • 使用 F_GETLKF_SETLKF_SETLKW 命令可以实现文件锁定,防止多个进程同时对同一个文件进行写操作,从而避免数据竞争。

3、多路复用 I/O ,让进程监视多个文件描述符1

在 Linux 系统中,select 函数是一种用于多路复用 I/O 的系统调用,它可以让进程监视多个文件描述符,当其中任意一个文件描述符就绪(可读、可写或有异常条件待处理)时,select 函数就会返回。

函数原型:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
参数说明:
  • nfds:是需要监视的文件描述符的最大值加 1。select 函数会监视从 0 到 nfds - 1 的所有文件描述符。

  • readfds:指向一个 fd_set 类型的指针,表示需要监视可读性的文件描述符集合。如果某个文件描述符对应的位被设置为 1,则表示该文件描述符需要被监视是否可读。

  • writefds:指向一个 fd_set 类型的指针,表示需要监视可写的文件描述符集合。如果某个文件描述符对应的位被设置为 1,则表示该文件描述符需要被监视是否可写。

  • exceptfds:指向一个 fd_set 类型的指针,表示需要监视异常条件的文件描述符集合。如果某个文件描述符对应的位被设置为 1,则表示该文件描述符需要被监视是否有异常条件发生。

  • timeout:指向一个 struct timeval 类型的指针,用于指定 select 函数的超时时间。如果设置为 NULL,则 select 函数会阻塞,直到有文件描述符就绪为止;如果设置为一个非 NULL 的指针,并且指针所指向的 struct timeval 的值为 0,则 select 函数会立即返回,不会阻塞。

返回值:
  • 如果有文件描述符就绪,返回值为就绪的文件描述符数量。

  • 如果超时返回 0。

  • 如果发生错误返回 -1,并设置相应的错误码。

示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>int main() {int sockfd; // 假设这是一个已经创建好的套接字文件描述符fd_set readfds;struct timeval timeout;char buffer[1024];int maxfd;// 初始化文件描述符集合FD_ZERO(&readfds);FD_SET(STDIN_FILENO, &readfds); // 添加标准输入文件描述符FD_SET(sockfd, &readfds); // 添加套接字文件描述符maxfd = (sockfd > STDIN_FILENO ? sockfd : STDIN_FILENO) + 1;// 设置超时时间timeout.tv_sec = 5; // 5 秒timeout.tv_usec = 0;int ret = select(maxfd, &readfds, NULL, NULL, &timeout);if (ret == -1) {perror("select error");exit(EXIT_FAILURE);} else if (ret == 0) {printf("Timeout occurred! No data after 5 seconds.\n");} else {if (FD_ISSET(STDIN_FILENO, &readfds)) {// 标准输入可读if (read(STDIN_FILENO, buffer, sizeof(buffer)) > 0) {printf("Data from stdin: %s\n", buffer);}}if (FD_ISSET(sockfd, &readfds)) {// 套接字可读if (recv(sockfd, buffer, sizeof(buffer), 0) > 0) {printf("Data from socket: %s\n", buffer);}}}return 0;
}

4、多路复用 I/O ,监视多个文件描述符2

在 Linux 系统中,poll 函数也是一种用于多路复用 I/O 的系统调用,它用于监视多个文件描述符,当其中任意一个文件描述符就绪(可读、可写或有异常条件待处理)时,poll 函数就会返回。与 select 函数相比,poll 函数在某些方面有其独特的优势。

函数原型:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数说明:
  • fds:指向一个 struct pollfd 类型的数组,每个数组元素表示一个需要监视的文件描述符及其状态。struct pollfd 的定义如下:

    struct pollfd {int fd;         // 文件描述符short events;   // 请求监视的事件short revents;  // 实际发生的事件
    };
    • fd:需要监视的文件描述符。

    • events:指定需要监视的事件类型,可以是以下值的组合:

      • POLLIN:数据可读。

      • POLLOUT:数据可写。

      • POLLPRI:高优先级数据可读。

      • POLLERR:发生错误。

      • POLLHUP:挂起。

      • POLLNVAL:无效的文件描述符。

    • reventspoll 函数返回时,该字段会包含实际发生的事件。

  • nfdsfds 数组中的元素数量,即需要监视的文件描述符数量。

  • timeout:指定 poll 函数的超时时间,单位为毫秒。如果设置为 -1,则 poll 函数会阻塞,直到有文件描述符就绪为止;如果设置为 0,则 poll 函数会立即返回,不会阻塞。

返回值:
  • 如果有文件描述符就绪,返回值为就绪的文件描述符数量。

  • 如果超时返回 0。

  • 如果发生错误返回 -1,并设置相应的错误码。

示例代码:
使用 poll 函数监视标准输入和一个套接字文件描述符是否可读
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/socket.h>int main() {int sockfd; // 假设这是一个已经创建好的套接字文件描述符struct pollfd fds[2];int ret;char buffer[1024];// 初始化 pollfd 数组fds[0].fd = STDIN_FILENO; // 标准输入文件描述符fds[0].events = POLLIN;fds[1].fd = sockfd; // 套接字文件描述符fds[1].events = POLLIN;// 设置超时时间int timeout = 5000; // 5 秒ret = poll(fds, 2, timeout);if (ret == -1) {perror("poll error");exit(EXIT_FAILURE);} else if (ret == 0) {printf("Timeout occurred! No data after 5 seconds.\n");} else {if (fds[0].revents & POLLIN) {// 标准输入可读if (read(STDIN_FILENO, buffer, sizeof(buffer)) > 0) {printf("Data from stdin: %s\n", buffer);}}if (fds[1].revents & POLLIN) {// 套接字可读if (recv(sockfd, buffer, sizeof(buffer), 0) > 0) {printf("Data from socket: %s\n", buffer);}}}return 0;
}

5、高效的 I/O 多路复用机制

在 Linux 系统中,epoll 是一种高效的 I/O 多路复用机制,用于同时监视多个文件描述符的 I/O 事件。它通过内核态的数据结构优化和事件驱动机制,解决了传统 selectpoll 的性能瓶颈。

主要特点:
  1. 高效的数据结构:

    • epoll 在内核中使用红黑树来管理所有需要监视的文件描述符,增删改操作的时间复杂度为 O(logn),相比 selectpoll 的线性扫描方式,效率更高。

    • 内核维护了一个就绪事件链表,当文件描述符有事件发生时,内核会将其加入到这个链表中。

  2. 事件驱动机制:

    • epoll 使用回调函数机制,当文件描述符有事件发生时,内核会自动将其加入到就绪事件链表中,用户调用 epoll_wait 时,直接返回就绪的文件描述符,无需像 selectpoll 那样遍历整个文件描述符集合。

  3. 支持水平触发和边缘触发:

    • 水平触发(LT):默认模式,只要文件描述符仍然处于就绪状态,就会一直触发事件。

    • 边缘触发(ET):只有当文件描述符的状态从非就绪变为就绪时才会触发事件,通常与非阻塞 I/O 搭配使用。

主要函数:
  1. epoll_create: 创建一个 epoll 实例,返回一个文件描述符,用于后续操作。从 Linux 2.6.8 开始,size 参数被忽略,但必须大于 0。

    int epoll_create(int size);

  2. epoll_ctl: 用于向 epoll 实例中添加、修改或删除需要监视的文件描述符。

    int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    • epfdepoll_create 返回的文件描述符。

    • op:操作类型,可以是 EPOLL_CTL_ADD(添加)、EPOLL_CTL_MOD(修改)或 EPOLL_CTL_DEL(删除)。

    • fd:需要监视的文件描述符。

    • event:指定需要监视的事件类型,如 EPOLLIN(可读)、EPOLLOUT(可写)等。

  3. epoll_wait: 等待 epoll 实例中的事件发生,返回就绪的文件描述符数量。

    int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
    • epfdepoll_create 返回的文件描述符。

    • events:存储就绪事件的数组。

    • maxeventsevents 数组的最大容量。

    • timeout:超时时间(毫秒),-1 表示永久阻塞,0 表示立即返回。

示例代码:
监视标准输入和一个套接字文件描述符是否可读:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <string.h>#define MAX_EVENTS 10int main() {int epfd = epoll_create(2); // 创建 epoll 实例if (epfd == -1) {perror("epoll_create");exit(EXIT_FAILURE);}struct epoll_event ev, events[MAX_EVENTS];ev.events = EPOLLIN; // 监听可读事件ev.data.fd = STDIN_FILENO; // 标准输入文件描述符if (epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) == -1) {perror("epoll_ctl");close(epfd);exit(EXIT_FAILURE);}int sockfd; // 假设这是一个已经创建好的套接字文件描述符ev.data.fd = sockfd;if (epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev) == -1) {perror("epoll_ctl");close(epfd);exit(EXIT_FAILURE);}while (1) {int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1); // 等待事件if (nfds == -1) {perror("epoll_wait");close(epfd);exit(EXIT_FAILURE);}for (int n = 0; n < nfds; ++n) {if (events[n].data.fd == STDIN_FILENO) {char buffer[1024];if (read(STDIN_FILENO, buffer, sizeof(buffer)) > 0) {printf("Data from stdin: %s\n", buffer);}} else if (events[n].data.fd == sockfd) {char buffer[1024];if (recv(sockfd, buffer, sizeof(buffer), 0) > 0) {printf("Data from socket: %s\n", buffer);}}}}close(epfd); // 关闭 epoll 实例return 0;
}

参考资源:https://download.csdn.net/download/2403_82436914/90631882?spm=1001.2014.3001.5503

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com