3.6.1.非阻塞IO
3.6.1.1、阻塞与非阻塞
阻塞:阻塞具有很多优势(是linux系统的默认设置),单路IO的时候使用阻塞式IO没有降低CPU的性能
补充:阻塞/非阻塞, 它们是程序在等待消息(无所谓同步或者异步)时的状态.
阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回。
有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的。
对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。
非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
3.6.1.2、为什么有阻塞式
(1)常见的阻塞:wait(显式回收子进程)、pause、sleep等默认阻塞函数;read或write某些文件时也是默认阻塞式的
(2)阻塞式的好处:当前线程被挂起等待条件满足才返回结果
3.6.1.3、非阻塞
(1)为什么要实现非阻塞
(2)如何实现【非阻塞IO访问】:
1)打开文件时加入O_NONBLOCK
2)fcntl函数,对文件描述符(文件已经打开)进行操作
单路IO就用阻塞式比较好;
多路IO最好是用非阻塞式的。避免资源被一个占有不放,让其他IO有资源可抢。
从CPU的利用角度来看,单路IO就是一对一;多路IO就是一对多,即一个CPU对应多个资源抢占通道。前者单路效率更高,后者需要兼顾分配。单路IO模型只需要监听一个IO流,多路IO模型可以同时监听(内部轮循)多个IO流,CPU要不停去查看。
3.6.2.阻塞式IO的困境
3.6.2.1、程序中读取键盘
3.6.2.2、程序中读取鼠标 cat /dev/input/mouse1
3.6.2.3、程序中同时读取键盘和鼠标
3.6.2.4、问题分析
并不是所有的情况下都适阻塞式io的。
代码示例:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
/*
程序中读取键盘和鼠标
*/
#define NAME "/dev/input/mouse1"
char buff[100]={0};
char buf[100]={0};
int main(int argc,char **argv)
{
int ssize_t=-1;
int fd=-1;
fd=open(NAME,O_RDWR);
if(-1==fd)
{
perror("open");
_exit(-1);
}
printf("打开成功!fd=%d\n",fd);
while(1){
ssize_t=read(fd,buff,sizeof(buff));
if(-1==ssize_t)
{
perror("read");
_exit(-1);
}
printf("读到的鼠标字符数为%d\n",ssize_t);
printf("读到的鼠标字符是[%s]\n",buff);
read(0,&buf,sizeof(buf));
printf("读到的鼠标字符是[%s]\n",buf);
sleep(1);
}
return 0;
}
3.6.3.并发式IO的3种解决方案【并发式IO】
3.6.3.1、非阻塞式IO:性能不够好,有点类似于轮询的方式,CPU不停的去查看
代码示例:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define NAME "/dev/input/mouse1"
char buff[100]={0}; //键盘
char buf[100]={0};//鼠标
int main(void)
{
int ssize_t=-1;
int fd=-1;
int flag=-1;
int ret=-1;
fd=open(NAME,O_RDWR | O_NONBLOCK );
if(-1==fd)
{
perror("open");
_exit(-1);
}
//把标准输入文件描述符0通过fcntl函数变成非阻塞式子
// 把0号文件描述符(stdin)变成非阻塞式的
flag = fcntl(0, F_GETFL); // 先获取原来的flag
flag |= O_NONBLOCK; // 添加非阻塞属性
fcntl(0, F_SETFL, flag); // 更新flag
// 这3步之后,0就变成了非阻塞式的了
while (1)
{
// 读鼠标
memset(buf, 0, sizeof(buf));
//printf("before 鼠标 read.\n");
ret = read(fd, buf, 50);
if (ret > 0)
{
printf("鼠标读出的内容是:[%s].\n", buf);
}
// 读键盘
memset(buff, 0, sizeof(buff));
//printf("before 键盘 read.\n");
ret = read(0, buff, 5);
if (ret > 0)
{
printf("键盘读出的内容是:[%s].\n", buff);
}
}
return 0;
}
3.6.3.2、多路复用IO :性能相对比较好,解决并发性IO的解决
3.6.4.IO多路复用原理
3.6.4.1、何为IO多路复用 【说白了,多路复用其实就是一个管多个】
(1)IO multiplexing
(2)用在什么地方?多路非阻塞式IO(多路及时响应)。
(3)select和poll两个函数:(poll出现的比较晚一点。其性能也要比select函数的性能更好一些)
(4)外部阻塞式(select和poll两个函数本身在调用的时候是阻塞式的,对外表现为阻塞式),内部非阻塞式(
两个函数内部实现的时候,也就是当把要监听的东西(比如A和B两个阻塞式IO)放在这个函数的监听范围)【自动】轮询【这两个多路阻塞式IO】 (轮询的意思就是CPU不停的去查看)
补充:
可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要
等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。
3.6.4.2、select函数介绍:
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout); //nfds表示文件描述符的个数 nfds is the highest-numbered file descriptor in any of the //three sets, plus 1,是指集合中所有文件描述符的范围,【即所有文件描述符的最大值加1】这一点要注意,所有文件描述符的最大值加1,比如一个文件描述符是0,一个文件描述符是1,则为1+1
相关函数:
(1)void FD_ZERO(fd_set *set); //把文件描述符集合清零
(2)void FD_SET(int fd, fd_set *set); //把某个文件描述符添加到这个集合中去
(3)int FD_ISSET(int fd, fd_set *set);
//检查集合中指定的文件描述符是否可以读写 FD_ISSET() tests to see if a file descriptor is part of the set; this is useful after select() returns.
(4)void FD_CLR(int fd, fd_set *set); //把一个给定的文件描述符从集合中删除
相关结构体:
(1)struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor)。
(2)
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。
linux内部代码示例:
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
fd_set rfds;
struct timeval tv;
int retval;
/* Watch stdin (fd 0) to see when it has input. */
FD_ZERO(&rfds); //先把文件描述符集合清零
FD_SET(0, &rfds); //把0文件描述符添加到这个集合中去
/* Wait up to five seconds. */
tv.tv_sec = 5;
tv.tv_usec = 0;
retval = select(1, &rfds, NULL, NULL, &tv);
/* Don't rely on the value of tv now! */
if (retval == -1)
perror("select()");
else if (retval)
printf("Data is available now.\n");
/* FD_ISSET(0, &rfds) will be true. */
else
printf("No data within five seconds.\n");
exit(EXIT_SUCCESS);
}
3.6.4.3、poll函数介绍
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
#define _GNU_SOURCE /* See feature_test_macros(7) */
结构体:
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
代码示例:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
/*
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
*/
#define NAME "/dev/input/mouse1"
int main(void)
{
char buf[100]={0}; //注意这里的定义的buf不能够使用全局的
//int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd a[2]={0};
int fd= open(NAME,O_RDWR); //定义鼠标文件描述符
if(fd<0)
{
perror("open mouse");
_exit(-1);
}
//实例化结构体
a[0].fd=fd; //鼠标
a[0].events=POLLIN;
a[1].fd=0; //键盘
a[1].events=POLLIN;
int ret=poll(a,fd+1,10000);
if(ret<0)
{
perror("poll");
_exit(-1);
}
if(ret==0)
{
printf("超时了");
}
else //判断是谁发生了IO
{
if(a[0].events==a[0].revents) //鼠标
{
memset(buf,0,sizeof(buf));
read(fd,buf,10);
printf("读出来的鼠标内容是:[%s]\n",buf);
}
if(a[1].events==a[1].revents) //键盘
{
memset(buf,0,sizeof(buf));
read(0,buf,50);
printf("读出来的键盘内容是:[%s]\n",buf);
}
}
return 0;
}
3.6.5.IO多路复用实践
3.6.5.1、用select函数实现同时读取键盘鼠标
select()函数实现IO多路复用的步骤
(1)清空描述符集合
(2)建立需要监视的描述符与描述符集合的关系
(3)调用select函数
(4)检查监视的描述符判断是否已经准备好
(5)对已经准备好的描述符进程IO操作
代码示例:
/*
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#define NAME "/dev/input/mouse1"
char buf[100]={0};
int main(void)
{
int fd=-1;
fd_set readfds=-1;
struct timeval time;
int ret=-1;
fd=open(NAME,O_RDWR);
printf("fd=%d\n",fd);
if(-1==fd)
{
perror("open");
_exit(-1);
}
FD_ZERO(&readfds); //清除文件描述符集合
FD_SET(fd,&readfds);
FD_SET(0,&readfds);
time.tv_sec=5; //设置时间为5秒
time.tv_usec=0;
ret=select(3,&readfds,NULL,NULL,&time); //调用select函数 ,注意第一个参数
if (ret < 0) //表示出错
{
perror("select: ");
return -1;
}
else if (ret == 0) //表示超过了规定的时间限制
{
printf("超时了\n");
}
else
{
// 等到了一路IO,然后去监测到底是哪个IO到了,处理之
if (FD_ISSET(0, &readfds))
{
// 这里处理键盘
memset(buf, 0, sizeof(buf));
read(0, buf, 5);
printf("键盘读出的内容是:[%s].\n", buf);
}
if (FD_ISSET(fd, &readfds))
{
// 这里处理鼠标
memset(buf, 0, sizeof(buf));
read(fd, buf, 50);
printf("鼠标读出的内容是:[%s].\n", buf);
}
}
return 0;
}
3.6.5.2、用poll函数实现同时读取键盘鼠标
poll函数介绍:
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
第一个参数 pollfd 结构体定义如下:
引用
/* Data structure describing a polling request. */
struct pollfd
{
int fd; /* poll 的文件描述符. */
short int events; /* fd 上感兴趣的事件(等待的事件或者说是监视的事件). */
short int revents; /* fd 上实际发生的事件. */
};
fd 成员表示感兴趣的,且打开了的文件描述符;
events 成员是位掩码,用于指定针对这个文件描述符感兴趣的事件;
revents 成员是位掩码,用于指定当 poll 返回时,在该文件描述符上已经发生了哪些事情。
events 和 revents 结合下列常数值(宏)指定即将唤醒的事件或调查已结束的 poll() 函数被唤醒的原因,这些宏常数如下:
POLLIN
events 中使用该宏常数,能够在折本文件的可读情况下,结束 poll() 函数。相反,revents 上使用该宏常数,在检查 poll() 函数结束后,可依此判断设备文件是否处于可读状态(即使消息长度是 0)。
POLLPRI
在 events 域中使用该宏常数,能够在设备文件的高优先级数据读取状态下,结束 poll() 函数。相反,revents 上使用该宏常数,在检查 poll() 函数结束后,可依此判断设备文件是否处于可读高优先级数据的状态(即使消息长度是 0)。该宏常数用于处理网络信息包(packet) 的数据传递。
POLLOUT
在 events 域中使用该宏常数,能够在设备文件的写入状态下,结束 poll() 函数。相反,revents 域上使用该宏常数,在检查 poll() 结束后,可依此判断设备文件是否处于可写状态。
POLLERR
在 events 域中使用该宏常数,能够在设备文件上发生错误时,结束 poll() 函数。相反,revents 域上使用该宏函数,在检查 poll() 函数结束后,可依此判断设备文件是否出错。
POLLHUP
在 events 域中使用该宏常数,能够在设备文件中发生 hungup 时,结束 poll() 函数 。相反,在检查 poll() 结束后,可依此判断设备文件是否发生 hungup 。
POLLNVAL
在 events 域中使用该宏函数,能够在文件描述符的值无效时,结束 poll() 。相反,在 revents 域上使用该宏函数时,在检查 poll() 函数后,文件描述符是否有效。可用于处理网络信息时,检查 socket handler 是否已经无效。
最后一个参数 timeout 指定 poll() 将在超时前等待一个事件多长事件。这里有 3 种情况:
1) timeout 为 -1
这会造成 poll 永远等待。poll() 只有在一个描述符就绪时返回,或者在调用进程捕捉到信号时返回(在这里,poll 返回 -1),并且设置 errno 值为 EINTR 。-1 可以用宏定义常量 INFTIM 来代替(在 pth.h 中有定义) 。
2) timeout 等于0
在这种情况下,测试所有的描述符,并且 poll() 立刻返回。这允许在 poll 中没有阻塞的情况下找出多个文件描述符的状态。
3) time > 0
这将以毫秒为单位指定 timeout 的超时周期。poll() 只有在超时到期时返回,除非一个描述符变为就绪,在这种情况下,它立刻返回。如果超时周期到齐,poll() 返回 0。这里也可能会因为某个信号而中断该等待。
和 select 一样,文件描述符是否阻塞对 poll 是否阻塞没有任何影响。
代码示例:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
#define NAME "/dev/input/mouse1"
int main(void)
{
char buf[100]={0}; //注意这里的定义的buf不能够使用全局的
//int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd a[2]={0};
int fd= open(NAME,O_RDWR); //定义鼠标文件描述符
if(fd<0)
{
perror("open mouse");
_exit(-1);
}
//实例化结构体
a[0].fd=fd; //鼠标
a[0].events=POLLIN;
a[1].fd=0; //键盘
a[1].events=POLLIN;
int ret=poll(a,fd+1,10000);
if(ret<0)
{
perror("poll");
_exit(-1);
}
if(ret==0)
{
printf("超时了");
}
else //判断是谁发生了IO
{
if(a[0].events==a[0].revents) //鼠标
{
memset(buf,0,sizeof(buf));
read(fd,buf,10);
printf("读出来的鼠标内容是:[%s]\n",buf);
}
if(a[1].events==a[1].revents) //键盘
{
memset(buf,0,sizeof(buf));
read(0,buf,50);
printf("读出来的键盘内容是:[%s]\n",buf);
}
}
return 0;
}
3.6.6.异步IO
3.6.6.1、何为异步IO
(1)几乎可以认为:异步IO就是操作系统用软件实现的一套中断响应系统(有点类似与硬件中断)。
有两种类型的文件IO同步:同步文件IO和异步文件IO。异步文件IO也就是重叠IO。【在同步文件IO中,线程启动一个IO操作然后就立即进入等待状态,直到IO操作完成后才醒来继续执行。而异步文件IO方式中,线程发送一个IO请求到内核,然后继续处理其他的事情,内核完成IO请求后,将会通知线程IO操作完成了。】
如果IO请求需要大量时间执行的话,异步文件IO方式可以显著提高效率,因为在线程等待的这段时间内,CPU将会调度其他线程进行执行,如果没有其他线程需要执行的话,这段时间将会浪费掉(可能会调度操作系统的零页线程)。如果IO请求操作很快,用异步IO方式反而还低效,还不如用同步IO方式。
同步IO在同一时刻只允许一个IO操作,也就是说对于同一个文件句柄的IO操作是序列化的,即使使用两个线程也不能同时对同一个文件句柄同时发出读写操作。重叠IO允许一个或多个线程同时发出IO请求。
异步IO在请求完成时,通过将文件句柄设为有信号状态来通知应用程序,或者应用程序通过GetOverlappedResult察看IO请求是否完成,也可以通过一个事件对象来通知应用程序。
简单的说“同步在编程里,一般是指某个IO操作执行完后,才可以执行后面的操作。异步则是,将某个操作给系统,主线程去忙别的事情,等内核完成操作后通知主线程异步操作已经完成。”
(2)异步IO的工作方法是:我们当前进程向内核注册一个异步IO事件(使用signal注册一个信号SIGIO的处理函数),然后当前进程可以正常处理自己的事情,内核就去帮你完成或者说是检测你希望的事件是否发生,当异步事件发生后当前进程会收到内核传来的一个SIGIO信号(类似于中断信号)从而执行绑定的处理函数去处理这个异步事件。
3.6.6.2、涉及的函数:
(1)fcntl(F_GETFL、F_SETFL、O_ASYNC、F_SETOWN) 设置异步通知
(2)signal或者sigaction函数(SIGIO)
3.6.3.代码实践
3.6.7.存储映射IO
存储映射IO使一个磁盘文件(物理地址)与存储空间(内存地址)中的一个缓冲区相映射。于是当从缓冲区取数据,就相当于读文件中的相应字节。与此类似,将数据存入缓冲区,则相应字节就自动地写入文件。这样就可以在不使用read和write的情况下执行IO。为了使用这种功能,应首先告诉内核将一个给定的文件映射到一个存储区域中,这是由mmap函数实现的。
3.6.7.1、mmap函数:把一个磁盘文件和一个内存映射起来 内存映射函数
3.6.7.2、LCD显示和IPC之共享内存
3.6.7.3、存储映射IO的特点
(1)共享而不是复制,减少内存操作
(2)处理大文件时效率高,小文件不划算(视频用到的也比较多)
函数原型:
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
addr参数用于指定映射存储区的起始地址,通常将其设置为0,这表示由系统选择该映射区的起始地址,此函数的返回地址是该映射区的起始地址。
fd指定要被映射文件的描述符,在映射该文件到一个地址空间之前,先要打开该文件。
length是映射的字节数。
offset是要映射字节在文件中的起始偏移量。
prot参数说明对映射存储区的保护要求,但不能超过文件open模式访问权限,prot可选值如下:
PROT_EXEC Pages may be executed.
PROT_READ Pages may be read.
PROT_WRITE Pages may be written.
PROT_NONE Pages may not be accessed.
flags参数影响映射存储区的多种属性,其中MAP_SHARED和MAP_PRIVATE两者必须选择其一,还有许多其它的MAP_XXX是可选的。
【还有一种就是多进程下,父进程fork创建一个子进程来处理不同的事情】。
- (50)LINUX应用编程和网络编程之五 Linux信号(进程间通信)
信号实现进程间的通信 3.5.1.什么是信号 ...
- Linux下高并发网络编程
Linux下高并发网络编程 1.修改用户进程可打开文件数限制 在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时, 最高的并发数量都要受到系统对用户单一进程同时可打 ...
- (46)LINUX应用编程和网络编程之一Linux应用编程框架
3.1.1.应用编程框架介绍 3.1.1.1.什么是应用编程 (1)整个嵌入式linux核心课程包括5个点,按照学习顺序依次是:裸机.C高级.uboot和系统移植.linux应用编程和网络编程.驱动. ...
- 老师的blog整理 .网络编程部分 .网络编程部分 前端部分 django基础部分
老师的blog整理 .网络编程部分 .网络编程部分 前端部分 django基础部分 老师的blog整理 python基础部分: 宝哥blog: https://www.cnblogs.com/gu ...
- linux服务端的网络编程
常见的Linux服务端的开发模型有多进程.多线程和IO复用,即select.poll和epoll三种方式,其中现在广泛使用的IO模型主要epoll,关于该模型的性能相较于select和poll要好不少 ...
- linux服务器开发三(网络编程)
网络基础 协议的概念 什么是协议 从应用的角度出发,协议可理解为"规则",是数据传输和数据的解释的规则. 假设,A.B双方欲传输文件.规定: 第一次,传输文件名,接收方接收到文件名 ...
- UNIX网络编程——揭开网络编程常见API的面纱【下】
Linux网络编程数据收发的API流程分析 只要把数据在协议栈中的流动线路和脉络弄清楚了,关于协议栈的实现部分,理解起来就轻松多了.在网络编程章节的数据接收过程中,我们主要介绍过read ...
- UNIX网络编程——揭开网络编程常见API的面纱【上】
Linux网络编程API函数初步剖析 今天我们来分析一下前几篇博文中提到的网络编程中几个核心的API,探究一下当我们调用每个API时,内核中具体做了哪些准备和初始化工作. 1.socket(famil ...
- TCP/IP网络编程之网络编程和套接字
网络编程和套接字 网络编程又称为套接字编程,就是编写一段程序,使得两台连网的计算机彼此之间可以交换数据.那么,这两台计算机用什么传输数据呢?首先,需要物理连接,将一台台独立的计算机通过物理线路连接在一 ...
随机推荐
- 小记---------网页之htmlunit
HtmlUnit是一款开元的Java页面分析工具,可以有效的使用htmlunit分析页面大汉的内容,项目可以模拟浏览器运行,被誉为Java浏览器的开元实现,这个没有界面的浏览器 API的使用 ...
- Integer类的常量池
- thinkphp5 安装
thinkphp 5开始可以使用composer安装 所以在安装thinkphp5.1之前,我们先安装composer ,下载地址:https://www.phpcomposer.com/ 安装完co ...
- 如何使用Resource资源文件
一.目的 为了能够在DisplayAttribute中重复使用同样的名称,将所有的显示字符串集中管理. 二.方法 1.DisplayAttribute本身支持直接使用资源文件. [Display(Re ...
- 用C#控制台编写 推箱子之类的 坐标移动----之二维坐标
//首先用枚举 列出方向 上,下,左,右(枚举的最后一位数后不用符号 否则会报错) public enum dro { up = 1, down = ...
- vim最常用命令
vi/vim常用命令汇总 vi/vim概述 vi/vim是Linux和Unix下的一款非常强大的编辑器,vim是vi的增强 版,命令更加多种和复杂,但是最常用的也就是那几个. vi有三种模式 命令行模 ...
- ABAP中TAB分隔符的使用
在ABAP开发中,存在很多特殊字符,使用情况也不同,下面及时SAP中的TAB分隔符的使用案例: 46C以下的版本: DATA: gc_result(50) type c. constants: con ...
- Linux 硬盘挂载(服务器重启自动挂载)
1.先查看目前机器上有几块硬盘,及已挂载磁盘: fdisk -l 能够查看到当前主机上已连接上的磁盘,以及已经分割的磁盘分区.(下面以/dev/vdb磁盘进行分区.挂载为例,挂载点设置为/data) ...
- 创建AIX克隆盘
1.AIX的克隆盘技术 AIX克隆盘,AIX rootvg的备用替换盘,可以用于保留AIX的原始状态,使AIX在进行升级操作时保留一个AIX操作系统的原始映像,在系统需要时实现即时还原,回到升级操作前 ...
- PAT Basic 1081 检查密码 (15 分)
本题要求你帮助某网站的用户注册模块写一个密码合法性检查的小功能.该网站要求用户设置的密码必须由不少于6个字符组成,并且只能有英文字母.数字和小数点 .,还必须既有字母也有数字. 输入格式: 输入第一行 ...