2018.09.10-拾遗

Hexo真的太难用啦,今天终于把所有文章搬运了过来,也算可以正常写blog了。

好久没有拾遗过了。中间发生了很多事情,一直没下决心来拾遗。今天来拾遗一下~

Redis

与Memcached仅支持简单的key-value结构的数据记录不同,Redis支持的数据类型要丰富得多,常用的数据类型主要有五种:String、List、Hash、Set和Sorted Set。

Redis数据类型内存结构分析

Redis内部使用一个redisObject对象来表示所有的key和value。redisObject主要的信息包括数据类型(type)、编码方式(encoding)、数据指针(ptr)、虚拟内存(vm)等。type代表一个value对象具体是何种数据类型,encoding是不同数据类型在redis内部式。

 

 

redisObject 对象示意图

 

下面分别介绍5种数据类型的用法。

String类型

字符串是Redis值的最基础的类型。Redis中使用的字符串是通过包装的,基于c语言字符数组实现的简单动态字符串(simple dynamic string, SDS)一个抽象数据结构。其源码定义如下:

 

struct sdshdr {

    int len; //len表示buf中存储的字符串的长度。

    int free; //free表示buf中空闲空间的长度。

    char buf[]; //buf用于存储字符串内容。

};

 

 

 

C语言字符串内存结构示意图1

 

假设上图是”hello”字符串的内存结构,这个时候len=5,free=2那么redis包装后(sds)其长度为:

 

sizeof(struct sdshdr) + len + free + 1

 

其中buf的大小为:

 

len + free + 1

 

1表示1个字节是用来存储结束符’\0’的。Redis字符串是二进制安全的,因为二进制数据通常会有中间某个字节存储’\0’的这种情况,这意味着一个Redis字符串可以包含任何种类的数据,例如一个JPEG图像或者一个序列化的Ruby对象。二进制是否安全,简单的理解就是能不能在字符串中间有‘\0’,如下图:

 

 

C语言字符串内存结构示意图2

 

对于上图,sds认为这个字符串是“hello world”,而C语言的字符处理函数认为这个字符串是“hello”。

 

应用场景

String是最常用的一种数据类型,普通的key/value存储都可以归为此类。

常用命令

(1)set——设置key对应的值为String类型的value

(2)get——获取key对应的值

 

192.168.2.129:6379> setnx name lisi

(integer) 0

192.168.2.129:6379> setnx name1 wangwu

(integer) 1

192.168.2.129:6379> get name

"zhangsan"

192.168.2.129:6379> get name1

"wangwu"

192.168.2.129:6379>

 

(3)mget——批量获取多个key的值,如果可以不存在则返回nil

 

192.168.2.129:6379> mget name name1

1) "zhangsan"

2) "wangwu"

192.168.2.129:6379> mget name name1 name2

1) "zhangsan"

2) "wangwu"

3) (nil)

192.168.2.129:6379>

 

(4)incr && incrby——incr对key对应的值进行加加操作,并返回新的值;incrby加指定值

 

192.168.2.129:6379> get age

"20"

192.168.2.129:6379> incr age

(integer) 21

192.168.2.129:6379> set age1 "20"

OK

192.168.2.129:6379> get age1

"20"

192.168.2.129:6379> incr age1

(integer) 21

192.168.2.129:6379> incrby age 3

(integer) 24

 

从上面的结果可以看出,我们对int型的age和string型的age1都能进行incr操作时,

实际上type=string代表value存储的是一个普通字符串,那么对应的encoding可以是raw或者是int,如果是int则代表实际redis内部是按数值型类存储和表示这个字符串的,当然前提是这个字符串本身可以用数值表示,比如"20"这样的字符串,当遇到incr、decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int。如果你试图对name进行incr操作则报错。

 

192.168.2.129:6379> incr name

(error) ERR value is not an integer or out of range

 

(5)decr && decrby——decr对key对应的值进行减减操作,并返回新的值;decrby减指定值

 

