作者:刘昊昱

博客:http://blog.csdn.net/liuhaoyutz

内核版本:3.10.1

 
一、kset结构定义
kset结构体定义在include/linux/kobject.h文件中,其内容如下:
142/**
143 * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem.
144 *
145 * A kset defines a group of kobjects. They can be individually
146 * different "types" but overall these kobjects all want to be grouped
147 * together and operated on in the same manner. ksets are used to
148 * define the attribute callbacks and other common events that happen to
149 * a kobject.
150 *
151 * @list: the list of all kobjects for this kset
152 * @list_lock: a lock for iterating over the kobjects
153 * @kobj: the embedded kobject for this kset (recursion, isn't it fun...)
154 * @uevent_ops: the set of uevent operations for this kset. These are
155 * called whenever a kobject has something happen to it so that the kset
156 * can add new environment variables, or filter out the uevents if so
157 * desired.
158 */
159struct kset {
160 struct list_head list;
161 spinlock_t list_lock;
162 struct kobject kobj;
163 const struct kset_uevent_ops *uevent_ops;
164};

从注释可以看出,kset是一组kobject的集合,这些kobject可以具有不同的“types”,下面来看kset的成员变量:
list用于将该kset下的所有kobject链接成一个链表。
list_lock是一个自旋锁,在遍历该kset下的kobject时用来加锁。
kobj是代表该kset的一个kobject。
uevent_ops是一组函数指针,当kset中的某个kobject状态发生变化需要通知用户空间时,就通过这些函数来完成。uevent_ops是struct kset_uevent_ops类型,该结构体定义在include/linux/kobject.h文件中,其定义如下:
123struct kset_uevent_ops {
124 int (* const filter)(struct kset *kset, struct kobject *kobj);
125 const char *(* const name)(struct kset *kset, struct kobject *kobj);
126 int (* const uevent)(struct kset *kset, struct kobject *kobj,
127 struct kobj_uevent_env *env);
128};

关于kset_uevent_ops结构体中的成员函数的作用,我们后面再分析。
 
二、kset的创建和注册
要创建并注册一个kset,使用的是kset_create_and_add函数,该函数定义在lib/kobject.c文件中,其内容如下:
827/**
828 * kset_create_and_add - create a struct kset dynamically and add it to sysfs
829 *
830 * @name: the name for the kset
831 * @uevent_ops: a struct kset_uevent_ops for the kset
832 * @parent_kobj: the parent kobject of this kset, if any.
833 *
834 * This function creates a kset structure dynamically and registers it
835 * with sysfs. When you are finished with this structure, call
836 * kset_unregister() and the structure will be dynamically freed when it
837 * is no longer being used.
838 *
839 * If the kset was not able to be created, NULL will be returned.
840 */
841struct kset *kset_create_and_add(const char *name,
842 const struct kset_uevent_ops *uevent_ops,
843 struct kobject *parent_kobj)
844{
845 struct kset *kset;
846 int error;
847
848 kset = kset_create(name, uevent_ops, parent_kobj);
849 if (!kset)
850 return NULL;
851 error = kset_register(kset);
852 if (error) {
853 kfree(kset);
854 return NULL;
855 }
856 return kset;
857}

828行,从注释可以看出,kset_create_and_add函数的作用是动态创建一个kset结构并把它注册到sysfs文件系统中。注意该函数的三个参数:
name是kset的名字,它会被赋值给kset.kobj.name。
uevent_ops是struct kset_uevent_ops变量,它会被赋值给kset.uevent_ops。
parent_kobj是该kset的父kobject,它会被赋值给kset.kobj.parent。
848行,调用kset_create函数动态创建kset结构并对其进行初始化,该函数定义在lib/kobject.c文件中,其内容如下:
783/**
784 * kset_create - create a struct kset dynamically
785 *
786 * @name: the name for the kset
787 * @uevent_ops: a struct kset_uevent_ops for the kset
788 * @parent_kobj: the parent kobject of this kset, if any.
789 *
790 * This function creates a kset structure dynamically. This structure can
791 * then be registered with the system and show up in sysfs with a call to
792 * kset_register(). When you are finished with this structure, if
793 * kset_register() has been called, call kset_unregister() and the
794 * structure will be dynamically freed when it is no longer being used.
795 *
796 * If the kset was not able to be created, NULL will be returned.
797 */
798static struct kset *kset_create(const char *name,
799 const struct kset_uevent_ops *uevent_ops,
800 struct kobject *parent_kobj)
801{
802 struct kset *kset;
803 int retval;
804
805 kset = kzalloc(sizeof(*kset), GFP_KERNEL);
806 if (!kset)
807 return NULL;
808 retval = kobject_set_name(&kset->kobj, name);
809 if (retval) {
810 kfree(kset);
811 return NULL;
812 }
813 kset->uevent_ops = uevent_ops;
814 kset->kobj.parent = parent_kobj;
815
816 /*
817 * The kobject of this kset will have a type of kset_ktype and belong to
818 * no kset itself. That way we can properly free it when it is
819 * finished being used.
820 */
821 kset->kobj.ktype = &kset_ktype;
822 kset->kobj.kset = NULL;
823
824 return kset;
825}

