文件和目录管理


一.文件与其元数据

我们首先看一下一个简单的文本文件是怎么保存的:

打开vim,编辑一段文本:

[root@localhost ~]# vim hello.txt

编辑内容如下:

opencfg.com is best website for java 

查看其属性:

[root@localhost ~]# ls -l
-rw-r--r--  1 root root    37  9月  4 19:03 hello.txt

据这个例子的目的,是为了说明Linux系统中文件是由3个部分组成:

 

1.数据-data(编辑内容, opencfg.com is best websire for java)

2.元数据-metadata(当你用ls -l 命令 或者ll命令时,列出的信息就是元数据, 在Linux,Unix系统中,所有与文件相关的元数据都保存在一个被叫做inode的结构中)

3.文件名-directtory entry(也叫做目录项,保存文件名)

inode节点

    在Linux、Unix文件系统中的每个文件都有一个相关的inode节点,保存了除文件名、文件内容(data)以外的所有文件信息,其中包括:

1.文件类型

    在Linux、Unix文件系统内的任何东西,包括一般文件和目录、符号连接、设备节点、与进程间通信相关的 命名管道函数,套接字(socket)都是文件类型中的一种,下边列出了可能出现的文件类型:

Linux文件类型
文件类型    ls缩写    应用范围
常规文件    -   保存数据
目录  d   存放文件名
符号连接    l   指向其他文件
字符设备节点  c   访问设备
块设备节点   b   访问设备
命名管道函数  p   进程间通信
套接字 s   进程间通信

这7种文件类型,具有相同的inode节点结构,他们具有相同的属性:所有者的身份、权限、修改时间、当使用命令ls -l 或者命令ll列出文件时,稳健类in个由第一个字符标识,该字符所对应的是上表中的缩写标记.

file命令

 

除了使用ls -l 与 ll 两个命令外,还可以使用file命令来查看文件的类型:

 

这里再写一个hello.sh:

#!/bin/shecho "hello"

下边使用file分别查看hello.txt与hello.sh,看看输出结果:

[root@localhost ~]# file hello.txt
hello.txt: ASCII text
[root@localhost ~]# file hello.sh
hello.sh: POSIX shell script text executable

file命令可以深入文件内容,查看具体的文件类型,给出的描述比较清晰

 

2. 所有者的身份与权限

 

    每个常规文件、目录都所有者、组 和 三组访问权限: 读取、写入、执行,当使用ls -l 或者ll命令列出文件时,第一列显示权限(其中第一个字符表示文件类型缩写), 第三列显示用户所有者,第四列显示组。

[root@localhost ~]# ls -l
-rw-r--r--  1 root root    37  9月  4 19:03 hello.txt
第一列是-rw-r--r--, 其中第一个字符是"-",在缩写表中对应常规文件以后的权限是rw-r--r--,表示权限第二列是1              表示连接数,如果有硬连接到这个文件,这里的数值会+1,删除硬连接这里会-1第三列是root         表示文件所有者是root第四列是root         表示文件所有组是root第五列是37           表示文件占用37字节第六列是9月 4 19:03  默认表示文件最后的ctime, change-time第七列是hello.txt   表示文件名

3. 更多的inode信息

 

我们可以使用stat命令来查看文件更多的元数据信息:

[root@localhost ~]# stat hello.sh File: "hello.sh"Size: 24              Blocks: 8          IO Block: 4096   普通文件
Device: fd00h/64768d    Inode: 149215      Links: 1
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2011-09-04 20:41:02.153122391 +0800
Modify: 2011-09-04 20:40:59.160742070 +0800
Change: 2011-09-04 20:40:59.178744361 +0800

stat命令列出了更多的元数据信息:

size   表示文件的理论长度,单位是字节
Block 与 IO Block 的乘积是文件所占的实际大小,在linux下文件所占的空间分配,最小的单位是块(Bolck),而块的大小与块的数量,决定了文件实际占用的磁盘空间.Device 表示内核对该设备的编号
Inode  是内核为每一个文件分配的标志
Links   表示文件名指向的inode节点的数量
Access:(0755/-rwxr-xr-x) 表示了访问权限,以及文件类型
uid:     表示了文件所有者,包括了系统为所有者分配的数值id
gid:     表示了文件组,包括了系统为组分配的数值id接下来有三个时间通常被叫做文件的atime, ctime, mtime:文件时间信息
缩写  全称  名称  描述
atime   access time 访问时间    文件数据每次被阅读后所记录的时间
ctime   change time 改变时间    文件的inode节点信息被改变后记录的时间
mtime   modify time 修改时间    文件内容数据被修改后记录的时间