192.168.2.129:6379> decr age

(integer) 23

192.168.2.129:6379> decrby age 3

(integer) 20

192.168.2.129:6379>

 

(6)其他命令

 

命令

说明

setnx

设置key对应的值为String类型的value,如果key已经存在则返回0

setex

设置key对应的值为String类型的value,并设定有效期

setrange

设置key对应value的子字符串

getrange

获取key对应value的子字符串

mset

批量设置多个key的值,如果成功表示所有值都被设置,否则返回0表示没有任何值被设置

msetnx

同mset,不存在就设置,不会覆盖已有的key

getset

设置key的值,并返回key旧的值

append

给指定key的value追加字符串,并返回新字符串的长度

strlen

取指定key的value的长度

Hash类型

Hash是一个String类型的field和value之间的映射表,即redis的Hash数据类型的key(hash表名称)对应的value实际的内部存储结构为一个HashMap,因此Hash特别适合存储对象。相对于把一个对象的每个属性存储为String类型,将整个对象存储在Hash类型中会占用更少内存。

 

 

 

Hash 数据类型内部结构示意图

 

当前HashMap的实现有两种方式:当HashMap的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,这时对应的value的redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht。

应用场景

用一个对象来存储用户信息,商品信息,订单信息等等。

常用命令

(1)hset——设置key对应的HashMap中的field的value

(2)hget——获取key对应的HashMap中的field的value

 

192.168.2.129:6379> hset myhash name zhangsan

(integer) 1

192.168.2.129:6379> hset myhash age 20

(integer) 1

192.168.2.129:6379> hget myhash name

"zhangsan"

192.168.2.129:6379> hget myhash age

"20"

192.168.2.129:6379>

 

(3)hgetall——获取key对应的HashMap中的所有field的value

 

192.168.2.129:6379> hgetall myhash

1) "name"

2) "zhangsan"

3) "age"

4) "20"

192.168.2.129:6379>

 

(4)其它命令

 

命令

说明

hsetnx

设置key对应的HashMap中的field的value,如果不存在则先创建

hmset

批量设置key对应的HashMap中的field的value

hmget

批量获取key对应的HashMap中的field的value

hincrby

给key对应的HashMap中的field的value加指定的值

hexits

测试key对应的HashMap中的field是否存在

hlen

返回key对应的HashMap中的field的数量

hdel

删除key对应的HashMap中的field

hkeys

返回key对应的HashMap中所有的field

hvals

返回key对应的HashMap中所有的field的value

 

List类型

Redis的List类型其实就是每一个元素都是String类型的双向链表。我们可以从链表的头部和尾部添加或者删除元素。这样的List既可以作为栈,也可以作为队列使用。

 

 

List数据结构内部示意图

应用场景

如好友列表,粉丝列表,消息队列,最新消息排行等。

常用命令

(1)lpush——在key对应的list的头部添加一个元素。

(2)lrange——获取key对应的list的指定下标范围的元素,-1表示获取所有元素。

(3)lpop——从key对应的list的尾部删除一个元素,并返回该元素。

 

192.168.2.129:6379> lpush newlist news1 news2 news3

(integer) 3

192.168.2.129:6379> lrange newlist 0 -1

1) "news3"

2) "news2"

3) "news1"

192.168.2.129:6379> lpop newlist

"news3"

192.168.2.129:6379> lrange newlist 0 -1

1) "news2"

2) "news1"

192.168.2.129:6379>

 

从上面的操作可以看出,lpush、lpop从表头操作。

 

 

(4)rpush——在key对应的list的尾部添加一个元素。

(5)rpop——从key对应的list的尾部删除一个元素,并返回该元素。

 

192.168.2.129:6379> rpush newlist2 news1 news2 news3

(integer) 3

192.168.2.129:6379> lrange newlist2 0 -1

1) "news1"

2) "news2"

3) "news3"

