从linus那里pull,当然,前提是你先从linus那里clone一份代码树。
git pull
最好不用 `git pull`,使用`git fetch` 和 `git merge`来代替它.
撤销所有在本地的修改
git checkout -f
查看你所做的修改
git commit -a
撤销最近的提交
git reset HEAD~2
5.List all changes in working dir,in diff format
`git diff`
Dispaly changes since last commit
git diff HEAD
List all changeset descriptions
git log
追踪特定文件的changesets
git log net/ieee80211/ieee80211_module.c
List all branches
git branch
Make desired branch current in working dur
git checkout $branch
Create a new branch, and make it current
git checkout -b my-new-branch master
Examine which branch is current
git status
Obtain a diff between current branch with master branch
git diff master master..HEAD
Obtain a list of changes between current branch with master branch
git log master..HEAD
A one line summary of each changes
git shortlog master..HEAD
假设你在分支A,分支B上完成了工作,你需要把你的工作放进主分支M,
git checkout M
git merge A
git merge B
cd linux-source
git checkout -b tmp v2.6.22
cd linux-kernel-source
git am --utf8 --signoff /path/to/mbox
The –signoff option 暗示git am 在最后加上
Signed-off-by: Your Name <[email protected]>
The name and email are taken from the GIT_COMMITTER_NAME AND GIT_COMMITTER_EMAIL environment variables,(可以设定在.bash_profile 或者相似的文件)
git fetch --tags $URL
Tag a partical commit
cd linux-tree
git tag my-tag
#什麼是愛情
原来ctags的功能这么强大,之前自己是蒙着眼使用的啊。
有人说默认有这个插件,确实有,再下一次吧。
在你的工作目录中,输入以上命令,$是shell的命令提示符,不是命令的一部分,这一点需要注意,这样就会在当前工作目录下产生ctags的标签文件。
xx就是你要查找的变量和函数名
It maybe acted on some impact using “SHIFT + K”
在vim的命令行下输入__:ts__就会列出列表供用户选择满足的变量或者函数(tagslist)。
display preview tags-file and display next tags-file.
jump to the function or variable where whose were defined.
Return where jump from where
Waa…
command:
cscope -Rbqk
-R:表示把所有子目录里的文件也建立索引
-b:仅仅建立符号数据库
-q:生成cscope.in.out和cscope.po.out文件,加快cscope的索引
-k: 在生成索引文件时,不搜索/usr/include目录
vim的normal模式下输入:
:cs add cscope.out
就把索引文件加入到vim中.将以下命令放在.vimrc中,即可省去该命令:
if filereadable("cscope.out")
cs add cscope.out
endif
再试试把下面的内容复制到~/.vimrc里,这样就可以利用相应的快捷键进行不同的查找了. 原文1 原文2 cscope add cscope.out ##4. Commmand & Options After adding the file that is called cscope.out ,we can call cscope find x 简写 cscope f x
c:Find functions calling this function d:Find functions called by this function e:Find this egrep pattern f:Find this file g:Find this definition i:Find files #including this file s:Find this C symbol t:Find assignments to
#taglist 将这三个工具用起来了,然后在命令窗口中键入:
:TlistToggle
在关闭与打开之间转换.
我的vimrc中定义了下面的映射,使用“,tl”键就可以打开/关闭taglist窗口:
map
----------------- |boot sector| | |___________| | | data sector | |_______________|
__________________0x000 |jmp to this code| |________________|0x003 |disk para table | |________________|0x02c/0x03e | part of code | |_______________ |0x1f
一个硬盘在DOS的文件系统下可分为四个基本分区(primairy partition),如果数目达不到所需的数目,可以设置扩展分区(Extended partition ),整个硬盘一张分区表,它存放在硬盘的第一个分区里,而每个扩展分区也都有一张分区表,它存放在扩展分区的第一个分区表中,整个硬盘的主引导扇区(MBR)如下图所示:
------------------ | code | |________________| |partition table | |________________| | setup signal | |________________|
在启动的过程中,BIOS会把Boot Loader读入内存,并把控制权交给它,MBR(硬盘启动)或者启动扇区(软盘)的代码就是Boot Loader的一部分,这部分的实现其实是很复杂的,Boot Loader实际上会寻找各自的启动的扇区。
我的学习过程中有一个很明显的特点,先知道是什么,最后才知道为什么,不知道这样好不好?
http://faq.perl.org/
Programming Perl(Camel book)
Perl 5 Pocket Reference
其实,perl语言是这么一个流程:在内存中保存他的输出;更新内存;并且打印它
1.scalar : a number,a string,You can act on a scalar value with operators(like additioon or concatenation),You can read scalars from file and devices and write to them as well.
2.在整数常量中,如果数字特别巨大,可以使用 21_323_123_987_365表示
3.Others Nondecimal 0377 #377 octal 0xff #FF hex, 0b11111111 #also 255 decimal
4.如果在程序中使用utf-8,这样 use utf8;
5.在字符串的使用中,需要注意’ ‘的内容,里面除了“‘”和“",其余的都代表 他们自己本身,如果想得到一个“\”,那么就使用“\”,为了表示”’“,同样使用“\“转义; 与此对应的是,双引号的使用,你可以想象成c语言中的双引号
6.使用 “.”符号进行链接剪切等操作 For example:
"hello" . "world" #same as "helloworld"
"hello" ' ' . "world" # same as 'hello world'
'hello world' . "\n" #same as "hello world\n"
7.重复字符串”x”,对,你没看错,就是小写字母x,
"yubo" x 3
"yubo" x (3+1)
5 x 4.8 #same as “5555” 最后一个需要说明一下,在操作符 “x”的左边,我们称之为左值,你可以看成源操作数 ,符号右边是右值,你可以看成是目的操作数,4.8被自动转换为4.
8.在数字和字符串之间自由转换,这只是取决于你自己用在标量的的操作符, 比如,“+”系统默认为左值和右值为数字,“.”左右值默认为字符串 , 比如,”12” * “3”就是36,即便在“12yubo34” * 3的结果依旧是36
1.变量
用$表示,$name,…还可以用于数组
2.变量赋值
$bin = 12;
$asd = $bin *2;
另外需要注意的是在perl变量中,“ ”与shell中的“ ”一样, 在比较多的变量中,使用${varible}调用其值
3.打印特殊符号,先找到ascii码,然后使用 chr()函数,$alef = chr(0x05D0); 即可。
4.有关perl的运算符的优先级和结合性(结合性依次降低)
结合性 运算符
left ->
++ --
right **
right \ ! ~ + -(unary operators)
left =~ !~(?)
left */%x
left =-.(binary operations)
left <<>>
< <= > >=
== != <=>
left &
left | ^
left &&
.. ...(?)
right ?:(conditional operator)
right = += -= .=(and similar assignment operators )
left , =>
right not
left and
left or xor
没有必要记住上面的这些东西,如果你找不到你想要的运算顺序,尽管加上括 号即可(parenthses),the same time:
Numeric String
== eq
!= ne
< lt
> gt
<= le
>= ge
##perl中的bool类型 1.如果返回的值是0为false,其他为true;
2.如果返回的是一个字符串,‘ ’意味着false;其他意味着成功
3.如果还有其他情况,请转化为这两种情况。
1.1,Getting User Input
For example:
$line = <STDIN>;
if ($line eq "\n")
{ print "That was just a blank line!\n";}
else { print "The line of input was: $line";}
这样就是用户从屏幕标准输入了。
1.2
The chomp Operator
它的作用简单说就是作用于一个变量(输入的值),把结束符(newline)去掉,
这一步在以后的编程中很有用的,上面的例子,我们一般情况下这样做:
chomp($text =
如果你以这样的方式写出这样的程序,那么就没有换行输出了,因为它自动将换行符去掉了。
1.3 in short,chomp也是一个简单的函数,也有返回值,他的返回值就是清除字符的个数,但是这种用法和我们上面不太一样,是这样
$yubo = <STDIN>;
$NAME = chomp $yubo;
输出的结果是1.
1.4 The while control construction 同c语言的一样,很简单的。
1.5使用defined function
$yubo = <STDIN>;
if ( defined($yubo) ) {
print "The input was $yubo\n";}
else { print "No input available\n";}
1.
$fred[0] = "yubo"; print $fred[0]; #perl的数组没有限制,随便用
2.列表
(1,2,3) ("yubo",4.5) (1..100)
2.2 The qw Shortcut
qw( fred yubo hechun wilma dino )# 等同于有双引号的列表
qw! yahoo\! google ask msn ! #include yahho! as an element
2.3 List Assignment
3.在给数组赋值的时候,可以在数组前面加上@,这样就全部赋值了。例如,
@rocks = qw/ bedrock slate lava /;
@tiny = ();# the empty list
@giant = 1..1e5; # a list with 100000 elements
The pop operator takes the last element off of an array and returns it @array = 5..9;
$yubo = pop(@array); #$yubo gets 9,@array now has (5,6,7,8)
$barney = pop @array;#barney gets 8,@array now has (5, 6, 7)
push(@array,0) # @array now has (5,6,0)
push @array,8 # @array now has (5,6,0,8)
push @array,1..10; # @array now has those 10 new elements
@yubo = qw# hechun wuhan wangyang #;
$m = shift (@yubo);
$n = shift @yubo;
unshift play an oppose play vs shift,and it used to be filled with elements.
unshift(@yubo, 5);
unshift @yubo, 5;
6.分离数组
1.splice就是分离数组的符号,有4个参数,其中
@yubo = qw(apple, dino, hechun, love);
@removed = splice @yubo, 2; #remove everything after hechun
#@removed is qw(hechun,love)
#@yubo is qw(apple, dino)
2.第三个参数就是要删除元素的个数
@removed = splice @yubo, 1, 2; #@removed is qw(dino, hechun)
#@yubo is qw(apple, love)
3.这第四个参数就是你放置要取代的元素,这个数不必和你取出去的相同。
@removed = splice @yubo, 1, 2, qw(wilma);
当然,你可以不用去掉任何元素,只要使第三个参数变0即可。
7.@和$ 当变量中含有@(list-var)时要注意使用”"转义,还有注意数组的使用。
8.foreach 一个loop的控制变量,一个普遍的用法是用$var读取list的内容,如下:
@yubo = qw(yu hechun hehe); foreach $num (@yubo){ print "$num\m"; }
注意这时的$num不是简单的@yubo的元素的复制,而是实实在在的东西, 你只要改变$num的内容,也能影响@yubo的内容。我们可以使用$_作为foreach的 默认控制变量,打印语句可以直接使用print;
以反序输出内容,如果是单词,按首字母排序。
如果是单词,按首字母排序;如果是数字,也是按照首位的大小排序。
以上几个内建函数的对象是list(我觉得),使用的时候注意, scalar和list的混用的情况,注意内容与类型的对应要正确。 作者说了,这部分的理解决定了你这一辈子是否使用perl的关键因素, 没有之一。
sub subroutine-name{ }
stores the parameter list in the special array named @_, you can access it to get number of argument and the value of those arguments. For example,$_[0],$_[1]…
在函数内部使用 @_
就是参数个数
sub max{ if (@_ != 2){ print "wrong\n"; } else { print "yes\n"; } } $n = &max(19,23);
下面的例子,把函数的规则总结的不错
$maxinum = &max(3, 5, 10, 4, 6); sub max { my($max_so_far) = shift @_; # private variable foreach (@_){ #look at the remaining arguments if ($_ > $max_so_far){ $max_so_far = $_; } } $max_so_far; } print "The maxinum is $maxinum\n";
my(var)也叫Lexical(my) Variables,这个变量位于最小的块中,可以是代码块 或者文件。利用关键词my去声明局部变量。
foreach (1..10){
my($square) = $_ * $_;
print "$_ square is $square\n";
}
在代码中你只要使用了这条语句,在使用变量的时候就必须先声明后使用, 不然编译器就报告失败.当然对于内建变量是不会报错的, please use it possiblely. 此时感觉perl真是非同一般,你只要使用了strict,那么程序中的变量就要使用 my去声明。
use strict;
my @name = qw(yubo hechun xixi qianqian);
my $result = &which_element_is("xixi",@name);
sub which_element_is {
my($what, @array) = @_;
foreach (0..$#array){
if ($what eq $array[$_]){
return $_;
}
}
-1;
}
What is state?Please see example follwing:
#!/use/bin/perl
use strict;
sub marine {
my $n += 1;
print "$n\n";
}
foreach (1..5) {
&marine;
}
output:五个1。原因在这里很简单,就是my把变量局限于子函数内, 请不疑惑,尽管我们的$n 在sub marine内,但是,在foreach的调用中, 整体已经过去了,所以只会输出五个1(??)
典型例子
while (defined($line = <STDIN>)){
print "I saw $line";
}
同样的表达可以是
while (<STDIN>){
print "I saw $_";
}
foreach (<STDIN>){
print "I saw $_";
}
# Ctrl + D
Reading variables from input,
while (<>) {
chmop;
print "It was $_ that i saw";
}
名字最好使用大写字母,这样有好几个优势。
open CONFIG, 'dino'; # dino is filename
open config, '<dino';
open BEDROCK,'>fred'
you can use a “three-argument” open:
open CONFIG, '<', 'dino';
open BEDROCK, '>', '$file_name';
open CONFIG,'<:encoding(UTF-8)', 'dino';
open LOG, '>>:encoding(UTF-8)', &logfile_name();
if (! open LOG, '>>', 'logfile') {
die "Cannot create logfile: $!"
}
这个判断语句就是判断是否能创建logfile文件,不能的话返回一个数值, 这个值就是不能的含义,在这里,可以把die和pirnt联系起来,共同报错。
Recently i thought sth deeply,English is my shortcut to learn linux kernel and others, and lack of indpendent of thinking.
use strict;
use warnings;
open (my $yubo, '>', 'log');
print $yubo "My first report generated by perl\n";
close $yubo;
print "done\n";
这就是一个典型的文件句柄。open函数可以传入3个参数。
第一个, $yubo,是在 open()调用中定义的标量,第二个参数是读写文件的方式,第三个是文件路径。
文件的内容仍在磁盘上,并不在$yubo变量中,可能你往句柄中写入内容, 东西就会在文件中保存。
这个$yubo就可以类推c语言中的文件描述符(fd),有意思吧!
open(my $yubo, ‘>’, ‘no_exist/file’) or die;
下面这种用法结合前面几种的方法
my $filename = 'No_exist_file';
open(my $yubo, '>', $filename) or die "could not open $filename $!";
# return error information
当时偷懒了,只是简单的记录下来,并不知道什么意思。现在再重新整理整理。 顾名思义,@ARGV就是一个参数,这个参数接受外部变量,这里,这个变量就是某个路径上的文件,将该文件上的内容输出。
@ARGV = ("file1","file2");
while ($line = <>) {
print "$line\n";
}
上面的东西,一看就是菜鸟写的,应该简单一点是这样
@ARGV = ("file1");
while (<>) {
print "$_";
}
For example:
$family_name{'yubo'} = 'flintstone';
$family_name{'hechun'} = 'love';
foreach my $person (qw <yubo hechun>) {
print "I've heard of $person $family_name{$person}.\n";
}
my %new_hash = %old_hash;
my %last_name = (
'Yu' => 'bo',
'He' => 'chun',
);
foreach my $first (qw <Yu He>) {
print "$first in @last_name{$first}.\n";
}
上面的hash赋值完后还可以使用 $hash{“d”} = 1; 的方式解决。 还有一种方式可以赋值:
my %last_name;
$last_name{"fred"} = "hehe";
$last_name{"yubo"} = "bo";
$last_name{"he"} = "chun";
注意,赋值的时候使用%,使用的时候却是$
下面这句话改变了 key 和 value 的位置。
my %inverse_hash = reverse %any_hash;
my %hash = ('a' => 1, 'b' => 2, 'c' => 3);
my @k = keys %hash;
my @v = values %hash;
foreach (@k) {
print "$_\n";
}
这样就会打印hash的key,但是有一点请注意,perl不保证输出的顺序, 也就是你最好不要期望得到你想要的顺序。又但是,keys 和 values函数 可以保证此时的值相对应,例如,上面的例子@k会打印出c b a,那么打印 的@v一定是3 1 2
There is another function:
my $count = keys %hash;
$count的值就是hash函数中的对数。
while ( ($key, $value) = each %hash) {
print "$key => $value\n";
}
After sorted hash
foreach $key (sort keys %hash) {
$value = $hash{$key};
print "$key => $value\n";
}
The exists Function,to see whether a key exists in the hash, use the exists function,if true
以上面的代码为例:
if (exists $hash{"a"}) {
print "Hey, there exist\n";
}
if (!exists $hash{"d"}) {
print "NO exist!\n";
}
不知道我的思路是否有问题,$hash,@hash,%hash三者之间有着某种联系, 虽说hash是用来定义的,但它仅仅是定义吗?在其他的外部调用中,我们 反而使用的是$hash{“”},在当时学习@hash和$hash也有同样的问题。
print "PATH is $ENV{path}\n";
write a perl file, output the times of that have read from terminal. For example, input “yubo,hechun,yubo,hechun” output the numbers”yubo 2…hechun 2”
my(@words, %count, $word); # declare
chomp(@words = <STDIN>); # input
foreach $word (@words) {
$count{$word} += 1;
}
foreach $word (keys %count) {
print "$word was seen $count{$word} time . \n";
}
自己从这个程序中稍微理解了hash的原理,其中最后一个比较有意思, 在count hash中$word作为keys,so the value is $count{$word}.
下面的例子与上面的很类似
$longest = 0;
foreach my $key (keys %ENV) {
my $key_long = length($key);
$longest = $key_long if $key_long > $longest;
}
foreach my $key (sort keys %ENV) {
printf "%-s{longest} %s \n.$key, $ENV{$key}";
}
#Debugging Techniques 无论什么样的原因你都应该自己编译内核,在__kernel hacking__ menu 下,你最好开启以下选项. ###CONFIG_DEBUG_KERNEL the option just makes other debugging options available. ###CONFIG_DEBUG_SLAB 这对于内核内存分配函数非常关键, 它可以推断溢出的内存的数量以及未初始化的错误,每一个被分配的内存的字节在处理之前是0xa5,当被释放后是0x6b… ###CONFIG_DEBUG_PAGEALLOC Full pages are removed from the kernel address space,the option can slow it,also point out certain kinds of memory corruption errors ###CONFIG_DEBUG_SPINLOCK It catches uninitialized spinlock and other errors(such as unlocking a lock twice)
###CONFIG_DEBUG_SPINLOCK_SLEEP 开启这个选项,如果你呼叫一个潜在(可能不是)的sleep函数,它就会发出警告. ###CONFIG_INIT_DEBUG Items marked with init(__initdata)在系统初始化完成后或模块加载完毕后被丢弃. ###CONFIG_DEBUG_INFO The information debugging with __gdb,You also want to enable CONFIG_FRAME_POINTER. ###CONFIG_MAGIC_SYSRQ
###CONFIG_DEBUG_STACKOVERFLOW AND CONFIG_DEBUG_STACK_USAGE These options can help trace down kernel stack overflows. ###CONFIG_KALLSYMS This option (under “General setup/Stardsrd features”) causes kernel symbol information to be built into the kernel. ###CONFIG_IKCONFIG AND CONFIG_IKCONFIG_PROC These options (under “General setup”) cause the full kernel configuration state and to be made available via /proc. ###CONFIG_ACPI_DEBUG
###CONFIG_DEBUG_DRIVER It is useful to trace down the low-level support code. ###CONFIG_SCSI_CONSTANTS The item (under “Device drivers/SCSI device support”) ###CONFIG_INPUT_EVBUG The item (“Device drivers/Input device suuport”) including security implications and however: it logs everything you type. ###CONFIG_PROFILING
#Debugging by Printing The variable console_loglevel is initialized to DEFAULT_CONSOLE_LOGLEVEL AND can be modified through the sys_syslog system call,P95 这一段没看明白. ###Redirecting Console Messages 同样没有看懂 ###How Messages Get Logged The printk function writes messages into a circular buffer that is __LOG_BUF_LEN bytes long (from 4 KB to 1 MB) while configuring the kernel.
###系统日志那块很麻烦 ###Rate Limit 如果你的设备有错误,但是你只是想打印一次的错误,那可以考虑
int printk_ratelimit(void);
如果这个函数返回一个非0的数,那么继续打印消息,典型的应用是
if (printk_ratelimit())
printk(KERN_NOTICE "The printer is still on fire\n");
这个函数的行为可以通过 /proc/sys/kernel/printk_ratelimit改变. ###print device numbers Defined in <linux/kdev_t.h>
int print_dev_t(char *buffer, dev_t dev);
char *format_dev_t(char *buffer, dev_t dev);
这一块学的真垃圾.
###Using the /proc/ Filesystem
这个目录下的文件是内核把硬件信息告诉外界./proc是很重要在linux system中,许多命令例如ps,top,uptime严重依赖这个目录.你的驱动也可以使用这个东西,并且是动态的. 我们最好使用 sysfs去调试信息.
<linux/proc_fs.h>是所有工作在/proc的模块的头文件,当有进程访问/proc下的文件,内核首先分配一页内存(4096 kb),此内存用来将数据发往用户空间,
int (*read_proc)(char *page, char **start, off_t offset, int count,
int *eof, void *data)
其中的offset和count同read方法的一致,其他的就可以按照字面上的理解.这个函数的参数需要注意.
下面是一个简单的例子:
int scull_read_procmen(char *buf, char **start, off_t offset,
int count, int *eof, void *data)
{
int i, j, len = 0;
int limit = count -80;
for (i = 0; i < scull_nr_devs && len <= limit; i++) {
struct scull_dev *d = &scull_devices[i];
struct scull_qset *qs = d->data;
if (down_interruptible(&d->sem))
return -ERESTARTSYS;
len += sprintf(buf + len,"\nDevice %i: qset %i, q %i, sz %li\n",
i, d->qset, d->quantum, d->size);
for (;qs && len <= limit; qs = qs->next) {
len += sprintf(buf + len, " item at %p, qset at %p\n",
qs, qs->data);
if (qs->data && !qs->next)/*dump only last item*/
for (qs->data[j])
len += sprintf(buf + len,
" % 4i:%8p\n",
j, qs->data[j]);
}
up(&scull_devices[i].sem);
}
*eof = 1;
return len;
}
##kdb内核调试器
1.在编译内核时,首先将支持kdb的选项打开,在 Linux hacking » KGDB
2.在控制台下,按下 Pause Break 键启动调试,当内核发生oops,或者到达一个断点时也会开启调试。
3.具体用到的时候再说 ##并发和竞争
1.当一个linux process reaches a point where it cannot make any further process it goes to sleep (or “block”)
2.使用锁机制容易造成“sleep”,所以使用信号量来避免,在linux中,有一对函数叫做P,V。在进入临界区前,首先呼叫p,并且将value-1,V 释放这个信号量。当这两个函数出现意外时,需要等待…
3.实现
为了使用semaphore,需要包括<asm/semaphore>
struct semaphore;
void sem_init(struct semaphore *sem, int val);
// val is the inital value to assign to a semaphore.
还可以使用MACROS
DECLARE_MUTEX(NAME); // ==>> 1
DECLARE_MUTEX_LOCKED(NAME); // ==>> 0
如果在运行时进行初始化,可以使用以下两种方法
void init_MUTEX(struct semaphore *sem);
void init_MUTEX_LOCKED(struct semaphore *sem);
使用:
void down (struct semaphore *sem);
void down_interruptible(struct semaphore *sem);
//产生不可杀的进程(D state in PS command)
int down_trylock(struct semaphore *sem);
//立即返回nonzero
void up(struct semaphore *sem);
使用信号量通过down获得,也就是说在你的目标代码中使用上述的的down xx(),可以获得这个信号量;当你离开这个临界区时,通过up()释放。
首先我们在设备文件中设置
struct semaphore sem;
init_MUTEX(&scull_devices[i].sem);
//一定要在设备分配之前使用
up(&dev->sem);
//must clean up
###Read/write semaphore 如果只是仅仅访问收保护的临界区,我们可以使用rwsem(“reader/writer semaphore”), it comes from <linux/rwsem.h>.
void down_read(struct rw_semaphore *sem);
int down_read_trylock(struct rw_semaphore * sem);
void up_read(struct rw_semaphore *sem);
down_read只是使用可读的权限使用被保护的数据,还可以是并发的。它可以把呼叫程序处于一种不可中断的睡眠。down_read_trylock如果成功的话返回非0,不成功返回0,这一点与其它的内核函数一样。一个 rwsem使用down_read到的,必须使用up_read释放。
for writer:
void down_write(struct rw_semaphore *sem);
int down_write_trylock(struct rw_semaphore *sem);
void up_write(struct rw_semaphore *sem);
void downgrade_write(struct rw_semaphore *sem);
综合rwsem的表现,我们拥有rwsem的时间不能过长,否则会出问题. ###completion 即便使用信号量我们也不能保障临界区的安全使用,in the 2.4.7 kernel,we can use completion that allow one thread to tell another that job is done. <linux/completion.h>
DECLARE_COMPLETION(my_completion);
如果动态的使用,则
struct completion my_completion;
/*...*/
init_completion(&my_completion); wait.
void wait_for_completion(strcut completion *c);
这个函数是不可中断的,如果使用后不及时释放,会产生不可杀的进程.