二.链接

Linux 文件系统最重要的特点之一是它的文件链接。链接是对文件的引用,这样您可以让文件在文件系统中多处被看到。不过,在 Linux 中,链接可以如同原始文件一样来对待。链接可以与普通的文件一样被执行、编辑和访问。对系统中的其他应用程序而言,链接就是它所对应的原始文件。当您通过链接对文件进行编辑时,您编辑的实际上是原始文件。链接不是副本。有两种类型的链接:硬链接和符号链接(软链接)。
 
硬链接只能引用同一文件系统中的文件。它引用的是文件在文件系统中的物理索引(也称为 inode)。当您移动或删除原始文件时,硬链接不会被破坏,因为它所引用的是文件的物理数据而不是文件在文件结构中的位置。硬链接的文件不需要用户有访问原始文件的权限,也不会显示原始文件的位置,这样有助于文件的安全。如果您删除的文件有相应的硬链接,那么这个文件依然会保留,直到所有对它的引用都被删除。
 
符号链接(软链接)是一个指针,指向文件在文件系统中的位置。符号链接可以跨文件系统,甚至可以指向远程文件系统中的文件。符号链接只是指明了原始文件的位置,用户需要对原始文件的位置有访问权限才可以使用链接。如果原始文件被删除,所有指向它的符号链接也就都被破坏了。它们会指向文件系统中并不存在的一个位置。
两种链接都可以通过命令 ln 来创建。ln 默认创建的是硬链接。使用 -s 开关可以创建符号链接。

三.带外通信--ioctl()

 什么是ioctl 
    ioctl是设备驱动程序中对设备的I/O通道进行管理的函数 。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。它的调用个数如下: 
int ioctl(int fd, ind cmd, …); 
    其中fd是用户程序打开设备时使用open函数返回的文件标示符,cmd是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,这个参数的有无和cmd的意义相关。 
    ioctl函数是文件结构中的一个属性分量,就是说如果你的驱动程序提供了对ioctl的支持,用户就可以在用户程序中使用ioctl函数来控制设备的I/O通道。

 ioctl的必要性 
    如果不用ioctl的话,也可以实现对设备I/O通道的控制,但那是蛮拧了。例如,我们可以在驱动程序中实现write的时候检查一下是否有特殊约定的数 据流通过,如果有的话,那么后面就跟着控制命令(一般在socket编程中常常这样做)。但是如果这样做的话,会导致代码分工不明,程序结构混乱,程序员 自己也会头昏眼花的。所以,我们就使用ioctl来实现控制的功能。要记住,用户程序所作的只是通过命令码(cmd) 告诉驱动程序它想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情。

下面给出几个实用实例

/*如下是获取网络设备的信息的程序*/
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
unsigned char g_eth_name[16];
unsigned char g_macaddr[6];
unsigned char g_subnetmask;
unsigned char g_ipaddr;
unsigned char g_broadcast_ipaddr;   void init_net(void)
{   int i;   int sock;   struct sockaddr_in sin;   struct ifreq ifr;   sock = socket(AF_INET,SOCK_DGRAM,0);   if (sock == -1)   {   perror("socket");   }   strcpy(g_eth_name,"eth0");   strcpy(ifr.ifr_name,g_eth_name);   printf("eth name:\t%s\n",g_eth_name);   if (ioctl(sock,SIOCGIFHWADDR,&ifr) < 0)   {   perror("ioctl");   }   memcpy(g_macaddr,ifr.ifr_hwaddr.sa_data,6);   printf("local mac:\t");   for (i=0;i<5;i++)   {   printf("%.2x:",g_macaddr[i]);   }   printf("%.2x:\n",g_macaddr[i]);   //获取并打印IP地址   if (ioctl(sock,SIOCGIFADDR,&ifr) < 0)   {   perror("ioctl");   }   memcpy(&sin,&ifr.ifr_addr,sizeof(sin));   g_ipaddr = sin.sin_addr.s_addr;   printf("local eth0:\t%s\n",inet_ntoa(sin.sin_addr));   //获取并打印广播地址   if (ioctl(sock,SIOCGIFBRDADDR,&ifr) < 0)   {   perror("ioctl");   }   memcpy(&sin,&ifr.ifr_addr,sizeof(sin));   g_broadcast_ipaddr = sin.sin_addr.s_addr;   printf("broadcast:\t%s\n",inet_ntoa(sin.sin_addr));   //获取并打印子网掩码   if (ioctl(sock,SIOCGIFNETMASK,&ifr) < 0)   {   perror("ioctl");   }   memcpy(&sin,&ifr.ifr_addr,sizeof(sin));   g_subnetmask = sin.sin_addr.s_addr;   printf("subnetmask:\t%s\n",inet_ntoa(sin.sin_addr));   close(sock);
}   int main()
{   init_net();   return 0;
}  