805行,为kset结构分配内存空间。
808行,将name参数赋值给kset.kobj.name。它对应kset在sysfs文件系统中的目录名。
813行,将uevent_ops赋值给kset->uevent_ops。
814行,将parent_kobj 赋值给kset->kobj.parent。
816-822行,由注释可以知道,kset.kobj.ktype被赋于一个kset_ktype类型,并且kset.kobj.kset为NULL,即该kset不属于任何其它kset。这样可以保证在不再继续使用该kset时可以正确的释放它。这里我们要来看一下kset_ktype的定义,它定义在lib/kobject.c文件中,其内容如下:
778static struct kobj_type kset_ktype = {
779 .sysfs_ops = &kobj_sysfs_ops,
780 .release = kset_release,
781};

kobj_sysfs_ops定义在lib/kobject.c文件中,其内容如下:
708const struct sysfs_ops kobj_sysfs_ops = {
709 .show = kobj_attr_show,
710 .store = kobj_attr_store,
711};

结合上篇文章中对kobject的分析,我们可以得出如下结论:
如果用户空间程序要对kset对应的sysfs文件系统下的属性文件进行读操作时,kobj_attr_show函数会被调用。
如果用户空间程序要对kset对应的sysfs文件系统下的属性文件进行写操作时,
kobj_attr_store函数会被调用。
下面我们来看kobj_attr_show函数,它定义在lib/kobject.c文件中:
683/* default kobject attribute operations */
684static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,
685 char *buf)
686{
687 struct kobj_attribute *kattr;
688 ssize_t ret = -EIO;
689
690 kattr = container_of(attr, struct kobj_attribute, attr);
691 if (kattr->show)
692 ret = kattr->show(kobj, kattr, buf);
693 return ret;
694}

注意683行的注释,这是默认的kobject attribute操作函数。在这函数中,通过container_of取得包含attr变量的struct kobj_attribute变量kattr,然后调用kattr->show()函数。
kobj_attr_store函数与kobj_attr_show函数类似,同样定义在lib/kobject.c文件中:
696static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,
697 const char *buf, size_t count)
698{
699 struct kobj_attribute *kattr;
700 ssize_t ret = -EIO;
701
702 kattr = container_of(attr, struct kobj_attribute, attr);
703 if (kattr->store)
704 ret = kattr->store(kobj, kattr, buf, count);
705 return ret;
706}

在该函数中,通过container_of取得包含attr变量的struct kobj_attribute变量kattr,然后调用kattr->store()函数。
这样,如果用户空间程序要对kset对应的sysfs文件系统下的属性文件进行读写操作时,就会转而调用包含相应attribute的kobj_attribute结构体的show/store函数。实际上这种用法是和宏__ATTR结合在一起使用的,后面我们会再分析。
到此,kobject_create函数我们就分析完了,回到kset_create_and_add函数,
851行,调用kset_register(kset)函数注册kset,该函数定义在lib/kobject.c文件中,其内容如下:
713/**
714 * kset_register - initialize and add a kset.
715 * @k: kset.
716 */
717int kset_register(struct kset *k)
718{
719 int err;
720
721 if (!k)
722 return -EINVAL;
723
724 kset_init(k);
725 err = kobject_add_internal(&k->kobj);
726 if (err)
727 return err;
728 kobject_uevent(&k->kobj, KOBJ_ADD);
729 return 0;
730}

724行,首先对kset进行初始化。kset的初始化是通过调用kset_init函数完成的,该函数定义在lib/kobject.c文件中,其内容如下:
672/**
673 * kset_init - initialize a kset for use
674 * @k: kset
675 */
676void kset_init(struct kset *k)
677{
678 kobject_init_internal(&k->kobj);
679 INIT_LIST_HEAD(&k->list);
680 spin_lock_init(&k->list_lock);
681}