192.168.2.129:6379> rpop newlist2

"news3"

192.168.2.129:6379>

 

从上面的操作可以看出,rpush、rpop从表尾操作。

 

 

(6)其他命令

 

命令

说明

linsert

在key对应的list的特定元素的前或后插入元素

lset

设置key对应的list中指定下标元素的值

lrem

从key对应的list中删除n个和value相同的元素

ltrim

保留key对应的list中指定范围的元素

rpoplpush

从第一个list的尾部移除一个元素并添加到第二个list的头部

llen

返回key对应的list的长度

lindex

返回key对应的list中index的元素

 

Set类型

Redis 集合(Set类型)是一个无序的String类型数据的集合,类似List的一个列表,与List不同的是Set不能有重复的数据。实际上,Set的内部是用HashMap实现的,Set只用了HashMap的key列来存储对象。我们来看看java中HashSet的源码:

 

public class HashSet<E>

    extends AbstractSet<E>

    implements Set<E>, Cloneable, java.io.Serializable

{

    static final long serialVersionUID = -5024744406713321676L;

 

    private transient HashMap<E,Object> map;

 

    // Dummy value to associate with an Object in the backing Map

    private static final Object PRESENT = new Object();

 

    /**

     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has

     * default initial capacity (16) and load factor (0.75).

     */

   public HashSet() {

        map = new HashMap<>();

}

......

 

/**

     * Returns an iterator over the elements in this set.  The elements

     * are returned in no particular order.

     *

     * @return an Iterator over the elements in this set

     * @see ConcurrentModificationException

     */

    public Iterator<E> iterator() {

        return map.keySet().iterator();

    }

 

 

可见创建一个HashSet的时候实际上创建了一个HashMap;Set中的元素,只是存放在了底层HashMap的key上,底层HashMap的value列为空,遍历HashSet的时候从HashMap中取出keySet来遍历。

 

 

 

Set底层结构示意图

应用场景

集合有取交集、并集、差集等操作,因此可以求共同好友、共同兴趣、分类标签等。

常用命令

(1)sadd——在key对应的set中添加一个元素。

(2)smembers——获取key对应的set的所有元素。

(3)spop——随机返回并删除key对应的set中的一个元素。

 

192.168.2.129:6379> sadd myset news1 news2 news3

(integer) 3

192.168.2.129:6379> smembers myset

1) "news3"

2) "news2"

3) "news1"

192.168.2.129:6379> spop myset

"news3"

192.168.2.129:6379>

 

(4)sdiff——求给定key对应的set与第一个key对应的set的差集

 

192.168.2.129:6379> smembers myset

1) "news3"

2) "news2"

3) "news1"

192.168.2.129:6379> sadd myset2 news3 news4 news5

(integer) 3

192.168.2.129:6379> smembers myset2

1) "news4"

2) "news3"

3) "news5"

192.168.2.129:6379> sdiff myset myset2

1) "news1"

2) "news2"

192.168.2.129:6379>

 

(5)suion——求给定key对应的set并集

 

192.168.2.129:6379> sunion myset myset2

1) "news3"

2) "news1"

3) "news2"

4) "news4"

5) "news5"

192.168.2.129:6379>

 

(6)sinter——求给定key对应的set交集

 

192.168.2.129:6379> sinter myset myset2

1) "news3"

192.168.2.129:6379>

 

(7)其他命令

 

命令

说明

srem

删除key对应的set中的一个元素

sdiffstore

求给定key对应的set与第一个key对应的set的差集,并存储到另一个key对应的set中

sinterstore

求给定key对应的set交集,并存储到另一个key对应的set中

suionstore

求给定key对应的set并集,并存储到另一个key对应的set中

somve

从第一个key对应的set中删除指定元素并添加到第二个key对应的set中

scard

返回key对应的set的元素个数

sismember

测试某个元素是否为key对应的set中的元素个数

srandmember

随机返回key对应的set中的一个元素,但不删除元素