程序说明:程序先创建一个用于网络通信的套接字,然后利用ioctl对其操作,获取网络信息。程序中的函数net_ntoa用来将网络地址转换成字符串形式。

//调节音量
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include <stdio.h>
#include <unistd.h>
#include <math.h>
#include <string.h>
#include <stdlib.h> #define  BASE_VALUE 257 int main(int argc,char *argv[])
{ int mixer_fd=0; char *names[SOUND_MIXER_NRDEVICES]=SOUND_DEVICE_LABELS; int value,i; //检查参数 if (argc<3) { printf("\nusage:%s dev_no.[0..24] value[0..100]\n\n",argv[0]); printf("eg. %s 0 100\n",argv[0]); printf("    will change the volume to MAX volume.\n\n"); printf("The dev_no. are as below:\n"); for (i=0; i<SOUND_MIXER_NRDEVICES; i++){ if (i%3==0) printf("\n"); printf("%s:%d\t\t",names[i], i); } printf("\n\n"); exit(1); } //打开文件 if ((mixer_fd = open("/dev/mixer",O_RDWR))){ printf("Mixer opened successfully,working...\n"); value = BASE_VALUE*atoi(argv[2]); //修改文件 if (ioctl(mixer_fd,  MIXER_WRITE(atoi(argv[1])), &value)==0) printf("successfully....."); else printf("unsuccessfully....."); printf("done.\n"); }else printf("can't open /dev/mixer error....\n"); exit(0);
} 

下面这个程序用于打开光驱

/*下面的程序会使用CDROMEJECT(由用户提供的参数)请求弹出CD-ROM设备的媒体托盘*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <linux/cdrom.h>
#include <stdio.h>int main(int argc, char *argv[])
{
int fd,ret;
if(argc < 2){
fprintf(stderr,"usage :%s <device to eject>\n", argv[0]);
return 1;
}
/*
*以只读方式打开CD-ROM设备。O_NONBLOCK用于通知内核,
*即使设备中没有媒体,我们也要打开该设备
*/
fd = open(argv[1], O_RDONLY | O_NONBLOCK);
if(fd < 0){
perror("open");
return 1;
}
/*给CD-ROM设备送出弹出命令*/
ret = ioctl(fd, CDROMEJECT, 0);
if(ret){
perror("ioctl");
return 1;
}
ret = close(fd);
if(ret){
perror("close");
return 1;
}
return 0;
}

四.监视文件事件

Inotify 是一个 Linux 内核特性,它监控文件系统,并且及时向专门的应用程序发出相关的事件警告,比如删除、读、写和卸载操作等。您还可以跟踪活动的源头和目标等细节。在实际项目中,如果项目带有配置文件,那么怎么让配置文件的改变和项目程序同步而不需要重启程序呢?一个明显的应用是:在一个程序中,使用Inotify监视它的配置文件,如果该配置文件发生了更改(更新,修改)时,Inotify会产生修改的事件给程序,应用程序就可以实现重新加载配置文件,检测哪些参数发生了变化,并在应用程序内存的一些变量做相应的修改。当然另一种方法可以是通过cgi注册命令,并通过命令更新内存数据及更新配置文件

1. Inotify 不需要对被监视的目标打开文件描述符,而且如果被监视目标在可移动介质上,那么在 umount 该介质上的文件系统后,被监视目标对应的 watch 将被自动删除,并且会产生一个 umount 事件。

2. Inotify 既可以监视文件,也可以监视目录。

3. Inotify 使用系统调用而非 SIGIO 来通知文件系统事件。

4. Inotify 使用文件描述符作为接口,因而可以使用通常的文件 I/O 操作select 和 poll 来监视文件系统的变化。