可见,只是简单初始化kset.kobj,kset.list,和kset.list_lock。
725行,将kset.kobj加入到kobject层次结构和sysfs文件系统中。
728行,调用kobject_uevent(&k->kobj, KOBJ_ADD),通知用户空间添加了一个kobject,即kset.kobj。kobject_uevent函数定义在lib/kobject_uevent.c文件中,其内容如下:
322/**
323 * kobject_uevent - notify userspace by sending an uevent
324 *
325 * @action: action that is happening
326 * @kobj: struct kobject that the action is happening to
327 *
328 * Returns 0 if kobject_uevent() is completed with success or the
329 * corresponding error when it fails.
330 */
331int kobject_uevent(struct kobject *kobj, enum kobject_action action)
332{
333 return kobject_uevent_env(kobj, action, NULL);
334}

从注释可以看出,kobject_uevent函数的作用是通过发送一个uevent通知用户空间内核中发生了某些事情。至于发生了什么事情,由第二个参数action指定,action是enum kobject_action类型变量,定义在include/linux/kobject.h文件中,其内容如下:
 40/*
41 * The actions here must match the index to the string array
42 * in lib/kobject_uevent.c
43 *
44 * Do not add new actions here without checking with the driver-core
45 * maintainers. Action strings are not meant to express subsystem
46 * or device specific properties. In most cases you want to send a
47 * kobject_uevent_env(kobj, KOBJ_CHANGE, env) with additional event
48 * specific variables added to the event environment.
49 */
50enum kobject_action {
51 KOBJ_ADD,
52 KOBJ_REMOVE,
53 KOBJ_CHANGE,
54 KOBJ_MOVE,
55 KOBJ_ONLINE,
56 KOBJ_OFFLINE,
57 KOBJ_MAX
58};