SortSet

SortSet顾名思义,是一个排好序的Set,它在Set的基础上增加了一个顺序属性score,这个属性在添加修改元素时可以指定,每次指定后,SortSet会自动重新按新的值排序。

sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score。

应用场景

如按时间排序的时间轴。

常用命令

(1)zadd ——在key对应的zset中添加一个元素

(2)zrange——获取key对应的zset中指定范围的元素,-1表示获取所有元素

 

192.168.2.129:6379> zadd myzset 1 "one" 2 "two" 3 "three"

(integer) 3

192.168.2.129:6379> zrange myzset 0 -1

1) "one"

2) "two"

3) "three"

192.168.2.129:6379> zrange myzset 0 -1 withscores

1) "one"

2) "1"

3) "two"

4) "2"

5) "three"

6) "3"

192.168.2.129:6379>

 

(3)zrem——删除key对应的zset中的一个元素

 

192.168.2.129:6379> zrem myzset one

(integer) 1

192.168.2.129:6379> zrange myzset 0 -1 withscores

1) "two"

2) "2"

3) "three"

4) "3"

192.168.2.129:6379>

 

(4)其它命令

 

命令

说明

zincrby

如果key对应的zset中已经存在元素member,则对member的score属性加指定的值

zrank

返回key对应的zset中指定member的排名。其中member按score值递增(从小到大);排名以0为底,也就是说,score值最小的成员排名为0

zrevrank

获得成员按score值递减(从大到小)排列的排名

zrevrange

返回有序集key中,指定区间内的成员。其中成员的位置按score值递减(从大到小)来排列

zrangebyscore

返回有序集key中,指定分数范围的元素列表

zcount

返回有序集key中,score值在min和max之间(默认包括score值等于min或max)的成员

zcard

返回key的有序集元素个数

Redis常用命令

键值常用命令

keys/exits/del/expire/ttl/move/persist/randomkey/rename/type

服务器常用命令

ping/echo/select/quit/dbsize/info/config get/flushdb/flushall

 

相关引用:https://www.cnblogs.com/hjwublog/p/5639990.html

 

读PHP-Manual

file部分:

flock — 轻便的咨询文件锁定

LOCK_EX 互斥锁

LOCK_SH 共享锁

 /* Activate the LOCK_NB option on an LOCK_EX operation */
