今天到了这一步 之前很多次想写一个网络爬虫,但是很多时候都无疾而终,这次,简单的记录一下。 perl模块的安装可以有很多的方法,现在我只熟悉两种方法。
这一步是怎么安装的我忘了,下次可以再次补充上。
??
cpan module-name
最简单的杀器是 :
man cpan
但是这里有一点注意的是在vps上使用cpan会有限制的,特别是你的memory的值小于500 MB的时候,那么,此时应该怎么办?应该使用下面的方法:cpanm
从字面上理解,这个就是cpan module的简写,其实这里只是一个巧合。下面简单的记录 一下它的用法。
curl -L -K http://cpanmin.us | perl - --self-upgrade
/usr/local/bin/cpanm local::lib
perl -I /usr/bin/perl -Mlocal::lib >> ~/.bashrc
解释几点:
which cpanm
即可找到cpanm的可执行路径,修改成对应的即可
3 最后一条语句的含义是将相关的库文件的可执行路径放到.bashrc中去。这两种方法安装模块的方法是大同小异,可以man一下,提醒一下,这里可以安装的文 件:
cpan上的模块;
网上的压缩文件;
本地文件;
URL;
随着时间的发展,看的书越来越多,需要进行重新归类了
2016-07-15 将apue的标题改为unix 文件 io,新增文件属性的内容。
这一章简单记录有关文件io的操作,大概提一点,这块的函数返回值不成功的话返回值 为-1。
相关的练习题放在github
#include <fcntl.h>
int open(const char *path,int oflag,...);
int openat(int fd,const char *path,int oflag...);
错误的话就会返回”-1” 尤其注意几个标志,它控制了你要打开的文件的方式。举几个例子,
O_RDONLY : open for reading only;
O_WRONLY : open for writing only;
O_RDWR : OPEN FOR READING AND writing
O_EXEC :
O_SEARCH :
这些标志放在了
#nclude <fcntl.h>
int create(const char *path, mode_t mode);
这儿有一篇厉害的c的程序,不知道在讲些什么,但是还使用了od命令: 上面代码库中的creat.c,真的不知道在讲些什么!!
#include <unistd.h>
int close(int fd);
/*
* error return -1
*/
这个函数的作用是关闭已打开文件描述符。当关闭一个文件时,这个文件会自动释放在 它身上的锁;当一个进程终止时,内核会自动释放相关的文件。
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd,off_t offset,int whence);
/* error on -1 */
其中参数列表中offset与第三个参数有关,若第三个参数为下列值:
SEEK_SET: 则文件的偏移量设置为从文件开始处offset的地方;
SEEK_CUR: 偏移量为现在的值加上offset,可正可负;
SEEK_END: 偏移量为整个文件的大小记上offset,可正可负;
函数的返回值为文件重新标定后的偏移量。所以,在有些机器上,返回值可能是负的 所以即使读取成功了有可能是-1,特别重要这一点·``
#include <unistd.h>
ssize_t read(int fd, void *buf,size_t bbytes);
返回值是读取的字节数,0读到文件尾,-1读出错了。
#include <unistd.h>
ssize_t write(int fd, const void *buf,size_t nbytes);
返回的是写入文件的字数,如果错误返回-1.
标准输入输出STDIN_FILEIN,有这个标志,就可以使用标准输入了。
简单地说,这类文件有三类:
process table entry 在进程表中有一个表项,里面有文件描述符(包括fd的标志和一个指向文件的指针)。这里的fd flag没有搞明白具体是啥含义。
file table entry: 这个表里的项是来自于上面的一个指向,在这个表中, 有三个域a.这个文件的状态:读、写、追加、同步…b.文件的偏移位置。c.一个指向 v-node的指针;
3.这v-node 是一个链表,里面有文件的类型、在这个文件上的函数指针,在linux上这 里是有i-node结构,里面包括文件的作者、文件的大小、文件指向实际块的指针;
对v(i)-node 的共享,不是对前1、2两种情况的分享;主要原因是每个进程要控制自己 在文件中的偏移量。
在这种情况下,有三种类型的操作需要我们分别:
write.
每次执行完成后,在file table entry中偏移量需要增加写入的个数;如果大于文件的大小,那么在i-node中设置现在文件的偏移量?
O_APPEND
如果打开的文件设置为O_APPEND,那么file table entry的偏移量会从 i-node的偏移量中选取后设置;
lseek
与O_APPEND原理相似,但不一样!!!
这个标志,仅仅修改文件指针的偏移位置,没有任何I/O操作。
有可能在file table entry中被不止一个文件描述符所指向,尤其是在执行完fork命令后,父进程和子进程就会共享一个file table entry。
1的flag为与2的status的区别在于前者仅仅局限于单一进程中,后者对所有的进程的开放。
#include <unistd.h>
ssize_t pread(int fd, void *buf, size_t nbytes, off_t offset);
ssize_t pwrite(ing fd, const void *buf, size_t nbytes, off_t offset);
两个函数成功后都会返回相应的字节数,0文件结尾, -1错误。
pread 是在lseek后立即执行read操作;pwrite是相同的结果。
在以下两种情况下,最好使用这个方法:
两个操作之间没有中断
目前的偏移没有更新
在函数create中,如果标志O_CREAT 和 O_EXCL也是表达的同样的意思。这里不是这个意思,好好理解去;
int dup(int fd);
int dup2(int fd, int fd2);
成功返回新的fd,失败-1.前者会分配最小的可利用的fd,后者将fd2的值作为返回的fd,如果fd2已经打开了,则先关闭它;如果参数1的fd与fd2相同,则返回fd2并保持打开状态。 如果熟悉fcntl函数的话,上面的函数分别等于以下代码段:
fcntl(fd, F_DUPFD,0);和
close(fd);
fcntl(fd, F_DUPFD,fd2);
但是需要注意的是,dup2是原子操作,而close()和fcntl函数之间可能存在异步的风险。还有就是errno有可能不同的。
这几个函数是为了控制延时写,将缓存的数据放回磁盘中。
#include<unistd.h>
int fsync(int fd);
int fdatasync(int fd);
void sync(void);
sync函数 以每隔30秒的周期从系统中调动,命令sync就是调用的这个函数。
fsync对于单个文件有效,且必须等待磁盘回写结束后才能返回。比如在数据库操作中 需要确认已修改的块已经被写回磁盘。
fdatasync仅对于文件中的数据部分,其他的没有影响。fsync对于文件属性也回写。
#include<fcntl.h>
int fcntl(int fd, int cmd, .../* int arg */);
这个函数主要有5个方面的作用:
复制一个存在的描述符(cmd = F_DUPFD 或者F_DUPFD_CLOEXEC)
得到/设置文件描述符标志(cmd = F_GETFD 或者F_SETFD)
得到/设置文件状态标志(cmd = F_GETFL或者F_SETFL)
得到/设置异步io属性(cmd = F_GETOWN或者F_SETOWN)
得到/设置记录锁(cmd = F_GETLK,F_SETLK或者F_SETLKW);
可以通过修改控制改变文件描述符的动作。通过3步来关闭磁盘缓冲。
获取位置
修改设置
存储设置
代码如下:
#include<fcntl.h>
int s; // settings
s = fcntl(fd, F_GETFL) //get flags
s |= O_SYNC; // set SYNC bit
result = fcntl(fd, F_SETFL, s); // set flags
if (result == -1) // if error
perror("setting SYNC"); // report
文件描述的属性被编码在一个整数的位中。
上面的代码含义就是fd所指定的文件上,参数F_GETFL得到当前的位集,变量s存放这个 flag集, 位逻辑或操作打开位O_SYNC.该位告诉内核,对write的调用仅能在数据写入实际的硬件时才能返回,而不是在数据复制到内核缓冲时就执行默认的返回操作。
设置O_SYNC会关闭内核的缓冲机制。
文件描述符的另一个属性是自动添加(auto-append mode), 自动添加模式对于若干进 程在同一时间写入文件是很有用的,这里,使用的效果就是避免竞态。
说的简单一点就是,auto-append mode 就是当文件描述符属性O_APPEND打开后,每个 对write的调用自动调用lseek函数将内容添加到文件的末尾。
#include <fcntl.h>
int s; // settings
s = fcntl(fd, F_GETFL); // get flags
s |= O_APPEND; // set APPEND bit
result = fcntl(fd, F_SETFL, s); //set flags
if (result == -1) //IF ERROR
perror("setting APPEND"); //report
else
write(fd, &rec, 1); // write
也就是说当O_APPEND被设置的时候,lseek和write被打包成一个原子操作
open和write在执行一个文件描述符时有很多的选项。详细的请看联机手册。
#include <termios.h>
#include <unistd.h>
int result = tcgetattr(int fd, struct termios *info);
测试位 if(flagset & MASK)
置位 flagset |= MASK
清除位 flagset &= ~MASK
sysfs是什么?—内核中,sysfs是一个动态生成的目录,断电后就玩完了。
sysfs - the filesystem for exporting kernel objects.
sysfs 是kobject的?继承。只要你在配置系统的时候定义了CONFIG_SYSFS,那么挂载 的方法是
mount -t sysfs sysfs /sys
一 用处
只要一个kobject注册到了系统中,就在/sys目录中创造了一个目录。在kobject结构体 体中kernfs_node与这个目录有关。
在sys_create_file(struct kobject * kobj, const struct attribute * attr),中, 那个attribute结构体需要自己写。
struct attribute {
char * name;
struct *owner;
umode_t mode;
};
这里还可以丰富attribute的定义,详细情况请了解内核代码Document/filesystem/sysfs.txt.
…
kobject是组成设备模型的基本结构,类似于c++的基类。尽管不懂,但还是有种似曾相 识的感觉。 它嵌入更大的容器中–所谓的容器就是用来描述设备模型的组件,如bus,devices,都是 容器,这些容器通过kobject连接起来,形成一个树形结构,这个树状结构与/sys对应
kobject结构为大的数据结构和子系统提供了基本的对象管理,避免了类似机能的重复 使用。这些机能包括:
-对象引用计数 -维护对象链表 -对象上锁 -在用户空间的表示
很少有代码会单独使用kobject,一般都是嵌入在其他结构中。把它看成一个抽象类用面 向对象的观点来看的话,这里也使用了继承的思想。内核中的link list也有这种思想 。
举个例子,drivers/uio/uio.c中:
struct uio_map{
struct kobject kobj;
struct uio_map *mem;
};
这样,你可以轻松如意的使用uio_map的kobject.但是,问题经常会反过来:给你一个 kobject指针,怎么找到宿主结构体的指针呢?千万别假设kobject位于一个结构体的哪 个位置。使用container_of()宏即可。
container_of(pointer, type, member);
tip: 首先读懂tape script,目的熟悉听力中的词组、句式。 收集最近两年的四六级的听力。
下面是听力核心词汇。
短对话中第二个人的答案重要
首先克隆下来,这个家伙还挺好用的。github,具体安装和 使用方法看一下Readme.
初次使用会有一个AttributeError:”AttributeError: ‘SqliteDatabase’ object has no attribute ‘drop_table’
Reference to : https://github.com/longcw/youdao/issues/11
This is one of my pet peeves: 这是我最讨厌的事情之一。
名词:系数矩阵,增广矩阵 二者的关系判断n元线性方程组有无解的情况
n元线性方程组->n维向量空间->线性空间->线性映射<-> 矩阵 然后n元线性方程组和矩阵的关系应该明白吧。
几何空间 线性空间没有度量,如何引入呢? 两个向量对应一个实数的桥梁是内积。 具有度量的线性空间:欧几里得空间(实数上)酉空间(复数上) 空间到自身的映射为线性变换。(投影)。 在欧几里得空间上与度量有关的变换有正交变换、对称空间;酉空间上有Hermite变换,这些都是与度量有关的变换。
一元多项式环的结构(配方得来的)->环的概论。
卡洛娃?证明了4次以上的一元方程没有求根公式。它引入了群的概念,近世代数研究群、环的结构。还有一个域。
«高等代数–大学创新xx»丘维声,清华大学出版社
n阶行列式:= |A|,也称为矩阵A的行列式,或者dotA。 n个元素的乘积,每一项按行指标成自然序排列,当列指标的逆排列为奇数为负数。
先讲一下,在行列式的定义中,为了解决每一项的正负号,我们把n个元素按行指标排 列起来,事实上,数的乘法是交换的,所以这n个元素的排序可以是任意的,则n个数排 列的符号为(-1)的套(i1i2i3…in)+套(j1j2..jn)次方.那么,将行指标排成自然 序列,则它的符号为(-1)的套(列1列2..)次方。
举个例子说明下,a21a32a14a43是4阶行列式的一项,套(2314)=2,套(1243)=1, 则(-1) ^(2+1) = -1;如按行指标排列起来,就是a14a21a32a43,套(4123)=3,符号为-1;
行指标与列指标的地位是对称的,可以把某一项按列指标排起来。
证明:综合上方,如果B是由A互换得来的,则A可以按照i行j列,B为j行i列,结合上面的推理。
证明:按照某一行展开,
dotA = ai1Ai1 + ai2Ai2 +... = ∑aijAij [1]
其中西格玛是j从1到n。如dotA的一行如有公因式c,以[1]的右端展开不就是每一项有c 吗,所以dotA*c,得证。
证明: 若这一行是第i行,形式为(bi+ci),则有dotA=(b1+c1)A11+(b2+c2)A22+…+ (bn+cn)Ain=(b1A11+b2A22+…+bnAin)+(c1A11+c2A22+…+cnAin)
二叉树是一种非常重要的数据结构,今天我们来讲讲它。
滚粗。。。老子不是遇到很好的知识点,我才懒得理你呢。。。
以下性质暗含root node 为第一层
证明:数学归纳法。
i=1时,显然2^(1-1) = 2^0 = 1个节点是对的
现在假设对所有的j,1<=j<i,第j层上至多有2^(j-1)个节点,那么可以证明j=i时命题 也成立。
由归纳假设,第i-1层上至多有2^(i-2)个节点,由于二叉树的每个度最多为2,故第i层 上节点数为第i-1层上的2倍,即2*2^(i-2).
证明: 根据上面的1,每层最多节点数之和是一个等比数列,故证之。
证明:设度为1的节点为n1,则二叉树T中节点总数n等于n=n1+n2+n0. 如二叉树T分枝数为B,因为二叉树中除了根节点没有入度,其余节点都有一个入度,所 以n=B+1.又因为这些分支由度为1或者2的节点射出的,则B=n1+2n2,所以有 1+n1+2n2=n1+n2+n0 ==> n0=n2+1
如果使用数组代表二叉树的话,那么数组里的元素依次就是从根节点水平方向读取来的 (也就是依照层进行读取的)
如果根节点在数组的下标是从1开始,那么就有如下性质:
PARENT(i) return floor(i/2)
LEFT(i) return 2i
RIGHT(i) return 2i + 1
如果根节点从数组下标为0开始,那么有如下性质:
PARENT(i) return floor[(i-1)/2]
LEFT(i) return 2*i+1
RIGHT(i) return 2i + 2
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
};
struct TreeNode *newNode(int data) {
struct TreeNode *p = (struct TreeNode *)malloc(sizeof(struct TreeNode));
p->val = data;
p->left = p->right = NULL;
}
struct TreeNode *createBinaryTree(struct TreeNode *root,int *array, int pos, int nums) {
if ( pos < nums) {
struct TreeNode *p = newNode(array[pos]);
root = p;
// left
root->left = createBinaryTree(root->left, array, 2*pos + 1, nums);
root->right = createBinaryTree(root->right, array, 2*pos +2, nums);
}
return root;
}
void show(struct TreeNode *p) {
if(p == NULL)
return ;
else {
show(p->left);
if(p->val == -1)
printf("#\t");
else
printf("%d\t", p->val);
show(p->right);
}
}
int main() {
//int a[] = {1,4,5,1,9,6,5};
int a [] = {1,2,3,4,-1,-1,7,-1,9,-1,-1,-1,-1,4};
int n = sizeof(a)/sizeof(a[0]);
struct TreeNode *root = createBinaryTree(root,a, 0,n);
show(root);
printf("\n");
}
上面的代码就是按层次遍历依次读进二叉树,-1
代表空节点。打印的时候是先打印左孩子,再是根节点,最后是右节点。
下面可以看做是有关树的API操作。
typedef struct Node{
struct Node* left;
struct Node* right;
int data;
}Node;
Node* newNode(int data)
{
Node* node=(Node*)malloc(sizeof(Node));
node->left=node->right=NULL;
node->data=data;
return node;
}
/* 将root == NULL的返回值置为-1或者0,结果不一样*/
int getHeight(Node* root) {
if(root == NULL)
return -1;
/* 返回为0, 结果是从根节点到叶子节点的节点数,
* 返回为-1, 是经历的路径长度 */
int left_height = getHeight(root->left);
int right_height = getHeight(root->right);
return (left_height > right_height ? left_height : right_height ) + 1 ;
}
/* The number of node*/
int node_num_tree(Node* root)
{
if(root == NULL)
return 0;
/* 这里的返回 0 还是有区别的 */
return 1 + node_num_tree(root->left) + node_num_tree(root->right);
}
/* the numbers of leaves */
int leaf_num_tree(Node* root)
{
if(root == NULL)
return 0;
if(root->left == NULL && root->right == NULL)
return 1;
return leaf_num_tree(root->left) + leaf_num_tree(root->right);
}