可见,一共有这7种事件可以通知用户空间。
回到kobject_uevent函数,333行,调用kobject_uevent_env函数来发送uevent,该函数定义在lib/kobject_uevent.c文件中,其内容如下:
121/**
122 * kobject_uevent_env - send an uevent with environmental data
123 *
124 * @action: action that is happening
125 * @kobj: struct kobject that the action is happening to
126 * @envp_ext: pointer to environmental data
127 *
128 * Returns 0 if kobject_uevent_env() is completed with success or the
129 * corresponding error when it fails.
130 */
131int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
132 char *envp_ext[])
133{
134 struct kobj_uevent_env *env;
135 const char *action_string = kobject_actions[action];
136 const char *devpath = NULL;
137 const char *subsystem;
138 struct kobject *top_kobj;
139 struct kset *kset;
140 const struct kset_uevent_ops *uevent_ops;
141 int i = 0;
142 int retval = 0;
143#ifdef CONFIG_NET
144 struct uevent_sock *ue_sk;
145#endif
146
147 pr_debug("kobject: '%s' (%p): %s\n",
148 kobject_name(kobj), kobj, __func__);
149
150 /* search the kset we belong to */
151 top_kobj = kobj;
152 while (!top_kobj->kset && top_kobj->parent)
153 top_kobj = top_kobj->parent;
154
155 if (!top_kobj->kset) {
156 pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
157 "without kset!\n", kobject_name(kobj), kobj,
158 __func__);
159 return -EINVAL;
160 }
161
162 kset = top_kobj->kset;
163 uevent_ops = kset->uevent_ops;
164
165 /* skip the event, if uevent_suppress is set*/
166 if (kobj->uevent_suppress) {
167 pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
168 "caused the event to drop!\n",
169 kobject_name(kobj), kobj, __func__);
170 return 0;
171 }
172 /* skip the event, if the filter returns zero. */
173 if (uevent_ops && uevent_ops->filter)
174 if (!uevent_ops->filter(kset, kobj)) {
175 pr_debug("kobject: '%s' (%p): %s: filter function "
176 "caused the event to drop!\n",
177 kobject_name(kobj), kobj, __func__);
178 return 0;
179 }
180
181 /* originating subsystem */
182 if (uevent_ops && uevent_ops->name)
183 subsystem = uevent_ops->name(kset, kobj);
184 else
185 subsystem = kobject_name(&kset->kobj);
186 if (!subsystem) {
187 pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
188 "event to drop!\n", kobject_name(kobj), kobj,
189 __func__);
190 return 0;
191 }
192
193 /* environment buffer */
194 env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
195 if (!env)
196 return -ENOMEM;
197
198 /* complete object path */
199 devpath = kobject_get_path(kobj, GFP_KERNEL);
200 if (!devpath) {
201 retval = -ENOENT;
202 goto exit;
203 }
204
205 /* default keys */
206 retval = add_uevent_var(env, "ACTION=%s", action_string);
207 if (retval)
208 goto exit;
209 retval = add_uevent_var(env, "DEVPATH=%s", devpath);
210 if (retval)
211 goto exit;
212 retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
213 if (retval)
214 goto exit;
215
216 /* keys passed in from the caller */
217 if (envp_ext) {
218 for (i = 0; envp_ext[i]; i++) {
219 retval = add_uevent_var(env, "%s", envp_ext[i]);
220 if (retval)
221 goto exit;
222 }
223 }
224
225 /* let the kset specific function add its stuff */
226 if (uevent_ops && uevent_ops->uevent) {
227 retval = uevent_ops->uevent(kset, kobj, env);
228 if (retval) {
229 pr_debug("kobject: '%s' (%p): %s: uevent() returned "
230 "%d\n", kobject_name(kobj), kobj,
231 __func__, retval);
232 goto exit;
233 }
234 }
235
236 /*
237 * Mark "add" and "remove" events in the object to ensure proper
238 * events to userspace during automatic cleanup. If the object did
239 * send an "add" event, "remove" will automatically generated by
240 * the core, if not already done by the caller.
241 */
242 if (action == KOBJ_ADD)
243 kobj->state_add_uevent_sent = 1;
244 else if (action == KOBJ_REMOVE)
245 kobj->state_remove_uevent_sent = 1;
246
247 mutex_lock(&uevent_sock_mutex);
248 /* we will send an event, so request a new sequence number */
249 retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)++uevent_seqnum);
250 if (retval) {
251 mutex_unlock(&uevent_sock_mutex);
252 goto exit;
253 }
254
255#if defined(CONFIG_NET)
256 /* send netlink message */
257 list_for_each_entry(ue_sk, &uevent_sock_list, list) {
258 struct sock *uevent_sock = ue_sk->sk;
259 struct sk_buff *skb;
260 size_t len;
261
262 if (!netlink_has_listeners(uevent_sock, 1))
263 continue;
264
265 /* allocate message with the maximum possible size */
266 len = strlen(action_string) + strlen(devpath) + 2;
267 skb = alloc_skb(len + env->buflen, GFP_KERNEL);
268 if (skb) {
269 char *scratch;
270
271 /* add header */
272 scratch = skb_put(skb, len);
273 sprintf(scratch, "%s@%s", action_string, devpath);
274
275 /* copy keys to our continuous event payload buffer */
276 for (i = 0; i < env->envp_idx; i++) {
277 len = strlen(env->envp[i]) + 1;
278 scratch = skb_put(skb, len);
279 strcpy(scratch, env->envp[i]);
280 }
281
282 NETLINK_CB(skb).dst_group = 1;
283 retval = netlink_broadcast_filtered(uevent_sock, skb,
284 0, 1, GFP_KERNEL,
285 kobj_bcast_filter,
286 kobj);
287 /* ENOBUFS should be handled in userspace */
288 if (retval == -ENOBUFS || retval == -ESRCH)
289 retval = 0;
290 } else
291 retval = -ENOMEM;
292 }
293#endif
294 mutex_unlock(&uevent_sock_mutex);
295
296 /* call uevent_helper, usually only enabled during early boot */
297 if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
298 char *argv [3];
299
300 argv [0] = uevent_helper;
301 argv [1] = (char *)subsystem;
302 argv [2] = NULL;
303 retval = add_uevent_var(env, "HOME=/");
304 if (retval)
305 goto exit;
306 retval = add_uevent_var(env,
307 "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
308 if (retval)
309 goto exit;
310
311 retval = call_usermodehelper(argv[0], argv,
312 env->envp, UMH_WAIT_EXEC);
313 }
314
315exit:
316 kfree(devpath);
317 kfree(env);
318 return retval;
319}