if(!flock($fp, LOCK_EX | LOCK_NB)) { // 非阻塞的上互斥锁 失败直接返回
    echo 'Unable to obtain lock';
    exit(-1);
 
fgets() 从文件句柄中读取一行
fgetss() 从文件句柄中读取一行,并且去除Html标签
 
Arrays 数组操作
 key_exists — 别名 array_key_exists() ----优雅的 isset
 array_rand — 从数组中随机取出一个或多个单元 

array_merge() 将一个或多个数组的单元合并起来,一个数组中的值附加在前一个数组的后面。返回作为结果的数组。

如果输入的数组中有相同的字符串键名,则该键名后面的值将覆盖前一个值。然而,如果数组包含数字键名,后面的值将不会覆盖原来的值,而是附加到后面。

array_key_last/first ----  Gets the last key of an array

 

String操作

strchr该函数返回 haystack 字符串中的一部分,这部分以 needle 的最后出现位置开始,直到 haystack 末尾。字符串首次出现(strstr) 返回匹配到的字符串

strpos ---- 字符串首次出现的位置 返回int

 strrpos — 计算指定字符串在目标字符串中最后一次出现的位置  返回int
 strrchr — 查找指定字符在字符串中的最后一次出现  返回匹配到的字符串

strstr 返回 haystack 字符串从 needle 第一次出现的位置开始到 haystack 结尾的字符串

 strip_tags — 从字符串中去除 HTML 和 PHP 标记
 strspn — 获取匹配遮罩的起始子字符串的长度(最长匹配)
 strcspn — 获取不匹配遮罩的起始子字符串的长度 
 nl2br — 在字符串所有新行之前插入 HTML 换行标记 - 方便发送email之类

int levenshtein ( string $str1 , string $str2 , int $cost_ins , int $cost_rep , int $cost_del ) 计算两个str的编辑距离 。。。据说这个经常作为面试题~

 string base_convert ( string $number , int $frombase , int $tobase ) 数值的任意进制转换
string heredoc 和 nowdoc区别 :nowdoc和单引号一样 不解析相关变量
一个 nowdoc 结构也用和 heredocs 结构一样的标记 <<<, 但是跟在后面的标识符要用单引号括起来,即 <<<'EOT'。Heredoc 结构的所有规则也同样适用于 nowdoc 结构,尤其是结束标识符的规则。
 
realpath Cache
realpath为PHP为require、include时进行引用文件时,因为使用变量而缓慢进行缓存优化,引用文件时,会将文件绝对地址解析并且缓存,这样以后在引用文件的时候,可以直接找到指定文件进行引用,提升性能。
PHP的相关缓存,简单的说,可以将脚本文件的script mtime作为更新根据,但是也要注意相关事件问题。
 
output_buffering

output buffering 作为PHP的输出缓存。首先来了解一下,什么是buffer吧!

buffer是一个内存地址空间,Linux系统默认大小一般为4096(4kb),即一个内存页。主要用于存储速度不同步的设备或者优先级不同的设备之间传办理数据的区域。通过buffer,可以使进程这间的相互等待变少。这里说一个通俗一点的例子,你打开文本编辑器编辑一个文件的时候,你每输入一个字符,操作系统并不会立即把这个字符直接写入到磁盘,而是先写入到buffer,当写满了一个buffer的时候,才会把buffer中的数据写入磁盘,当然当调用内核函数flush()的时候,强制要求把buffer中的脏数据写回磁盘。
 
同样的道理,当执行echo,print的时候,输出并没有立即通过tcp传给客户端浏览器显示, 而是将数据写入php buffer。php output_buffering机制,意味在tcp buffer之前,建立了一新的队列,数据必须经过该队列。当一个php buffer写满的时候,脚本进程会将php buffer中的输出数据交给系统内核交由tcp传给浏览器显示。所以,数据会依次写到这几个地方:echo/print -> php buffer -> tcp buffer -> browser
 
在学习PHP的时候,有遇到过一个现象,PHP作为FCGI模式运行,执行了一个循环,甚至使用了sleep也无法做到逐个输出,这就是因为output buffering,输出先到了buffer,到达指定大小,才输出至client。
你可以在php.ini中关闭output buffering 这样就可以做到逐个输出了:)
正因为它叫output buffer 因此 有些缓存操作函数 前缀就是ob
ob_start()
激活output_buffering机制。一旦激活,脚本输出不再直接出给浏览器,而是先暂时写入php buffer内存区域。

php默认开启output_buffering机制,只不过,通过调用ob_start()函数据output_buffering值扩展到足够大。也可以指定$chunk_size来指定output_buffering的值。$chunk_size默认值是0,表示直到脚本运行结束,php buffer中的数据才会发送到浏览器。如果你设置了$chunk_size的大小,则表示只要buffer中数据长度达到了该值,就会将buffer中的数据发送给浏览器。
 
ob_end_flush ob_end_clean
这二个函数有点相似,都会关闭ouptu_buffering机制。但不同的是,ob_end_flush只是把php buffer中的数据冲(flush/send)到客户端浏览器,而ob_clean_clean将php bufeer中的数据清空(erase),但不发送给客户端浏览器。ob_end_flush调用之后,php buffer中的数据依然存在,ob_get_contents()依然可以获取php buffer中的数据拷贝。而ob_end_clean()调用之后ob_get_contents()取到的是空字符串,同时浏览器也接收不到输出,即没有任何输出。
 
CPP的Sizeof
返回对象或者类型占用的字节数