Inotify 可以监视的文件系统事件包括:

IN_ACCESS,即文件被访问
IN_MODIFY,文件被 write
IN_ATTRIB,文件属性被修改,如 chmod、chown、touch 等
IN_CLOSE_WRITE,可写文件被 close
IN_CLOSE_NOWRITE,不可写文件被 close
IN_OPEN,文件被 open
IN_MOVED_FROM,文件被移走,如 mv
IN_MOVED_TO,文件被移来,如 mv、cp
IN_CREATE,创建新文件
IN_DELETE,文件被删除,如 rm
IN_DELETE_SELF,自删除,即一个可执行文件在执行时删除自己
IN_MOVE_SELF,自移动,即一个可执行文件在执行时移动自己
IN_UNMOUNT,宿主文件系统被 umount
IN_CLOSE,文件被关闭,等同于(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
IN_MOVE,文件被移动,等同于(IN_MOVED_FROM | IN_MOVED_TO)
注:上面所说的文件也包括目录。

用户接口

在用户态,inotify 通过三个系统调用和在返回的文件描述符上的文件 I/ 操作来使用,使用 inotify 的第一步是创建 inotify 实例:

int fd = inotify_init ();

每一个 inotify 实例对应一个独立的排序的队列。

文件系统的变化事件被称做 watches 的一个对象管理,每一个 watch 是一个二元组(目标,事件掩码),目标可以是文件或目录,事件掩码表示应用希望关注的 inotify 事件,每一个位对应一个 inotify 事件。Watch 对象通过 watch描述符引用,watches 通过文件或目录的路径名来添加。目录 watches 将返回在该目录下的所有文件上面发生的事件。

下面函数用于添加一个 watch:

int wd = inotify_add_watch (fd, path, mask);

fd 是 inotify_init() 返回的文件描述符,path 是被监视的目标的路径名(即文件名或目录名),mask 是事件掩码, 在头文件 linux/inotify.h 中定义了每一位代表的事件。可以使用同样的方式来修改事件掩码,即改变希望被通知的inotify 事件。Wd 是 watch 描述符。

下面的函数用于删除一个 watch:

int ret = inotify_rm_watch (fd, wd);

fd 是 inotify_init() 返回的文件描述符,wd 是 inotify_add_watch() 返回的 watch 描述符。Ret 是函数的返回值。

文件事件用一个 inotify_event 结构表示,它通过由 inotify_init() 返回的文件描述符使用通常文件读取函数 read 来获得

struct inotify_event {__s32           wd;             /* watch descriptor */__u32           mask;           /* watch mask */__u32           cookie;         /* cookie to synchronize two events */__u32           len;            /* length (including nulls) of name */char            name[0];        /* stub for possible name */
};

结构中的 wd 为被监视目标的 watch 描述符,mask 为事件掩码,len 为 name字符串的长度,name 为被监视目标的路径名,该结构的 name 字段为一个桩,它只是为了用户方面引用文件名,文件名是变长的,它实际紧跟在该结构的后面,文件名将被 0 填充以使下一个事件结构能够 4 字节对齐。注意,len 也把填充字节数统计在内。

通过 read 调用可以一次获得多个事件,只要提供的 buf 足够大。

 size_t len = read (fd, buf, BUF_LEN);

buf 是一个 inotify_event 结构的数组指针,BUF_LEN 指定要读取的总长度,buf 大小至少要不小于 BUF_LEN,该调用返回的事件数取决于 BUF_LEN 以及事件中文件名的长度。Len 为实际读去的字节数,即获得的事件的总长度。

可以在函数 inotify_init() 返回的文件描述符 fd 上使用 select() 或poll(), 也可以在 fd 上使用 ioctl 命令 FIONREAD 来得到当前队列的长度。close(fd)将删除所有添加到 fd 中的 watch 并做必要的清理。

int inotify_init (void);int inotify_add_watch (int fd, const char *path, __u32 mask);int inotify_rm_watch (int fd, __u32 mask);

取得事件队列的大小

未决事件队列的大小可有inotify实例的文件描述符上的FIONREAD ioctl 请求来取得。此请求的参数用于接收队列的大小(以字节为单位),这是一个无符号整数。

integer:
unsigned int queue_len;
int ret;
ret = ioctl(fd, FIONREAD, &queue_len);
if(ret < 0)
perror("ioctl");
else
printf("%u bytes pending in queue\n",queue_len);

请注意,此请求所返回的是队列的大小(以字节为单位),而不是队列中事件的数目。一个程序可以根据inotify_event 结构已知的大小(由sizeof()取得),从字节数目估算出时间数目,并且猜测出name字段的平均大小。然而,比较有用的是字节数目,因为这可让进程进行大小适当的读取操作。


销毁一个 inotify 实例

销毁一个inotify 实例以及任何相关联的监视项目就如关闭该实例文件描述符一样简单:

int ret;
/*fd 经inotify_init() 取得*/
ret = close(fd);
if(fd == -1)
perror("close");

当然,如同任何的文件描述符一样,内核会自动关闭文件描述符,并且在进程结束时清理资源。

下面给出一个实例:

#include <stdio.h>//printf
#include <string.h> //strcmp
#include <sys/inotify.h>//inotify_init inotify_add_watch....
#include <sys/select.h>//select timeval
#include <unistd.h>//close#define EVENT_SIZE  ( sizeof (struct inotify_event) )
#define BUF_LEN     ( 1024 * ( EVENT_SIZE + 16 ) )
#define ERR_EXIT(msg,flag)  {perror(msg);goto flag;}int main( int argc, char **argv )
{int length, i = 0;int fd;int wd;char buffer[BUF_LEN];if((fd = inotify_init()) < 0)ERR_EXIT("inotify_init",inotify_init_err);if( (wd = inotify_add_watch( fd, "/tmp", IN_MODIFY | IN_CREATE | IN_DELETE ) ) < 0)ERR_EXIT("inofity_add_watch", inotify_add_watch_err);fd_set rfd;struct timeval tv;tv.tv_sec = 0;tv.tv_usec = 10000;//10millsecondwhile(true){int retval;FD_ZERO(&rfd);FD_SET(fd, &rfd);retval = select(fd + 1, &rfd, NULL, NULL, &tv);if(retval == 0) continue;else if(retval == -1)ERR_EXIT("select",select_err);// retval > 0length = read( fd, buffer, BUF_LEN );  if(length < 0)ERR_EXIT("read",read_err);//length >= 0int i = 0;while ( i < length ) {struct inotify_event *event = ( struct inotify_event * ) &buffer[ i ];if ( event->len ) {if ( event->mask & IN_CREATE ) {if ( event->mask & IN_ISDIR ) printf( "The directory %s was created.\n", event->name );       elseprintf( "The file %s was created.\n", event->name );if(strcmp(event->name,"kill") == 0)ERR_EXIT("success exit",success_exit);}else if ( event->mask & IN_DELETE ) {if ( event->mask & IN_ISDIR ) printf( "The directory %s was deleted.\n", event->name );       elseprintf( "The file %s was deleted.\n", event->name );}else if ( event->mask & IN_MODIFY ) {if ( event->mask & IN_ISDIR )printf( "The directory %s was modified.\n", event->name );elseprintf( "The file %s was modified.\n", event->name );}}else{//TODO//when only a file(not directory) is specified by add watch function, event->len's value may be zero, we can handle it here}i += EVENT_SIZE + event->len;}}
success_exit:( void ) inotify_rm_watch( fd, wd );( void ) close( fd );return 0;read_err:
select_err:
inotify_add_watch_err:( void ) inotify_rm_watch( fd, wd );
inotify_init_err:( void ) close( fd );return -1;
}

以上代码需要注意的地方:

1.如果在/tmp目录下touch kill文件,程序则会退出

2.如果只有一个add watch 一个file,那么这个file的更改产生的event事件中event->len是为0,需要额外的处理,此代码省略了具体的处理过程,以注释代替

3.如果监测的是文件或目录的更改,使用 echo "xxx" >> file,会产生一个event事件,而使用echo "xxx" > file 会产生两个event事件,查了相关的资料,可能是因为后者需要先清空file文件内容,造成第一次event事件,再将xxx写入file保存,造成了第二次的event事件。

Linux System Programming --Chapter Seven相关推荐

  1. Linux System Programming --Chapter Nine

    这一章的标题是 "信号" ,所以本文将对信号的各个方面进行介绍,由于Linux中的信号机制远比想象的要复杂,所以,本文不会讲的很全面... 信号机制是进程之间相互传递消息的一种方法 ...

  2. Linux System Programming --Chapter Two

    首先,需要解释的东西是Linux系统调用 一. 什么是系统调用      在Linux的世界里,我们经常会遇到系统调用这一术语,所谓系统调用,就是内核提供的.功能十分强大的一系列的函数.这些系统调用是 ...

  3. Linux System Programming --Chapter Eight

    内存管理 一.分配动态内存的几个函数 用户空间内存分配:malloc.calloc.realloc 1.malloc原型如下: extern void *malloc(unsigned int num ...

  4. Linux System Programming --Chapter Six

    这一章的题目是--高级进程管理,这篇文章将以书中所叙的顺序进行讲解 1.让出处理器 Linux提供一个系统调用运行进程主动让出执行权:sched_yield.进程运行的好好的,为什么需要这个函数呢?有 ...

  5. Linux System Programming --Chapter Five

    这一章中的内容出现在博主的多篇文章中,所以并不对这一章进行详细的说明解释,只是对几个比较重要的概念进行说明 一.写时复制技术 COW技术初窥:       在Linux程序中,fork()会产生一个和 ...

  6. Linux System Programming --Chapter Four

    这一章介绍的主题是--高级文件 I/O 一. 分散--聚集I/O 分散聚集I/O是一种进行输入和输出的方法.通过此方法,单一系统调用可以将缓冲区向量写入单一数据流,或者将单一数据流读取到缓冲区向量.这 ...

  7. Linux System Programming --Chapter Three

    这一章的主题是--缓冲式I/O 一,流与缓冲        流I/O是由C语言的标准函数提供的,这些I/O可以替代系统中提供的read和write函数.事实上流I/O的内 部封装了这两个基本的文件读写 ...

  8. 书评:Linux System Programming

    原文地址:http://books.slashdot.org/article.pl?no_d2=1&sid=08/04/14/1415215 作者:Jon Mitchell "本文略 ...

  9. Linux System Programming -- Appendix

    这本书附录的名字是 "GCC对C语言的扩展" ,一下的内容是对扩展的总结 类型发现 GCC 允许通过变量的引用识别类型.这种操作支持泛型编程.在 C++.Ada 和 Java™ 语 ...

最新文章

  1. php mysql设置null,MySQL和PHP – 插入NULL而不是空string
  2. python运行完不能显示图_Python Pygame无法正确显示图像
  3. python 列表,元祖,字典的区别
  4. 解决安装kali 2020.1版本后的中文乱码问题:只需要安装中文字体(而不需要像之前版本那样需要选择locales和编码)。
  5. 《JavaScript 高级程序设计》 7.5 常用模式
  6. ios上架图片在线制作_不同风格gif在线制作,公众号动态图片制作方法
  7. 作者:王倩(1983-),女,上海计算机软件技术开发中心工程师。
  8. 发现个Asp.net英文Blog,嘿嘿,刚好对俺学e文有用:)
  9. 错误:Mixed Content: The page at ‘https://XXX’ was loaded over HTTPS, but requested an in...
  10. win7局域网共享设置_局域网硬盘一键共享软件下载-一键共享局域网修复查看设置工具包免费版...
  11. 某公司电子商务网站策划方案
  12. 服务器突然断电文件系统损坏,电脑突然断电造成系统文件损坏该如何修复?(高级篇)...
  13. Android程序员的十大转型之路
  14. Amazon S3文件存储的上传下载如何测试
  15. 杰瑞·卡普兰:人工智能的本质是自动化 而非智能化
  16. windows 音频编程
  17. [附源码]计算机毕业设计JAVA网上鞋店管理系统
  18. 组合数学拉丁方是什么
  19. 推荐丨办公软件WPS新版本--2019
  20. 利用易用宝——设置EXCEL 文本中,姓名拼音字母为姓全大写,名的首字母大写?

热门文章

  1. framework层和native层实现联网控制(iptable方式)
  2. (NO.00003)iOS游戏简单的机器人投射游戏成形记(十二)
  3. ActiveReports 报表应用教程 (2)---清单类报表
  4. Agile in a Flash:万物皆渐进(14)
  5. 国内卫星通信技术发展及应用概述
  6. 【重学Vue】数据响应原理真的是双向绑定吗?
  7. CCF201312-5 I’m stuck!(100分)
  8. solr索引服务器的配置和solrj集成开发总结
  9. 第三章 Git使用入门 心得笔记
  10. android特效按钮点击效果