122行,从注释可以看出,kobject_uevent_env函数的作用是发送带有环境变量数据的uevent。
150-160行,查找kobject所属的kset,如果这个kobject没有所属的kset,则看这个kobject.parent有没有所属的kset,如果还没有,继续沿着kobject层次结构树向上查找,直到找到一个具有所属kset的祖先kobject,如果确实没有找到,则出错退出。所以当前kobject的层次结构树中,必须有一个具有所属的kset。因为对事件的处理函数包含在kobject.kset.uevent_ops中,要处理事件,就必须找到上层一个不为空的kset。
值得注意的是,在创建kset的过程中,kset_create_and_add->kset_create,在kset_create函数中,将kset.kobj.kset设置为NULL,所以kset.kobj本身没有所属的kset,但是同样在kset_create函数中,kset.kobj.parent设置为parent_kobj,所以kset.kobj必然通过其上层祖先查找kset。
162行,取得相应的kset。
163行,将kset.uevent_ops赋值给uevent_ops变量。
165-171行,如果kobj->uevent_suppress被设置为1,则不发送uevent,退出。
172-179行,如果uevent_ops->filter(kset, kobj)返回值为0,说明kobj希望发送的uevent被顶层kset过滤掉了,不再发送。
181-191行,通过uevent_ops->name函数取得子系统名,如果uevent_ops->name为NULL,则使用kset.kobj.name做为子系统名。事实上,一个kset就是一个所谓的“subsystem”。
194行,分配struct kobj_uevent_env变量空间给env,该结构体用来保存环境变量,它定义在include/linux/kobject.h文件中,其内容如下:
116struct kobj_uevent_env {
117    char *envp[UEVENT_NUM_ENVP];
118    int envp_idx;
119    char buf[UEVENT_BUFFER_SIZE];
120    int buflen;
121};
199行,调用kobject_get_path取得kobject的绝对路径。
205-214行,调用add_uevent_var函数将ACTION、DEVPATH、SUBSYSTEM三个默认环境变量添加到env中。add_uevent_var函数定义在lib/kobject_uevent.c文件中,其作用是“add key value string to the environment buffer”。
217-223行,如果调用kobject_uevent_env函数时,通过第三个参数envp_ext传递进来了其它相关环境变量,也通过add_uevent_var函数添加到env中。
225-234行,如果uevent_ops->uevent不为空,则调用uevent_ops->uevent,kset可以通过该函数完成自己特定的功能。
236-246行,如果action是KOBJ_ADD,则设置kobj->state_add_uevent_sent为1。如果action是KOBJ_REMOVE,则设置kobj->state_remove_uevent_sent为1。其作用注释中说的很清楚“Mark "add" and "remove" events in the object to ensure proper events to userspace during automatic cleanup. If the object did send an "add" event, "remove" will automatically generated by the core, if not already done by the caller.”。
249行,将SEQNUM环境变量添加到env中。
kobject_uevent_env函数剩下的部分,用来和用户空间进程进行交互(或者在内核空间启动执行一个用户空间程序)。在Linux中,有两种方式完成这种交互,一个是代码中由CONFIG_NET宏包含的部分,即255-293行,这部分代码通过udev的方式向用户空间广播当前kset对象中的uevent事件。另外一种方式是在内核空间启动一个用户空间进程/sbin/hotplug,通过给该进程传递内核设定的环境变量的方式来通知用户空间kset对象中的uevent事件,即代码中296-312行。
热插拔(hotplug)是指当有设备插入或拨出系统时,内核可以检测到这种状态变化,并通知用户空间加载或移除该设备对应的驱动程序模块。在Linux系统上内核有两种机制可以通知用户空间执行加载或移除操作,一种是udev,另一种是/sbin/hotplug,在Linux发展的早期,只有/sbin/hotplug,它的幕后推手是内核中的call_usermodehelper函数,它能从内核空间启动一个用户空间程序。随着内核的发展,出现了udev机制并逐渐取代了/sbin/hotplug。udev的实现基于内核中的网络机制,它通过创建标准的socket接口来监听来自内核的网络广播包,并对接收到的包进行分析处理。
至此,kobject_uevent_env函数我们就分析完了,同时,kobject_uevent、kset_register、kset_create_and_add函数也分析完了,我们了解了kset的创建和注册过程。