1.      基本数据类型的sizeof

这里的基本数据类型是指short、int、long、float、double这样的简单内置数据类型。

由于它们的内存大小是和系统相关的,所以在不同的系统下取值可能不同。

 

2.      结构体的sizeof

结构体的sizeof涉及到字节对齐问题。

为什么需要字节对齐?计算机组成原理教导我们这样有助于加快计算机的取数速度,否则就得多花指令周期了。为此,编译器默认会对结构体进行处理(实际上其它地方的数据变量也是如此),让宽度为2的基本数据类型(short等)都位于能被2整除的地址上,让宽度为4的基本数据类型(int等)都位于能被4整除的地址上,依次类推。这样,两个数中间就可能需要加入填充字节,所以整个结构体的sizeof值就增长了。

字节对齐的细节和编译器的实现相关,但一般而言,满足三个准则:

1)  结构体变量的首地址能够被其最宽基本类型成员的大小所整除。

2)  结构体的每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要,编译器会在成员之间加上填充字节(internal adding)。

3)  结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员后加上填充字节(trailing padding)。

 

    注意:空结构体(不含数据成员)的sizeof值为1。试想一个“不占空间“的变量如何被取地址、两个不同的“空结构体”变量又如何得以区分呢,于是,“空结构体”变量也得被存储,这样编译器也就只能为其分配一个字节的空间用于占位了。

3.      联合体的sizeof

结构体在内存组织上市顺序式的,联合体则是重叠式,各成员共享一段内存;所以整个联合体的sizeof也就是每个成员sizeof的最大值。

4.      数组的sizeof

数组的sizeof值等于数组所占用的内存字节数。

注意:1)当字符数组表示字符串时,其sizeof值将’/0’计算进去。

            2)当数组为形参时,其sizeof值相当于指针的sizeof值。 

5.      指针的sizeof

指针是用来记录另一个对象的地址,所以指针的内存大小当然就等于计算机内部地址总线的宽度。

在32位计算机中,一个指针变量的返回值必定是4。

指针变量的sizeof值与指针所指的对象没有任何关系

int i;  
sizeof(int); //值为4  
sizeof(i); //值为4,等价于sizeof(int)  
sizeof i; //值为4  
sizeof(2); //值为4,等价于sizeof(int),因为2的类型为int  
sizeof(2 + 3.14); //值为8,等价于sizeof(double),因为此表达式的结果的类型为double  

char ary[sizeof(int) * 10]; //OK,编译无误
struct S1  
{  
    char a;  
    int b;  
};  
sizeof(S1); //值为8,字节对齐,在char之后会填充3个字节。  
  
struct S2  
{  
    int b;  
    char a;  
};  
sizeof(S2); //值为8,字节对齐,在char之后会填充3个字节。  
  
struct S3  
{  
};  
sizeof(S3); //值为1,空结构体也占内存
union u  
{  
    int a;  
    float b;  
    double c;  
    char d;  
};  
  
sizeof(u); //值为8
char *b = "helloworld";  
char *c[10];  
double *d;  
int **e;  
void (*pf)();    
  
cout<<"char *b = /"helloworld/"     "<<sizeof(b)<<endl;//指针指向字符串,值为4  
cout<<"char *b                    "<<sizeof(*b)<<endl; //指针指向字符,值为1  
cout<<"double *d                  "<<sizeof(d)<<endl;//指针,值为4  
cout<<"double *d                  "<<sizeof(*d)<<endl;//指针指向浮点数,值为8  
cout<<"int **e                  "<<sizeof(e)<<endl;//指针指向指针,值为4  
cout<<"char *c[10]                "<<sizeof(c)<<endl;//指针数组,值为40  
cout<<"void (*pf)();              "<<sizeof(pf)<<endl;//函数指针,值为4

 

如何判断一个机器是32位还是64位 辨别系统是大端还是小端字节序

字节序的概念就是说,在系统内存里面,随着变量的增加,系统内存的地址生长方向,是由高到低还是由低到高