Linux设备模型分析之kset(基于3.10.1内核)的更多相关文章

  1. Linux kernel驱动相关抽象概念及其实现 之“linux设备模型kobject,kset,ktype”

    kobject,kset,ktype三个很重要的概念贯穿Linux内核驱动架构,特转载一篇博文: (转载自http://blog.csdn.net/gdt_a20/article/details/64 ...

  2. 【原创】linux设备模型之kset/kobj/ktype分析

    背 景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本 ...

  3. Linux 设备模型之 (kobject、kset 和 Subsystem)(二)

    问题描写叙述:前文我们知道了/sys是包括内核和驱动的实施信息的,用户能够通过 /sys 这个接口.用户通过这个接口能够一览内核设备的全貌.本文将从Linux内核的角度来看一看这个设备模型是怎样构建的 ...

  4. linux设备模型_转

    建议原博文查看,效果更佳. 转自:http://www.cnblogs.com/wwang/category/269350.html Linux设备模型 (1) 随着计算机的周边外设越来越丰富,设备管 ...

  5. Linux设备模型 学习总结

    看LDD3中设备模型一章,觉得思维有些混乱.这里从整体的角度来理理思路.本文从四个方面来总结一些内容: 1.底层数据结构:kobject,kset.2.linux设备模型层次关系:bus_type,d ...

  6. Linux设备模型——设备驱动模型和sysfs文件系统解读

    本文将对Linux系统中的sysfs进行简单的分析,要分析sysfs就必须分析内核的driver-model(驱动模型),两者是紧密联系的.在分析过程中,本文将以platform总线和spi主控制器的 ...

  7. Linux设备模型(总结)

    转:http://www.360doc.com/content/11/1219/16/1299815_173418267.shtml 看了一段时间的驱动编程,从LDD3的hello wrod到后来的字 ...

  8. Linux设备模型 (4)

    <Linux设备模型 (2)>和<Linux设备模型 (3)>主要通过一些简单的实作介绍了kobject.kset.kobj_type.attribute等数据结构的用法,但这 ...

  9. Linux设备模型(9)_device resource management ---devm申请空间【转】

    转自:http://www.wowotech.net/linux_kenrel/device_resource_management.html . 前言 蜗蜗建议,每一个Linux驱动工程师,都能瞄一 ...

随机推荐

  1. MOSS 2010:Visual Studio 2010开发体验(14)——列表开发之事件接收器

    转:http://boke.25k5.com/kan141919.html 通过前面几篇,我们已经完成了内容类型,列表定义,列表实例g 8h"@的开发.本篇继续讲解列表中的一个重要环节- ...

  2. 【转】一个windows线程池实现

    #ifndef _ThreadPool_H_ #define _ThreadPool_H_ #pragma warning(disable: 4530) #pragma warning(disable ...

  3. Flash Player”又来了“!

    2012年,Adobe宣布缩小对Linux平台的支持,自Flash Player 11.2版本之后,只为Linux平台上的Flash Player提供安全更新和bug修复,而不再提供版本更新. 四年之 ...

  4. [Everyday Mathematics]20150207

    求极限 $$\bex \lim_{x\to+\infty}\sex{\sqrt{x+\sqrt{x+\sqrt{x^\al}}}-\sqrt{x}},\quad\sex{0<\al<2}. ...

  5. C# 邮件发送系统

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  6. iOS开发常用输入校验

    1.数字字符校验 #define NUMBERSPERIOD @"0123456789." - (BOOL)CheckInput:(NSString *)string { NSCh ...

  7. matlab的&和&&操作

    A&B(1)首先判断A的逻辑值,然后判断B的值,然后进行逻辑与的计算.(2)A和B可以为矩阵(e.g. A=[1 0],B=[0 0]).A&&B(1)首先判断A的逻辑值,如果 ...

  8. 瞬间从IT屌丝变大神——注释规则

    注释的主要规则如下: 公共组件和各栏目的维护者都需要在文件头部加上注释说明: /** *文件用途说明 *作者姓名 *联系方式*制作日期 **/ 大的模块注释方法: //======= //代码用途 / ...

  9. Iaas概述

    IAAS :设施即服务,为开发者提供存储,计算,网络等资源,整体架构如下图: 整体分为三个部分: 1.Iaas云的管理部分:对整个云有超级用户管理权限,可以查看监控整个云中的资源,租户信息,并进行管理 ...

  10. 通用表表达式(Common Table Expression)

    问题:编写由基本的 SELECT/FROM/WHERE 类型的语句派生而来的复杂 SQL 语句. 方案1:编写在From子句内使用派生表(内联视图)的T-SQL查询语句. 方案2:使用视图 方案3:使 ...