#include <stdio.h>
union U
{
    short i;
    char a[2];
};
int main()
{
    int *p;
    printf("%d\n",sizeof(p));//4 32位
 
    printf("sizeof(short):%d\n",sizeof(short));//2
 
    union U u;
    u.i=0x0102;
 
    printf("%d %d\n",u.a[0],u.a[1]);// 1 2说明是大端字节序否则小端
    
}

最高有效字节在最前面的方式称为大端法,例如假设变量x类型为int型,位于地址0x100的地方,其16进制值为0x12345678,地址范围为0x100到0x103字节。

对于大端法的机器来说:

0x1000x1010x1020x103
12 34 56 78

由上图可见,地址从左向右增长,x的最高有效字节12在最前面存储。这正好和我们平时书写习惯一致,先书写最高有效字节,再依次写其余字节。

 

最低有效字节在最前面的方式成为小端法,这正好和大端法相反,仍然用大端法中举的例子说明:

0x1000x1010x1020x103
78 56 34 12

由上图可见,地址依然从左向右增长,x的最低有效字节在最前面存储,与大端法相反。

 

动态链接库、静态库、import库区别

动态链接库(Dynamic Linked Library):
Windows为应用程序提供了丰富的函数调用,这些函数调用都包含在动态链接库中。其中有3个最重要的DLL,Kernel32.dll,它包含用于管理内存、进程和线程的各个函数;User32.dll,它包含用于执行用户界面任务(如窗口的创建和消息的传送)的各个函数;GDI32.dll,它包含用于画图和显示文本的各个函数。

静态库(Static Library):
函数和数据被编译进一个二进制文件(通常扩展名为.LIB)。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其它模块组合起来创建最终的可执行文件(.EXE文件)。

 

Linux 进程死锁

死锁。就是几个进程申请资源,出现了无限循环等待的现象。

四个必要条件

  1. 资源是互斥的
  2. 不可抢占
  3. 占有且申请
  4. 循环等待

A、B进程都需要打印机和CDROM。现在A占有了打印机,B占有了CDROM。 
1. 打印机和CDROM都是互斥的,打印机一个时间只能让一个进程使用,CDROM也是。 
2. A进程不能要求B进程放弃它拥有的CDROM,B进程也不能要求A放弃它所拥有的打印机。 
3. A占有了打印机还去申请CDROM,B占有了CDROM还去申请打印机。 
4. A等待B,B也在等待A。

解决方案也从这四个条件来着手。 
1. 资源的互斥是客观的,要改变的话不现实。 
2. 申请资源不可满足的时候释放已经申请到的资源。 
3. 只有在全部资源都可以得到的情况下才一次性分配。 
4. 资源有序分配。给所有资源编号,进程对资源的请求必须是严格递增的序列,只有在占有了小号资源的情况下才能申请了大号资源。(假设打印机是小号资源,CDROM是大号资源)

 

 

 
 
 
最近看的感觉不错的面经:
Tencent:https://blog.csdn.net/fdssdfdsf/article/details/7983861
CPP高危函数(一般都是溢出,或者str不复制‘\0’):https://blog.csdn.net/sea_mo/article/details/38658747
CPP实现ATOI:https://www.cnblogs.com/lcchuguo/p/5405878.html
awk:https://www.cnblogs.com/xudong-bupt/p/3721210.html
IPC 共享内存:https://www.ibm.com/developerworks/cn/linux/l-ipc/part5/index2.html
netstat  http://man.linuxde.net/netstat
ipcs 列出当前共享变量表 http://man.linuxde.net/ipcs
ipcrm 删除指定共享变量 http://man.linuxde.net/ipcrm
Mysql:https://blog.csdn.net/qq_39322743/article/details/79701420
 
 
 
posted @ 2018-09-10 23:59  guoguoqingzhe  阅读(116)  评论(0编辑  收藏  举报