Linux系统下.ko文件是什么文件?.so文件是什么文件... 5
我有一个文件abc.txt,我想用bunzip2压缩工具进行压缩!... 5
insmod(installmodule)5
记mount NFS遇到的一个问题(-o nolock)6
关于C语言结构体赋值(LINUX内核风格). 6
sprintf格式... 8
sprintf格式... 8
标识符... 9
宽度... 10
精度... 11
指示符... 11
指定参数... 14
注释问题... 15
解决 multiple definitionof15
Fopen函数简介... 16
strtok. 17
编辑本段原型... 17
编辑本段功能... 17
编辑本段说明... 18
编辑本段返回值... 18
编辑本段使用... 18
strncmp用法... 18
fwrite. 18
编辑本段函数名... 18
编辑本段功能... 18
编辑本段用法... 19
编辑本段程序示例... 19
#pragma pack. 21
编辑本段对齐方式... 21
gcc 错误搜集1. 22
如何解决warning:no newline at end of file?. 22
结构体初始化赋值={0},GCC打开-Wall选项编译会警告,大家探讨一下... 22
windows 如何查看端口占用情况?. 23
memmove、memcpy和memccpy简介... 24
流控制... 25
字符串操作函数... 26
使用pthread_mutex_t锁的例子... 27
linux下select 和 poll的用法... 29
ioctl. 30
套接口操作:... 33
文件操作:... 33
编辑本段定义... 33
编辑本段必要性... 34
编辑本段实现操作... 34
编辑本段其他信息... 35
Mkdir函数... 35
信号量sem_wait sem_post36
Linux下开启/关闭防火墙命令... 39
Linux下配置ip、子网掩码、网关,并把他们保存在指定的文件中,每次启动后不用重新设置。 40
Linux的关机与重启命令... 40
安装RPM包或者安装源码包... 41
Linux rpm 命令参数使用详解[介绍和应用]... 53
GCC警告选项例解... 62
Csocket基本原理... 74
从问题看本质:socket到底是什么?... 78
半双工通信... 83
MFC打开/保存文件对话框:CFileDialog. 83
setsockopt的各种使用... 84
使用CFile类对文件进行读写... 86
MFC同步类... 90
同步对象的适用场合... 90
等待类CSingleLock. 90
TTS. 92
-qws命令... 92
ultraedit 自动缩进修改... 94
typedef 函数指针的用法... 95
【转】(转)MFC中TRACE的用法... 99
linux c语言定时器... 100
Linux下查看文件和文件夹大小的df和du命令... 105
linux 查看文件属性命令... 107
pthread_attr_init线程属性... 107
1.线程属性... 107
2、线程的分离状态... 108
3、线程的继承性... 110
4、线程的调度策略... 110
5、线程的调度参数... 111
vsnprintf112
目 录... 112
1函数简介... 113
2用法实例... 113
SOCKADDR_IN.. 114
目 录... 114
1基本结构... 115
2参数说明... 115
AF_INET和PF_INET的细微不同... 117
popen. 117
pthread_cond_signal和pthread_cond_wait简介... 119
linux 下route命令... 125
UNIX环境高级编程读书笔记(十一)—终端IO (3)... 128
select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET. 132
ioctl 函数... 136
(C语言)共用体union的用法举例... 140
Linux下getsockopt/setsockopt 函数说明... 143
SVN多版本库配置问题... 146
windows 下本机配置svn以及多版本库的创建... 147
【C/C++】Linux下使用system()函数一定要谨慎... 151
fork()函数 UNIX.. 154
头文件:... 154
函数原型:... 154
函数说明:... 155
为什么fork会返回两次?... 155
Linux下/proc目录简介... 156
shell的条件分支语句:... 162
shell判断文件file存在:... 163
在 /dev 中创建设备... 163
6.8.1. 创建初始设备节点... 163
6.8.2. 挂载ramfs 并填充/dev 目录... 163
linux 目录树... 164
虚拟内存盘... 173
CramFS 文件系统的制作... 174
【摘】编程质量--内存移动函数... 176
vim与复制,删除,粘贴,块操作以及快速替换功能... 180
先谈一下基于块的复制,删除,粘贴操作... 181
使用块选的好处... 181
批量替换列块... 181
与移动相关... 182
与复制相关... 183
复原以及重做操作... 183
替换模式... 183
移动光标... 183
在一行内移动光标... 184
插入文本... 184
删除和移动文本... 185
修改文本... 185
复制文本... 186
如何画程序流程图... 186
10个比viso好的流程图制作软件... 193
Linuxpthread_mutex_init()函数... 194
条件变量、pthread_cond_init195
1.初始化条件变量pthread_cond_init195
2.阻塞在条件变量上pthread_cond_wait196
3.解除在条件变量上的阻塞pthread_cond_signal196
4.阻塞直到指定时间pthread_cond_timedwait197
5.释放阻塞的所有线程pthread_cond_broadcast197
6.释放条件变量pthread_cond_destroy. 197
7.唤醒丢失问题... 198
#ifdef __cplusplus 有什么作用... 198
Linux系统中的poll函数... 207
ubuntu下配置vim.. 210
VIM查找替换归纳总结zz. 211
Linux下转换字符集(UTF8转换)(转)... 212
pthread_attr_setdetachstate. 216
pthread_attr_init线程属性... 217
Linux信号量线程控制... 223
C语言中printf格式化输出函数... 226
例解GNUC之typeof232
预编译语句中#与##的作用... 234
Linux系统下.ko文件是什么文件?.so文件是什么文件?
A:.ko(kernel object),内核模块,可以在linux内核起来之后动态的加载和卸载
.so(shared object)用户层的动态库与(.a对应),使用同一个.so的程序在运行只需要该.so的一份拷贝
我有一个文件abc.txt,我想用bunzip2压缩工具进行压缩
#bzip2 abc.txt
注释:压缩后会得到一个压缩文件abc.txt.bz2,同时原abc.txt会被删除。(这点很重要哦,linux考试会问到这一点)
如果有一个文件abc.txt.bz2,想解压缩:
#bunzip2 abc.txt.bz2
注释:解压后会得到abc.txt,而原abc.txt.bz2会被删除。
功能说明:载入模块 install loadable kernel module
语法:insmod [-fkmpsvxX][-o<模块名称>][模块文件][符号名称= 符号值]
参数:
-f 不检查目前kernel版本与模块编译时的kernel版本是否一致,强制将模块载入。
-k 将模块设置为自动卸除。
-m 输出模块的载入信息。
-o <模块名称> 指定模块的名称,可使用模块文件的文件名。
-p 测试模块是否能正确地载入kernel。
-s 将所有信息记录在系统记录文件中。
-v 执行时显示详细的信息。
-x 不要汇出模块的外部符号。
-X 汇出模块所有的外部符号,此为预设置。
使用说明:Linux有许多功能是通过模块的方式,在需要时才载入kernel。如此可使kernel较为精简,进而提高效率,以及保有较大的弹性。这类可载入的模块,通常是设备驱动程序。
insmod命令主要用于在Linux 2.4内核之前加载Linux内核模块命令。对于通过此命令加载的Linux内核模块,系统不仅不会自动解决内核模块之间的依赖关系,而且还要求将模块路径写详细。所以在Linux 2.6内核出现时,此命令已渐渐被遗忘。
加载RAID1阵列级别模块,如下所示:
[root@rhel5 boot]# insmod /lib/modules/2.6.
18-8.el5/kernel/drivers/md/raid1.ko
[root@rhel5 boot]# lsmod |grep raid1
raid1 25153 0
从以上显示结果可知,RAID1模块已加载成功。只是在使用insmod命令加载模块时,需要使用绝对路径方能加载,且加载时无法自动解决依赖关系。
前两天测试过程中,测试机始终mount不上我们的nfs。
比如我使用命令:mount vt-nfs:/share /mnt/share
开始是mount命令一直hang在那里(卡住了);另一种情况是,有类似如下的错误输出:
portmap: server localhost not responding, timed out
RPC: failed to contact portmap (errno -5).
lockd_up: makesock failed, error=-5
经过找了很久才发现了解决方案:
nfs mount 默认选项包括文件锁,依赖于portmap提供的动态端口分配功能;
简单的解决方法:kill 文件锁(lockd)或者mount -o nolock
nolock这个选项是针对NFS所特有的:Disable NFS locking. Do not start lockd. This hasto be used with some old NFS servers that don't support locking.
命令改为:mount -o nolock my-nfs:/share /mnt/share
这样就可以正常工作了。
BTW:
mount iso文件常用命令 mount -o loop disk1.iso /mnt/disk
mount一个本地的目录 mount --bind ./dir1 ./dir2
参考:
http://blog.chinaunix.net/space.php?uid=24499&do=blog&cuid=480784
http://linux.die.net/man/8/mount
http://linux.die.net/man/5/nfs
http://helloxchen.itpub.net/post/42725/508908
作者helloxchen 16:53 | 静态链接网址 | 最新回复(0) | 引用(1) | Linux_C
1 对成员赋值.
例如结构体struct st1 {
int a;
int b;
int c;
}
1.1 用{}形式.
struct st1 st1 = {1,2,3);
1.2 linux kernel风格.
struct st1 st1 = {
.a = 1;
.b = 2;
};
//注此风格(即在成员变量之前加点“.”),可以不按成员变量的顺序进行赋值。如可以为
struct st1 st1 = {
.c = 3;
.a = 1;
.b = 2;
};
2 对整体赋值.
struct st1 a,b;
b = a;
3 结构体作为函数返回值对另一个结构体赋值.
struct st1 func1();
struct st1 a= func1();
举例:
[ctest]# vi t.c
#include
struct st1 {
int e1;
int e2;
};
struct st1 func1()
{
struct st1 h = { 77, 88};
return h;
}
int main()
{
struct st1 a= { 33, 44};
struct st1 b = {
.e1 = 55,
};
struct st1 c;
struct st1 d;
c = a;
d = func1();
printf("e1 e2 is %d %dn", a.e1, a.e2);
printf("e1 e2 is %d %dn", b.e1, b.e2);
printf("e1 e2 is %d %dn", c.e1, c.e2);
printf("e1 e2 is %d %dn", d.e1, d.e2);
return 0;
}
"t.c" 29L, 420Cwritten
[ctest]# gcc -o a t.c
[ctest]# ./a
e1 e2 is 33 44
e1 e2 is 55 0
e1 e2 is 33 44
e1 e2 is 77 88
http://blog.163.com/a3563@126/blog/static/54675706200710134410126/
Ruby的sprintf格式与C语言的sprintf(3)基本相同。但还是有些差别: 它没有针对C特有类型的修饰符,如short或long等; 它包含2进制数的指示符(%b); 它不支持sprintf的方言式的语法。
下面就对ruby的sprintf格式进行详细的说明。
sprintf格式的规格如下所示。[]
中的部分是可选的。
%[指定参数$][标识符][宽度][.精度]指示符
若想输出`%
'本身时, 请这样`%%
'处理。
下面就分别介绍一下各元素的用法。
标识符包括`#', `+', ` '(空格), `-'和`0'这5个。
#
使用2进制、8进制、16进制的指示符(`b', `o', `x', `X')时, 会分别添加"0b","0", "0x", "0X"前缀。
p sprintf("%#b", 10) # => "0b1010"
p sprintf("%#o", 10) # => "012"
p sprintf("%#x", 10) # => "0xa"
p sprintf("%#X", 10) # => "0XA"
对于浮点数 (`f', `e', `E', `g', `G'), 则必定在输出中添加"."。
p sprintf("%.0f", 10) # => "10"
p sprintf("%#.0f", 10) # => "10."
p sprintf("%.0e", 10) # => "1e+01"
p sprintf("%#.0e", 10) # => "1.e+01"
`g',`G'除了具有上述特性外, 还会在末尾添加多余的0。
p sprintf("%.05g", 10) # => "10"
p sprintf("%#.05g", 10) # => "10.000"
+
使输出字符串带上符号。如果是正数的话, 就会添加`+'。它只对数值指示符(`d', `i', `b', `o', `x', `X', `u', `f', `e', `E', `g', `G')起作用。另外, 如果是`b', `o', `x', `X', `u'的话, 则会为负数添加`-'。
p sprintf("%d", 1) # => "1"
p sprintf("%+d", 1) # => "+1"
p sprintf("%x", -1) # => "..f" # ".." 表示f无限延续
p sprintf("%+x", -1) # => "-1"
' '(空格)
与`+'相同, 用空格来代替正号`+'。它只对数值指示符(`d', `i', `b', `o', `x',`X', `u', `f', `e', `E', `g', `G')起作用。
p sprintf("%d", 1) # => "1"
p sprintf("%+d", 1) # => "+1"
p sprintf("% d", 1) # => " 1"
p sprintf("%x", -1) # => "..f"
p sprintf("% x", 1) # => " 1"
p sprintf("% x", -1) # => "-1"
-
使输出内容靠左. 若尚未指定宽度的话,则不起作用。
0
当输出内容靠右时, 使用`0'而并非空格来填充多余部分。
它只对数值指示符(`d', `i', `b', `o', `x', `X', `u', `f', `g', `G')起作用(对`e', `E'无效)
p sprintf("%010d", 10)
# => "0000000010"
与`#'一起使用时, 输出情况如下。
p sprintf("%#010x", 10) # => "0x0000000a"
p sprintf("%#010o", 10) # => "0000000012"
p sprintf("%#010b", 10) # => "0b00001010"
它等同于下例。
p sprintf("%#10.8x", 10) # => "0x0000000a"
p sprintf("%#10.9o", 10) # => "0000000012"
p sprintf("%#10.8b", 10) # => "0b00001010"
通常情况下, 会输出如下内容。
p sprintf("%#10x", 10) # => " 0xa"
p sprintf("%#10o", 10) # => " 012"
p sprintf("%#10b", 10) # => " 0b1010"
以非0数字开头的数串负责指定宽度。宽度是指生成字符串的宽度, 它不受后文中的精度的限制。
确定宽度时, 也会考虑标识符中附加的" ","+","-", "0b", "0", "0x","0X"的长度。
p sprintf("%#05x", 10) # => "0x00a"
宽度是指"必要的最小宽度". 若结果字符串的宽度超过指定宽度时, 指定宽度就会失效。
若将宽度指定为`*'时, 将从参数中取得宽度值。
p sprintf("%10s", "foo") # => " foo"
p sprintf("%*s", 10, "foo") # => " foo"
紧跟在"."后面的数串表示精度(若只有"."的话,则为".0")。若遇到整数的指示符(`d', `i', `b', `o', `x',`X', `u')的话,精度表示数值部分的长度。
p sprintf("%10.5d", 1) # => " 00001"
p sprintf("%#10.5x", 1) # => " 0x00001"
p sprintf("%+10.5x", 1) # => " +00001"
若遇到浮点数的指示符(`f')的话,它表示小数部分的位数。
p sprintf("%10.5f", 1) # => " 1.00000"
p sprintf("%10.5f", 10) # => " 10.00000"
若遇到浮点数的指示符(`e', `E', `g', `G')的话,它表示有效位数。
p sprintf("%10.5e", 1) # => "1.00000e+00"
p sprintf("%10.5e", 10) # => "1.00000e+01"
p sprintf("%10.5g", 10) # => " 10"
p sprintf("%#10.5G", 10) # => " 10.000"
如果是字符串指示符(`s', `p')的话,将会按照精度的规定来检查参数中的字符串长度,并切除多余部分。若将宽度和精度设为同值的话,则只输出参数字符串中的符合精度规定的部分。
p sprintf("%10.2s", "foo") # => " fo"
p sprintf("%5.5s", "foo") # => # => " foo"
p sprintf("%5.5s", "foobar") # => # => "fooba"
若将精度设为`*'的话,将从参数中提取精度的值。
p sprintf("%.5s", "foobar") # => "fooba"
p sprintf("%.*s", 5, "foobar") # => "fooba"
指示符指出参数的类型,且是必选的。大体说来它包括:
· 表示字符串的指示符: `c', `s', `p'
· 表示整数的指示符: `d', `i', `u', `b', `o', `x', `X',
· 表示浮点数的指示符: `f', `g', `e', `E', `G'
这几类。
c
将参数的数值(0×255)看作是字符代码,并输出对应的字符。若参数并非数值、String、 nil,true或false的话,将尝试用to_int方法进行变换。
此时,只有标识符`-'和"宽度"的设定是有效的。
s
输出字符串。
若参数并非String对象的话,将使用to_s方法对其进行变换。
p
ruby 1.8 特性: 输出Object#inspect的结果。
p sprintf("%s", [1, 2, 3]) # => "123"
p sprintf("%p", [1, 2, 3]) # => "[1, 2, 3]"
d
i
以10进制整数的形式输出参数中的数值。
若参数并非整数,则使用与Integer函数相同的规则将其变为整数。
u
将参数的数值看作是无符号整数,并以10进制整数的形式输出它。
p sprintf("%u", -1) # => "..4294967295"
上面的代码会输出 p ".." + 0xffff_ffff.to_s。
ruby 1.7 特性: 在version 1.7中,不会附加".."。若是'%u'的话,则将参数看作是定长整数。此时,对于负整数n来说
printf("%u", n)
与
printf("%d", n & ~(-1 << n.size*8))
是一个意思。
b
o
x
X
分别以2进制、8进制、16进制、16进制(大写字母)字符串的形式输出整数。
若使用了`#' 标识符的话,则分别在前面添加"0b", "0", "0x", "0X"。
若没有使用`+', ` ' 标识符时,将在负数的前面(若有`#' 标识符,则在"0x"等的后面)添加".."。这表示最高位字符无限延伸,它采用了2的补数形式来表现负数。
p sprintf("%#b", 10) # => "0b1010"
p sprintf("%#o", 10) # => "012"
p sprintf("%#x", 10) # => "0xa"
# 对负数添加".."
p sprintf("%#b", -1) # => "0b..1"
p sprintf("%#o", -1) # => "0..7"
p sprintf("%#x", -1) # => "0x..f"
p sprintf("%10x", -1) # => " ..f"
p sprintf("%-10x", -1) # => "..f "
# 若指定了"精度"的话,则不会添加".."
p sprintf("%.10x", -1) # => "ffffffffff"
f
e
E
g
G
`f' 以小数点形式(xxx.xxx)输出数值。
`e' 以指数形式(x.xxxe+xx)输出数值。
`g' 的情况比较特殊。当指数小于-4或者超出精度范围时,它采用`e'方式进行输出。除此之外,它采用`f'方式进行输出。另外,它会删除小数部分尾部的0。
大写字母指示符(`E', `G')会将输出中的字母变为大写形式。
p sprintf("%f", 1.0) # => "1.000000"
p sprintf("%e", 1.0) # => "1.000000e+00"
p sprintf("%g", 1.0) # => "1"
p sprintf("%f", 10.1) # => "10.100000"
p sprintf("%e", 10.1) # => "1.010000e+01"
p sprintf("%g", 10.1) # => "10.1"
p sprintf("%g", 10 ** 6) # => "1e+06"
p sprintf("%g", 10 ** -5) # => "1e-05"
精度的缺省值为6。
若遇到无限大值或NaN(Not a Number)时,输出情况如下。
p sprintf("%f", 1.0/0) # => "inf"
p sprintf("%f", -1.0/0) # => "-inf"
p sprintf("%f", 0.0/0) # => "nan"
p sprintf("%E", 1.0/0) # => "INF"
p sprintf("%E", -1.0/0) # => "-INF"
p sprintf("%E", 0.0/0) # => "NAN"
这部分的利用频率最低,所以放在最后。
nth$
表示将使用第nth个参数进行格式化操作。
p sprintf("%1$d, %1$x, %1$o", 10)
=> "10, a, 12"
p sprintf("%3$d, %2$x, %1$o", 1, 2, 3)
=> "3, 2, 1"
若您不想改变参数的顺序而只想改变格式的话,也可以使用它。
case ENV['LC_TIME']
when /^ja_JP/
fmt = "%1$d年%2$d月%3$d日"
else
fmt = "%2$02d/%03$2d/%1$02d"
end
p sprintf(fmt, 1, 4, 22)
=> "04/22/01"
您也可以先插入"*",然后借用参数来设定"宽度"和"精度"的值。
p sprintf("%5.2f", 1); # => " 1.00"
p sprintf("%*.*f", 5, 2, 1); # => " 1.00"
p sprintf("%1$*2$.*3$f", 1, 5, 2); # => " 1.00
2009-06-24 10:27 157人阅读 评论(0) 收藏 举报
====================================================================
// Some comment
// /
// Some more comment
int main(int, char **) { return 0; }
====================================================================
compiling with g++ will yield the following warning:
x.cc:2:2:warning: multi-line comment
What isthe point of this warning? Shouldn't the preprocessor
> > > just ignore everything between the // and the end-of-line?
> >
> > No, backslash-newline conversion happens before comments
> > are discarded. You really do have a multi-line comment;
> > one that would be dangerous if your next line weren't a
> > comment as well.
在//注释行的最后不能加 \ ,加 \ 表示下一行看成是注释,如果下一行不是注释的话会出错
总结了解决multipledefinition of的方法:
问题原因:
当多个文件包含同一个头文件时,并且你的.H里面没有加上条件编译
#ifndef TEST_H
#define TEST_H
#endif
就会独立的解释,然后生成每个文件生成独立的标示符。在编译器连接时,就会将工程中所有的符号整合在一起,由于,文件中有重名变量,于是就出现了重复定义的错误。
方法1:
给每一个头文件加上条件编译,避免该文件被多次引用时被多次解释,这是个应该是习惯。这个方法会解决大部分低级问题。
方法2:
当方法1无效时,可以把所有的全局变量放入一个头文件 global.h (名字随意起,但要加条件编译)中,每一个变量前面加extern,声明一下这些变量将在其它文件中定义。 然后建立一个和头文件名字对应的.cor .cpp文件 如global.c。在里面声明所有的全局变量。例如:void(*Handl_Display)();
然后,让涉及到全局变量的文件include ”global.h“。这样编译时,会先对global.c编译生成一个global.o ,然后再和其它文件的.o链接生成可执行文件。
方法3:
懒人方法,在所有的全局变量前加上static ,声明成静止变量。也能解决问题。
所有的方法都是网来的,O(∩_∩)O哈哈~
谢谢所有的提供方法的哥们~
函数fopen
函数功能:打开一个文件
函数原型:FILE* fopen(const char * path,const char * mode);
相关函数:open,fclose,fopen_s[1],_wfopen
所需库:<stdio.h>
返回值:文件顺利打开后,指向该流的文件指针就会被返回。如果文件打开失败则返回NULL,并把错误代码存在errno 中。
一般而言,打开文件后会作一些文件读取或写入的动作,若打开文件失败,接下来的读写动作也无法顺利进行,所以一般在fopen()后作错误判断及处理。
参数说明:
参数path字符串包含欲打开的文件路径及文件名,参数mode字符串则代表着流形态。
mode有下列几种形态字符串:
r 以只读方式打开文件,该文件必须存在。
r+ 以可读写方式打开文件,该文件必须存在。
rb+ 读写打开一个二进制文件,允许读写数据。
rw+ 读写打开一个文本文件,允许读和写。
w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
w+ 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
a+ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 (原来的EOF符不保留)
wb 只写打开或新建一个二进制文件;只允许写数据。
wb+ 读写打开或建立一个二进制文件,允许读和写。
ab+ 读写打开一个二进制文件,允许读或在文件末追加数据。
at+ 打开一个叫string的文件,a表示append,就是说写入处理的时候是接着原来文件已有内容写入,不是从头写入覆盖掉,t表示打开文件的类型是文本文件,+号表示对文件既可以读也可以写。
上述的形态字符串都可以再加一个b字符,如rb、w+b或ab+等组合,加入b 字符用来告诉函数库以二进制模式打开文件。如果不加b,表示默认加了t,即rt,wt,其中t表示以文本模式打开文件。由fopen()所建立的新文件会具有S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH(0666)权限,此文件权限也会参考umask值。
有些C编译系统可能不完全提供所有这些功能,有的C版本不用"r+","w+","a+",而用"rw","wr","ar"等,读者注意所用系统的规定。
二进制和文本模式的区别
1.在windows系统中,文本模式下,文件以""代表换行。若以文本模式打开文件,并用fputs等函数写入换行符"\n"时,函数会自动在"\n"前面加上"\r"。即实际写入文件的是""。
2.在类Unix/Linux系统中文本模式下,文件以"\n"代表换行。所以Linux系统中在文本模式和二进制模式下并无区别。
目录
原型
功能
说明
返回值
使用
其他相关信息
展开
原型
功能
说明
返回值
使用
其他相关信息
展开
char *strtok(char s[], const char *delim);
分解字符串为一组字符串。s为要分解的字符串,delim为分隔符字符串。
例如:strtok("abc,def,ghi",","),最后可以分割成为abc defghi.尤其在点分十进制的IP中提取应用较多。
strtok()用来将字符串分割成一个个片段。参数s指向欲分割的字符串,参数delim则为分割字符串中包含的所有字符。当strtok()在参数s的字符串中发现参数delim中包涵的分割字符时,则会将该字符改为\0 字符。在第一次调用时,strtok()必需给予参数s字符串,往后的调用则将参数s设置成NULL。每次调用成功则返回指向被分割出片段的指针。
从s开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。
所有delim中包含的字符都会被滤掉,并将被滤掉的地方设为一处分割的节点。
strtok函数会破坏被分解字符串的完整,调用前和调用后的s已经不一样了。如果
要保持原字符串的完整,可以使用strchr和sscanf的组合等。
函数名:strncmp
功 能: 串比较
用 法: intstrncmp(char *str1, char *str2, int maxlen);
说明:此函数功能即比较字符串str1和str2的前maxlen个字符。如果前maxlen字节完全相等,返回值就=0;在前maxlen字节比较过程中,如果出现str1[n]与str2[n]不等,则返回(str1[n]-str2[n])。
目录
函数名
功能
用法
程序示例
fwrite
C语言函数,向文件写入一个数据块
size_t fwrite(const void* buffer, size_t size, size_t count, FILE*stream);
注意:这个函数以二进制形式对文件进行操作,不局限于文本文件
返回值:返回实际写入的数据块数目
(1)buffer:是一个指针,对fwrite来说,是要输出数据的地址;
(2)size:要写入内容的单字节数;
(3)count:要进行写入size字节的数据项的个数;
(4)stream:目标文件指针;
(5)返回实际写入的数据项个数count。
说明:写入到文件的哪里? 这个与文件的打开模式有关,如果是w+,则是从filepointer指向的地址开始写,替换掉之后的内容,文件的长度可以不变,stream的位置移动count个数;如果是a+,则从文件的末尾开始添加,文件长度加大。
fseek对此函数有作用,但是fwrite[1]函数写到用户空间缓冲区,并未同步到文件中,所以修改后要将内存与文件同步可以用fflush(FILE *fp)函数同步。
示例一:
#include
struct mystruct
{
int i;
char ch;
};
int main(void)
{
FILE *stream;
struct mystruct s;
if ((stream = fopen("TEST.$$$", "wb")) ==NULL) /* open file TEST.$$$ */
{
fprintf(stderr, "Cannot open output file.\n");
return 1;
}
s.i = 0;
s.ch = 'A';
fwrite(&s, sizeof(s), 1, stream); /* 写的struct文件*/
fclose(stream); /*关闭文件*/
return 0;
}
示例二:
#include
#define SIZE 1
typedef struct
{
char name[10];
int num;
int age;
char addr[15];
}student;
student stu[SIZE];
void save()
{
FILE *fp;
int i;
if((fp=fopen("dat.txt","w"))==NULL)
{
printf("无法打开此文件!\n");
return;
}
for(i=0;i if(fwrite(&stu[i], sizeof(student), 1, fp) != 1) printf("文件写入错误。!\n"); fclose(fp); } void main() { int i; for(i=0;i scanf("%s%d%d%s",&stu[i].name,&stu[i].num,&stu[i].age,&stu[i].addr); save(); } 示例三: /* fwrite example : write buffer */ #include int main () { FILE * pFile; char buffer[] = { 'x' , 'y' , 'z' }; pFile = fopen ( "myfile.bin" , "wb" ); fwrite (buffer , sizeof(buffer), 1 , pFile ); fclose (pFile); return 0; } //称为myfile.bin的一个文件被创建并存储到它的缓冲区的内容。为了简单起见,该缓冲区包含Char元素,但它可以包含任何其他类型。. sizeof(buffer)字节数组的长度(在这种情况下,它是三个,因为数组有三个元素,每次一个字节)。 示例四: //程序示例fwrite fread fseek FILE *fp; char msg[] = "file content"; char buf[20]; fp = fopen("d:\\a\\a.txt","w+"); if (NULL == fp) { printf("The file doesn't exist!\n"); return -1; } fwrite(msg,strlen(msg),1,fp);//把字符串内容写入到文件 fseek(fp,0,SEEK_SET);//定位文件指针到文件开始位置 fread(buf,strlen(msg),1,fp);//把文件内容读入到缓存 buf[strlen(msg)] = '\0';//删除缓存内多余的空间 printf("buf = %s\n",buf); printf("strlen(buf) = %d\n",strlen(buf)); 目录 对齐方式 对齐用法详解 程序编译器对结构的存储的特殊处理确实提高CPU存储变量的速度,但是有时候也带来了一些麻烦,我们也屏蔽掉变量默认的对齐方式,自己可以设定变量的对齐方式。 编译器中提供了#pragma pack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须为n的倍数。 下面举例说明其用法。 #pragma pack(push) //保存对齐状态 #pragma pack(4)//设定为4字节对齐 struct test { char m1; double m4; int m3; }; #pragma pack(pop)//恢复对齐状态 以上结构体的大小为16,下面分析其存储情况,首先为m1分配空间,其偏移量为0,满足我们自己设定的对齐方式(4字节对齐),m1大小为1个字节。接着开始为m4分配空间,这时其偏移量为4,需要补足3个字节,这样使偏移量满足为n=4的倍数(因为sizeof(double)大于4),m4占用8个字节。接着为m3分配空间,这时其偏移量为12,满足为4的倍数,m3占用4个字节。这时已经为所有成员变量分配了空间,共分配了16个字节,满足为n的倍数。如果把上面的#pragma pack(4)改为#pragma pack(8),那么我们可以得到结构的大小为24。 以下错误,都是在别的编译编译正确,而在GCC的错误。dev c++ 错误提示:48 D:\××.c [Warning] malformed'#pragma pack(push[, id], #pragma pack(push) struct tagPhysStruct #pragma pack(pop) 修改开头两行合并为一行。#pragmapack(push,1) 今天写了一段代码, 是在Windows下编辑的, 保存后放在linux系统下编译. gcc和cc都产生以下的警告: 后来发现解决这个问题产生的原因是源文件的最后一行没有回车符造成的; 解决的办法很简单, 在最后一行敲一个回车, 然后保存, 重新编译. 比如结构体的第一个成员变量是数组或者嵌套的小结构体,linux下编译时gcc打开-Wall选项时会报告警类似如下: 开始--运行--cmd进入命令提示符 输入netstat-ano 即可看到所有连接的PID 之后在任务管理器中找到这个PID所对应的程序如果任务管理器中没有PID这一项,可以在任务管理器中选"查看"-"选择列" C:\>netstat -ano 协议 本地地址 外部地址 状态 PID TCP 127.0.0.1:1434 0.0.0.0:0 LISTENING 3236 2.查看指定端口的占用情况 协议 本地地址 外部地址 状态 PID TCP 127.0.0.1:9050 0.0.0.0:0 LISTENING 2016 P: 看到了吗,端口被进程号为2016的进程占用,继续执行下面命令: (也可以去任务管理器中查看pid对应的进程) 3.查看PID对应的进程 映像名称 PID 会话名 会话# 内存使用 4.结束该进程 C:\>taskkill /f /t /im tor.exe memmove、memcpy和memccpy三个函数都是内存的拷贝,从一个缓冲区拷贝到另一个缓冲区。 串口的流控是指数据流。数据在两个串口之间传输时,常常会出现丢失数据的现象,或者两台计算机的处理速度不同,如台式机与单片机之间的通信,接收端数据缓冲区已满,则此时继续发送来的数据就会丢失。当接收端数据处理不过来时,就发出"不再接收"的信号,发送端就停止发送,直到收到“可以继续发送”的信号再发送数据。PC机中常用的两种流控制分别是硬件流控制(包括RTS/CTS、DTR/DSR等)和软件流控制XON/XOFF。 基于OSI七层模型的流控制的类型包括:Buffering(缓存)、Window(基于窗口)、Congestionavoidance(冲突避免)。 2.硬件流控制 硬件流控制常用的有RTS/CTS流控制和DTR/DSR(数据终端就绪/数据设置就绪)流控制。 硬件流控制必须将相应的电缆线连上,用RTS/CTS(请求发送/清除发送)流控制时,应将通讯两端的RTS、CTS线对应相连,数据终端设备(如计算机)使用RTS来起始调制解调器或其它数据通讯设备的数据流,而数据通讯设备(如调制解调器)则用CTS来起动和暂停来自计算机的数据流。这种硬件握手方式的过程为:我们在编程时根据接收端缓冲区大小设置一个高位标志(可为缓冲区大小的75%)和一个低位标志(可为缓冲区大小的25%),当缓冲区内数据量达到高位时,我们在接收端将CTS线置低电平(送逻辑0),当发送端的程序检测到CTS为低后,就停止发送数据,直到接收端缓冲区的数据量低于低位而将CTS置高电平。RTS则用来标明接收设备有没有准备好接收数据。 常用的流控制还有还有DTR/DSR(数据终端就绪/数据设置就绪)。我们在此不再详述。 3.软件流控制 由于电缆线的限制,我们在普通的控制通讯中一般不用硬件流控制,而用软件流控制。一般通过XON/XOFF来实现软件流控制。常用方法是:当接收端的输入缓冲区内数据量超过设定的高位时,就向数据发送端发出XOFF字符(十进制的19或Control-S,设备编程说明书应该有详细阐述),发送端收到XOFF字符后就立即停止发送数据;当接收端的输入缓冲区内数据量低于设定的低位时,就向数据发送端发出XON字符(十进制的17或Control-Q),发送端收到XON字符后就立即开始发送数据。一般可以从设备配套源程序中找到发送的是什么字符。 应该注意,若传输的是二进制数据,标志字符也有可能在数据流中出现而引起误操作,这是软件流控制的缺陷,而硬件流控制不会有这个问题。 strtok() 字符串分割函数 strstr() 字符串查找函数 strspn() 字符查找函数 strrchr() 定位字符串中最后出现的指定字符 strpbrk() 定位字符串中第一个出现的指定字符 strncpy() 复制字符串 strncat() 字符串连接函数 strncasecmp() 字符串比较函数(忽略大小写) strlen() 字符串长度计算函数 strdup() 复制字符串 strcspn() 查找字符串 strcpy() 复制字符串 strcoll() 字符串比较函数(按字符排列次序) strcmp() 字符串比较函数(比较字符串) strchr() 字符串查找函数(返回首次出现字符的位置) strcat() 连接字符串 strcasecmp() 字符串比较函数(忽略大小写比较字符串) rindex() 字符串查找函数(返回最后一次出现的位置) index() 字符串查找函数(返回首次出现的位置) toupper() 字符串转换函数(小写转大写) tolower() 字符串转换函数(大写转小写) toascii() 将整数转换成合法的ASCII码字符 strtoul() 将字符串转换成无符号长整型数 strtol() 将字符串转换成长整型数 strtod() 将字符串转换成浮点数 gcvt() 将浮点型数转换为字符串(四舍五入) atol() 将字符串转换成长整型数 atoi() 将字符串转换成整型数 atof() 将字符串转换成浮点型数 linux下为了多线程同步,通常用到锁的概念。 select()函数的作用 系统调用select和poll的后端实现,用这两个系统调用来查询设备是否可读写,或是否处于某种状态。如果poll为空,则驱动设备会被认为即可读又可写,返回值是一个状态掩码 如何使用select()函数? select()函数的接口主要是建立在一种叫'fd_set'类型的基础上。它('fd_set') 是一组文件描述符(fd)的集合。由于fd_set类型的长度在不同平台上不同,因此应该用一组标准的宏定义来处理此类变量: fd_set set; FD_ZERO(&set); /* 将set清零 */ FD_SET(fd, &set); /* 将fd加入set */ FD_CLR(fd, &set); /* 将fd从set中清除 */ FD_ISSET(fd, &set); /* 如果fd在set中则真 */ 在过去,一个fd_set通常只能包含少于等于32个文件描述符,因为fd_set其实只用了一个int的比特矢量来实现,在大多数情况下,检查 fd_set能包括任意值的文件描述符是系统的责任,但确定你的fd_set到底能放多少有时你应该检查/修改宏FD_SETSIZE的值。*这个值是系统相关的*,同时检查你的系统中的select() 的man手册。有一些系统对多于1024个文件描述符的支持有问题。[译者注: Linux就是这样的系统!你会发现sizeof(fd_set)的结果是128(*8 = FD_SETSIZE=1024) 尽管很少你会遇到这种情况。] select的基本接口十分简单: int select(int nfds, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout); 其中: nfds 需要检查的文件描述符个数,数值应该比是三组fd_set中最大数 更大,而不是实际文件描述符的总数。 readset 用来检查可读性的一组文件描述符。 writeset 用来检查可写性的一组文件描述符。 exceptset 用来检查意外状态的文件描述符。(注:错误并不是意外状态) timeout NULL指针代表无限等待,否则是指向timeval结构的指针,代表最 长等待时间。(如果其中tv_sec和tv_usec都等于0, 则文件描述符 的状态不被影响,但函数并不挂起) Linux快速入门Linux快速入门(...Linux快速入门(... 函数将返回响应操作的对应操作文件描述符的总数,且三组数据均在恰当位置被修改,只有响应操作的那一些没有修改。接着应该用FD_ISSET宏来查找返回的文件描述符组。 这里是一个简单的测试单个文件描述符可读性的例子: int isready(int fd) { int rc; fd_set fds; struct timeval tv; FD_ZERO(&fds); FD_SET(fd,&fds); // tv.tv_sec = tv.tv_usec = 0; //rc = select(fd+1, &fds, NULL, NULL, &tv); rc = select(fd+1, &fds, NULL, NULL, NULL); if (rc < 0) return -1; return FD_ISSET(fd,&fds) ? 1 : 0; } 当然如果我们把NULL指针作为fd_set传入的话,这就表示我们对这种操作的发生不感兴趣,但select() 还是会等待直到其发生或者超过等待时间。 [译者注:在Linux中,timeout指的是程序在非sleep状态中度过的时间,而不是实际上过去的时间,这就会引起和非Linux平台移植上的时间不等问题。移植问题还包括在System V风格中select()在函数退出前会把timeout设为未定义的 NULL状态,而在BSD中则不是这样, Linux在这点上遵从System V,因此在重复利用timeout指针问题上也应该注意。] Linux下select调用的过程: 1.用户层应用程序调用select(),底层调用poll()) 2.核心层调用sys_select() ------> do_select() 最终调用文件描述符fd对应的struct file类型变量的struct file_operations *f_op的poll函数。 poll指向的函数返回当前可否读写的信息。 1)如果当前可读写,返回读写信息。 2)如果当前不可读写,则阻塞进程,并等待驱动程序唤醒,重新调用poll函数,或超时返回。 3.驱动需要实现poll函数。 当驱动发现有数据可以读写时,通知核心层,核心层重新调用poll指向的函数查询信息。 poll_wait(filp,&wait_q,wait) // 此处将当前进程加入到等待队列中,但并不阻塞 在中断中使用wake_up_interruptible(&wait_q)唤醒等待队列 函数名:ioctl 头文件:#include 功 能: 控制I/O设备 ,提供了一种获得设备信息和向设备发送控制参数的手段。用于向设备发控制和配置命令 ,有些命令需要控制参数,这些数据是不能用read/ write 读写的,称为Out-of-band数据。也就是说,read /write 读写的数据是in-band数据,是I/O操作的主体,而ioctl 命令传送的是控制信息,其中的数据是辅助的数据。 用 法: intioctl(int handle, int cmd,[int *argdx, int argcx]); 返回值:成功为0,出错为-1 usr/include/asm-generic/ioctl.h中定义的宏的注释: #define _IOC_NRBITS 8 //序数(number)字段的字位宽度,8bits #define _IOC_TYPEBITS 8 //幻数(type)字段的字位宽度,8bits #define _IOC_SIZEBITS 14 //大小(size)字段的字位宽度,14bits #define _IOC_DIRBITS 2 //方向(direction)字段的字位宽度,2bits #define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) //序数字段的掩码,0x000000FF #define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) //幻数字段的掩码,0x000000FF #define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) //大小字段的掩码,0x00003FFF #define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) //方向字段的掩码,0x00000003 #define _IOC_NRSHIFT 0 //序数字段在整个字段中的位移,0 #define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) //幻数字段的位移,8 #define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) //大小字段的位移,16 #define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) //方向字段的位移,30 /* * Direction bits. */ #define _IOC_NONE 0U //没有数据传输 #define _IOC_WRITE 1U //向设备写入数据,驱动程序必须从用户空间读入数据 #define _IOC_READ 2U //从设备中读取数据,驱动程序必须向用户空间写入数据 #define _IOC(dir,type,nr,size) \ (((dir) << _IOC_DIRSHIFT) | \ ((type) << _IOC_TYPESHIFT) | \ ((nr) << _IOC_NRSHIFT) | \ ((size) << _IOC_SIZESHIFT)) /* * used to create numbers */ //构造无参数的命令编号 #define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) //构造从驱动程序中读取数据的命令编号 #define _IOR(type,nr,size)_IOC(_IOC_READ,(type),(nr),sizeof(size)) //用于向驱动程序写入数据命令 #define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size)) //用于双向传输 #define _IOWR(type,nr,size)_IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size)) /* *used to decode ioctl numbers.. */ //从命令参数中解析出数据方向,即写进还是读出 #define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) &_IOC_DIRMASK) //从命令参数中解析出幻数type #define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) &_IOC_TYPEMASK) //从命令参数中解析出序数number #define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) &_IOC_NRMASK) //从命令参数中解析出用户数据大小 #define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) &_IOC_SIZEMASK) /* ...and for the drivers/sound files... */ #define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT) #define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT) #define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT) #define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT) #define IOCSIZE_SHIFT (_IOC_SIZESHIFT) 程序例: #include #include #include int main(void) { ..int stat; /* use func 8 to determine if the default drive is removable */ ..stat = ioctl(0, 8, 0, 0); ..if (!stat) ....printf("Drive %c is removable.\n", getdisk() + 'A'); ..else ....printf("Drive %c is not removable.\n", getdisk() +'A'); ..return 0; } int ioctl( int fd, int request, .../* void *arg */ ) 详解 第三个参数总是一个指针,但指针的类型依赖于request参数。我们可以把和网络相关的请求划分为6类: 套接口操作 文件操作 接口操作 ARP 高速缓存操作 路由表操作 流系统 下表列出了网络相关ioctl请求的request 参数以及arg 地址必须指向的数据类型: 类别 Request 说明 数据类型 套 接 口 SIOCATMARK SIOCSPGRP SIOCGPGRP 是否位于带外标记 设置套接口的进程ID 或进程组ID 获取套接口的进程ID 或进程组ID int int int 文 件 FIONBIO FIOASYNC FIONREAD FIOSETOWN FIOGETOWN 设置/ 清除非阻塞I/O 标志 设置/ 清除信号驱动异步I/O 标志 获取接收缓存区中的字节数 设置文件的进程ID 或进程组ID 获取文件的进程ID 或进程组ID int int int int int 接 口 SIOCGIFCONF SIOCSIFADDR SIOCGIFADDR SIOCSIFFLAGS SIOCGIFFLAGS SIOCSIFDSTADDR SIOCGIFDSTADDR SIOCGIFBRDADDR SIOCSIFBRDADDR SIOCGIFNETMASK SIOCSIFNETMASK SIOCGIFMETRIC SIOCSIFMETRIC SIOCGIFMTU SIOCxxx 获取所有接口的清单 设置接口地址 获取接口地址 设置接口标志 获取接口标志 设置点到点地址 获取点到点地址 获取广播地址 设置广播地址 获取子网掩码 设置子网掩码 获取接口的测度 设置接口的测度 获取接口MTU (还有很多取决于系统的实现) struct ifconf struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq ARP SIOCSARP SIOCGARP SIOCDARP 创建/ 修改ARP 表项 获取ARP 表项 删除ARP 表项 struct arpreq struct arpreq struct arpreq 路 由 SIOCADDRT SIOCDELRT 增加路径 删除路径 struct rtentry struct rtentry 流 I_xxx 明确用于套接口操作的ioctl请求有三个, 它们都要求ioctl的第三个参数是指向某个整数的一个指针。 SIOCATMARK: 如果本套接口的的度指针当前位于带外标记,那就通过由第三个参数指向的整数返回一个非0值;否则返回一个0值。POSIX以函数sockatmark替换本请求。 SIOCGPGRP : 通过第三个参数指向的整数返回本套接口的进程ID或进程组ID,该ID指定针对本套接口的SIGIO或SIGURG信号的接收进程。本请求和fcntl的F_GETOWN命令等效,POSIX标准化的是fcntl函数。 SIOCSPGRP : 把本套接口的进程ID或者进程组ID设置成第三个参数指向的整数,该ID指定针对本套接口的SIGIO或SIGURG信号的接收进程,本请求和fcntl的F_SETOWN命令等效,POSIX标准化的是fcntl操作。 以下5个请求都要求ioctl的第三个参数指向一个整数。 FIONBIO : 根据ioctl的第三个参数指向一个0或非0值分别清除或设置本套接口的非阻塞标志。本请求和O_NONBLOCK文件状态标志等效,而该标志通过fcntl的F_SETFL命令清除或设置。 FIOASYNC : 根据ioctl的第三个参数指向一个0值或非0值分别清除或设置针对本套接口的信号驱动异步I/O标志,它决定是否收取针对本套接口的异步I/O信号(SIGIO)。本请求和O_ASYNC文件状态标志等效,而该标志可以通过fcntl的F_SETFL命令清除或设置。 FIONREAD : 通过由ioctl的第三个参数指向的整数返回当前在本套接口接收缓冲区中的字节数。本特性同样适用于文件,管道和终端。 FIOSETOWN : 对于套接口和SIOCSPGRP等效。 FIOGETOWN : 对于套接口和SIOCGPGRP等效。 ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就 是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。它的调用个数 如下: int ioctl(int fd, int cmd, …); 其中fd就是用户程序打开设备时使用open函数返回的文件标示符,cmd就是用户程序对设 备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,有或没有是和 cmd的意义相关的。 ioctl函数是文件结构中的一个属性分量,就是说如果你的驱动程序提供了对ioctl的支 持,用户就能在用户程序中使用ioctl函数控制设备的I/O通道。 如果不用IOCTL的话,也能实现对设备I/O通道的控制,但那就是蛮拧了。例如,我们可 以在驱动程式中实现WRITE的时候检查一下是否有特别约定的数据流通过,如果有的话, 那么后面就跟着控制命令(一般在SOCKET编程中常常这样做)。不过如果这样做的话,会 导致代码分工不明,程式结构混乱,程式员自己也会头昏眼花的。 所以,我们就使用IOCTL来实现控制的功能。要记住,用户程式所作的只是通过命令码告 诉驱动程式他想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程式要 做的事情。 读者只要把write换成ioctl,就知道用户程式的ioctl是怎么和驱动程式中的ioctl实现联系在一起的了。 我这里说一个大概思路,因为我觉得《Linux设备驱动程式》这本书已说的非常清晰 了,不过得花一些时间来看。 在驱动程式中实现的ioctl函数体内,实际上是有一个switch{case}结构,每一个case对 应一个命令码,做出一些相应的操作。怎么实现这些操作,这是每一个程式员自己的事 情,因为设备都是特定的,这里也没法说。关键在于怎么样组织命令码,因为在ioctl中 命令码是唯一联系用户程式命令和驱动程式支持的途径。 命令码的组织是有一些讲究的,因为我们一定要做到命令和设备是一一对应的,这样才不 会将正确的命令发给错误的设备,或是把错误的命令发给正确的设备,或是把错误的 命令发给错误的设备。这些错误都会导致不可预料的事情发生,而当程式员发现了这些奇 怪的事情的时候,再来调试程式查找错误,那将是非常困难的事情。 所以在Linux核心中是这样定义一个命令码的: ____________________________________ | 设备类型| 序列号| 方向|数据尺寸| |----------|--------|------|--------| | 8 bit | 8 bit |2 bit |8~14 bit| |----------|--------|------|--------| 这样一来,一个命令就变成了一个整数形式的命令码。不过命令码非常的不直观,所以 Linux Kernel中提供了一些宏,这些宏可根据便于理解的字符串生成命令码,或是从 命令码得到一些用户能理解的字符串以标明这个命令对应的设备类型、设备序列号、数 据传送方向和数据传输尺寸。 这些宏我就不在这里解释了,具体的形式请读者察看Linux核心原始码中的和,文件里给 除了这些宏完整的定义。这里我只多说一个地方,那就是"幻数"。 幻数是个字母,数据长度也是8,所以就用一个特定的字母来标明设备类型,这和用一 个数字是相同的,只是更加利于记忆和理解。就是这样,再没有更复杂的了。 更多的说了也没有,读者还是看一看原始码吧,推荐各位阅读《Linux设备驱动程式》所 带原始码中的short一例,因为他比较短小,功能比较简单,能看明白ioctl的功能和细 节。 cmd参数怎么得出 这里确实要说一说,cmd参数在用户程式端由一些宏根据设备类型、序列号、传送方向、 数据尺寸等生成,这个整数通过系统调用传递到内核中的驱动程式,再由驱动程式使用解 码宏从这个整数中得到设备的类型、序列号、传送方向、数据尺寸等信息,然后通过 switch{case}结构进行相应的操作。 要透彻理解,只能是通过阅读原始码,我这篇文章实际上只是个引子。Cmd参数的组织 还是比较复杂的,我认为要搞熟他还是得花不少时间的,不过这是值得的,驱动程式中最 难的是对中断的理解。 五、 小结 ioctl其实没有什么非常难的东西需要理解,关键是理解cmd命令码是怎么在用户程式里生成 并在驱动程式里解析的,程式员最主要的工作量在switch{case}结构中,因为对设备的 I/O控制都是通过这一部分的代码实现的。 原型:int mkdir (const char *filename, mode_t mode) 信号量的数据类型为结构sem_t,它本质上是一个长整型的数。函数sem_init()用来初始化一个信号量。它的原型为: extern int sem_init __P((sem_t *__sem, int __pshared, unsigned int __value)); sem为指向信号量结构的一个指针;pshared不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享;value给出了信号量的初始值。 函数sem_post( sem_t *sem )用来增加信号量的值。当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不在阻塞,选择机制同样是由线程的调度策略决定的。 函数sem_wait( sem_t *sem )被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少。函数sem_trywait( sem_t *sem )是函数sem_wait()的非阻塞版本,它直接将信号量sem的值减一。 函数sem_destroy(sem_t *sem)用来释放信号量sem。 信号量用sem_init函数创建的,下面是它的说明: #include sem_t bin_sem; void*thread_function2(void *arg) 1) 永久性生效,重启后不会复原 二.UBuntu关闭防火墙 三.CentOS Linux防火墙配置及关闭 执行”setup”命令启动文字模式配置实用程序,在”选择一种工具”中选择”防火墙配置”,然后选择”运行工具”按钮,出现防火墙配置界面,将”安全级别”设为”禁用”,然后选择”确定”即可. 或者用命令: 防火墙的关闭,关闭其服务即可: 1、 可以把它放在/etc/rc.d/init.d/rc.local文件中 作者: Aillo, 发布于2009-05-10, 在系统分类下, 1条留言。 重启命令: 关机命令: 在windows下安装一个软件很轻松,只要双击.exe的文件,安装提示连续“下一步”即可,然而linux系统下安装一个软件似乎并不那么轻松了,因为我们不是在图形界面下。所以你要学会如何在linux下安装一个软件。 在前面的内容中多次提到的yum,这个yum是Redhat所特有的安装RPM程序包的工具,使用起来相当方便。因为使用RPM安装某一个程序包有可能会因为该程序包依赖另一个程序包而无法安装。而使用yum工具就可以连同依赖的程序包一起安装。当然CentOS同样可以使用yum工具,而且在CentOS中你可以免费使用yum,但Redhat中只有当你付费后才能使用yum,默认是无法使用yum的。在介绍yum之前先说一说RPM相关的东西。 【RPM工具】 RPM是”Redhat Package Manager”的缩写,根据名字也能猜到这是Redhat公司开发出来的。RPM 是以一种数据库记录的方式来将你所需要的套件安装到你的Linux 主机的一套管理程序。也就是说,你的linux系统中存在着一个关于RPM的数据库,它记录了安装的包以及包与包之间依赖相关性。RPM包是预先在linux机器上编译好并打包好的文件,安装起来非常快捷。但是也有一些缺点,比如安装的环境必须与编译时的环境一致或者相当;包与包之间存在着相互依赖的情况;卸载包时需要先把依赖的包卸载掉,如果依赖的包是系统所必须的,那就不能卸载这个包,否则会造成系统崩溃。 如果你的光驱中还有系统安装盘的话,你可以通过”mount /dev/cdrom /mnt”命令把光驱挂载到/mnt目录下,那么你会在/mnt/CentOS目录下看到很多.rpm的文件,这就是RPM包了。 每一个rpm包的名称都由”-“和”.”分成了若干部分。就拿 a2ps-4.13b-57.2.el5.i386.rpm 这个包来解释一下,a2ps 为包名;4.13b则为版本信息;57.2.el5为发布版本号;i386为运行平台。其中运行平台常见的有i386,i586, i686, x86_64 ,需要你注意的是cpu目前是分32位和64位的,i386,i586和i686都为32位平台,x86_64则代表为64位的平台。另外有些rpm包并没有写具体的平台而是noarch,这代表这个rpm包没有硬件平台限制。例如 alacarte-0.10.0-1.fc6.noarch.rpm。下面介绍一下rpm常用的命令。 1)安装一个rpm包 -i :安装的意思 -v :可视化 -h :显示安装进度 另外在安装一个rpm包时常用的附带参数有: --force 强制安装,即使覆盖属于其他包的文件也要安装 --nodeps 当要安装的rpm包依赖其他包时,即使其他包没有安装,也要安装这个包 2)升级一个rpm包 rpm-Uvh filename -U :即升级的意思 3)卸载一个rpm包 rpm -efilename 这里的filename是通过rpm的查询功能所查询到的,稍后会作介绍。 卸载时后边跟的filename和安装时的是有区别的。上面命令提到的 “|”在linux系统中用的非常多也非常有用,它是一个管道符,用来把前面运行的结果传递给后面的命令。以后会做详细介绍,而后出现的grep命令则是用来过滤某个关键词的工具,在后续章节中会做详细介绍。 4)查询一个包是否安装 rpm -qrpm包名(这里的包名,是不带有平台信息以及后缀名的) 如果加上了平台信息以及后缀名反而不能查出来。你还可以查询当前系统中所安装的所有rpm包。 因为太多,所以笔者列出前十个。 5)得到一个rpm包的相关信息 rpm -qi 包名 (同样不需要加平台信息与后缀名) 6)列出一个rpm包安装的文件 rpm -ql 包名 通过上面的命令可以看出vim是通过安装vim-enhanced-7.0.109-6.el5这个rpm包得来的。那么反过来如何通过一个文件去查找是由安装哪个rpm包得来的? 7)列出某一个文件属于哪个rpm包 rpm -qf 文件的绝对路径 前面讲过如何查找一个文件(可执行命令)的绝对路径 所以你也可以把这两条命令连起来写 看到了吗,whichvim 这条命令是由两个反引号引起来的,这代表引用反引号里面的命令所产生的结果。关于rpm工具的使用还有很多内容,笔者就不一一列举了,只要你掌握上面这些内容,完全够你平时工作用的了。 【yum工具】 介绍完rpm工具后,还需要你掌握最常用的yum工具,这个工具比rpm工具好用多了,当然前提是你使用的linux系统是支持yum的。yum最大的优势在于可以联网去下载所需要的rpm包,然后自动安装,在这个工程中如果要安装的rpm包有依赖关系,yum会帮你解决掉这些依赖关系依次安装所有rpm包。下面笔者介绍常用的yum 命令。 1) 列出所有可用的rpm包 “yum list “ 限于篇幅,笔者只列举出来前7个包信息。从上例中可以看到有”mirrors.163.com”信息出现,这是在告诉用户,它是从mirrors.163.com这里下载到的rpm包资源。如果你使用的是CentOS则你可以从/etc/yum.repos.d/CentOS-Base.repo这个文件下看到相关的配置信息。从上面的例子中你还可以看到最左侧是rpm包名字,中间是版本信息,最右侧是安装信息,如果安装了就显示installed,未安装则显示base或者extras,如果是该rpm包已安装但需要升级则显示updates。 2)搜索一个rpm包 “yum search [相关关键词]” 除了这样搜索外,笔者常用的是利用grep来过滤 相信你也会喜欢用后者吧,这样看起来简明的多。 3)安装一个rpm包 “yum install [-y] [rpm包名]” 如果不加-y选项,则会以与用户交互的方式安装,首先是列出需要安装的rpm包信息,然后会问用户是否需要安装,输入y则安装,输入n则不安装。而笔者嫌这样太麻烦,所以直接加上-y选项,这样就省略掉了问用户是否安装的那一步。 4)卸载一个rpm包 “yum remove [-y] [rpm包名]” 卸载和安装一样,你也可以直接加上-y选项来省略掉和用户交互的步骤。在这里笔者要提醒你一下,卸载某个rpm包一定要看清楚了,不要连其他重要的rpm包一起卸载了,以免影响正常的业务。 4)升级一个rpm包 “yum update [-y] [rpm包]” 以上介绍了如何使用yum搜索、安装、卸载以及升级一个rpm包,如果你掌握了这些那么你就已经可以解决日常工作中遇到的与rpm包相关问题了。当然yum工具还有好多其他好用的命令,笔者不在列举出来,如果你感兴趣就去man一下吧。除此之外,笔者还会教你一些关于yum的小应用。 1 使用本地的光盘来制作一个yum源 有时候你的linux系统不能联网,当然就不能很便捷的使用联网的yum源了,这时候就需要你自己会利用linux系统光盘制作一个yum源。具体步骤如下: a.挂载光盘 [root@fortest Server]# mount -t iso9660 -o loop /dev/cdrom /mnt b.删除/etc/yum.repos.d目录所有的repo文件 [root@fortest Server]# rm -rf /etc/yum.repos.d/* c.创建新文件dvd.repo [root@fortest Server]# vim /etc/yum.repos.d/dvd.repo 加入以下内容: [dvd] name=install dvd baseurl=file:///mnt enabled=1 gpgcheck=0 d.刷新repos,生成缓存 [root@fortest Server]#yum makecache 然后就可以使用yum命令安装你所需要的软件包了 2 利用yum工具下载一个rpm包 有时,我们需要下载一个rpm包,只是下载下来,拷贝给其他机器使用,前面也介绍过yum安装rpm包的时候,首先得下载这个rpm包然后再去安装,所以使用yum完全可以做到只下载而不安装。 a. 首选要安装 yum-downloadonly # yum install -y yum-downloadonly.noarch b. 下载一个rpm包而不安装 # yum install test.rpm -y --downloadonly //这样虽然下载了,但是并没有保存到我们想要的目录下,那么如何指定目录呢? c. 下载到指定目录 # yum install test.rpm -y --downloadonly --downloaddir=/usr/local/src 【安装源码包】 其实,在linux下面安装一个源码包是最常用的,笔者在日常的管理工作中,大部分软件都是通过源码安装的。安装一个源码包,是需要我们自己把源代码编译成二进制的可执行文件。如果你读得懂这些源代码,那么你就可以去修改这些源代码自定义功能,然后再去编译成你想要的。使用源码包的好处除了可以自定义修改源代码外还可以定制相关的功能,因为源码包在编译的时候是可以附加额外的选项的。 源码包的编译用到了linux系统里的编译器,常见的源码包一般都是用C语言开发的,这也是因为C语言为linux上最标准的程序语言。Linux上的C语言编译器叫做gcc,利用它就可以把C语言变成可执行的二进制文件。所以如果你的机器上没有安装gcc就没有办法去编译源码。你可以使用 yum install -y gcc 来完成安装。 安装一个源码包,通常需要三个步骤: 1. ./config 在这一步可以定制功能,加上相应的选项即可,具有有什么选项可以通过”./config--help ”命令来查看。在这一步会自动检测你的linux系统与相关的套件是否有编译该源码包时需要的库,因为一旦缺少某个库就不能完成编译。只有检测通过后才会生成一个Makefile文件。 2. make 使用这个命令会根据Makefile文件中预设的参数进行编译,这一步其实就是gcc在工作了。 3. make install 安装步骤,生成相关的软件存放目录和配置文件的过程。 上面介绍的3步并不是所有的源码包软件都一样的,笔者以前也曾经遇到过,安装步骤并不是这样,也就是说源码包的安装并非具有一定的标准安装步骤。这就需要你拿到源码包解压后,然后进入到目录找相关的帮助文档,通常会以INSTALL或者README为文件名。所以,你一定要去看一下。下面笔者会编译安装一个源码包来帮你更深刻的去理解如何安装源码包。 1. 下载一个源码包 这里要提一下,建议以后你把所有下载的源码包放到/usr/local/src/目录下,这个并不是必须的,只是一个约定。方便你和你的同事将来更好的去运维这台服务器。wget即为下载的命令,后边跟源码包的下载地址。该地址为笔者从网上找的一个apache的下载地址。 2. 解压源码包 一般的源码包都是一个压缩包,如何解压一个.tar.gz的包上一章讲过的。 3. 配置相关的选项,并生成Makefile 使用./config--help 可以查看可用的选项。一般常用的有”--prefix=PREFIX“ 这个选项的意思是定义软件包安装到哪里。到这里,笔者再提一个小小的约定,通常源码包都是安装在/usr/local/目录下的。比如,我们把apache安装在/usr/local/apache2下,那么这里就应该这样写” --prefix=/usr/local/apache2”。其他还有好多选项,如果你有耐心你可以挨个去看一看都有什么作用。 笔者在这里只定义了apache的安装目录,其他都是默认。回车后,开始执行check操作。 等check结束后生成了Makefile文件 除了查看有没有生成Makefile文件来判定有没有完成./config 的操作外,还可以通过这个命令”echo $?”来判定,如果是0,则表示上一步操作成功完成,否则就是没有成功。 4. 进行编译 这一步操作,就是把源代码编译成二进制的可执行文件,这一步也是最漫长的一步,编译时间的长短取决于源代码的多少和机器配置。 5. 安装 在安装前,先确认上一步操作是否成功完成。 makeinstall 会创建相应的目录以及文件。当完成安装后,会在/usr/local目录下多了一个apache2目录,这就是apache所安装的目录了。 其实在日常的源码安装工作中,并不是每个都像笔者这样顺利完成安装的,遇到错误不能完成安装的情况是很多的。通常都是因为缺少某一个库文件导致的。这就需要你仔细琢磨报错信息或者查看当前目录下的config.log去得到相关的信息。另外,如果自己不能解决那就去网上google一下吧,通常你会得到你想要的答案。 全屏阅读 2013-03-0823:03:47 作者:王睿宇 所属分类:Linux 阅读:322 评论:0 标签: Linux, rpm, YUM · · RPM是RedHat Package Manager(RedHat软件包管理工具)类似Windows里面的“添加/删除程序” rpm 执行安装包 常用命令组合: -ivh:安装显示安装进度--install--verbose--hash rpm -q samba //查询程序是否安装rpm -ivh /media/cdrom/RedHat/RPMS/samba-3.0.10-1.4E.i386.rpm //按路径安装并显示进度 rpm -ivh file.rpm #[安装新的rpm]--install--verbose--hash rpm -Uvh file.rpm #[升级一个rpm]--upgrade 常用参数: Install/Upgrade/Erase options: -i, --install install package(s) RPM源代码包装安装 .src.rpm结尾的文件,这些文件是由软件的源代码包装而成的,用户要安装这类RPM软件包,必须使用命令: rpm--recompilevim-4.6-4.src.rpm #这个命令会把源代码解包并编译、安装它,如果用户使用命令:rpm--rebuildvim-4.6-4.src.rpm #在安装完成后,还会把编译生成的可执行文件重新包装成i386.rpm的RPM软件包。 偶不喜欢写比较复杂的东东,麻烦的话`不过做为参考`偶还素转了一位哒人的`写的真很全面` 作者:北南南北 RPM 是 Red Hat Package Manager 的缩写,本意是Red Hat 软件包管理,顾名思义是RedHat 贡献出来的软件包管理;在Fedora 、Redhat、Mandriva、SuSE、YellowDog等主流发行版本,以及在这些版本基础上二次开发出来的发行版采用; RPM包里面都包含什么?里面包含可执行的二进制程序,这个程序和Windows的软件包中的.exe文件类似是可执行的;RPM包中还包括程序运行时所需要的文件,这也和Windows的软件包类似,Windows的程序的运行,除了.exe文件以外,也有其它的文件; 一个RPM 包中的应用程序,有时除了自身所带的附加文件保证其正常以外,还需要其它特定版本文件,这就是软件包的依赖关系;依赖关系并不是Linux特有的, Windows操作系统中也是同样存在的;比如我们在Windows系统中运行3D游戏,在安装的时候,他可能会提示,要安装Direct 9 ;Linux和Windows原理是差不多的; 软件安装流程图: 1)Fedora 系统管理软件包工具 system-config-packages,方便的添加和移除系统安装盘提供的软件包,详情请看《Fedora 软件包管理器system-config-packages》 2)Redhat 系统管理软件包工具,新一点的系统应该是 redhat-config-packages ,用法和《Fedora 软件包管理器system-config-packages》一样; 3)apt + synaptic 软件包在线安装、移除、升级工具;用法:《用apt+synaptic 在线安装或升级Fedoracore 4.0 软件包》 5)所有的yum和apt 教程《apt and yum》 目前 apt和yum 已经极为成熟了,建议我们安装软件时,采用apt或者yum ;如果安装系统盘提供的软件包,可以用 system-config-packages 或redhat-config-packages ; 1、可以安装、删除、升级和管理软件;当然也支持在线安装和升级软件; 注:这两个参数是极为有用,有时rpm系统出了问题,不能安装和查询,大多是这里出了问题; RPM的查询功能是极为强大,是极为重要的功能之一;举几个常用的例子,更为详细的具体的,请参考#man rpm 1)查询系统已安装的软件; 举例: -q就是 --query ,中文意思是“问”,此命令表示的是,是不是系统安装了gaim ;如果已安装会有信息输出;如果没有安装,会输出gaim 没有安装的信息; 查看系统中所有已经安装的包,要加 -a参数; 如果分页查看,再加一个管道 |和more命令; 在所有已经安装的软件包中查找某个软件,比如说gaim ;可以用 grep 抽取出来; 上面这条的功能和rpm -q gaim 输出的结果是一样的; 2)查询一个已经安装的文件属于哪个软件包; 注:文件名所在的绝对路径要指出 举例: 3)查询已安装软件包都安装到何处; 举例: 4)查询一个已安装软件包的信息 举例: 5)查看一下已安装软件的配置文件; 举例: 6)查看一个已经安装软件的文档安装位置: 举例: 7)查看一下已安装软件所依赖的软件包及文件; 举例: 查询已安装软件的总结:对于一个软件包已经安装,我们可以把一系列的参数组合起来用;比如 rpm -qil ;比如: 1)查看一个软件包的用途、版本等信息; 举例: 2)查看一件软件包所包含的文件; 举例: 3)查看软件包的文档所在的位置; 举例: 5)查看一个软件包的配置文件; 举例: 4)查看一个软件包的依赖关系 举例: 如果有依赖关系的,请解决依赖关系,其实软件包管理器能很好的解决依赖关系,请看前面的软件包管理器的介绍;如果您在软件包管理器中也找不到依赖关系的包;那只能通过编译他所依赖的包来解决依赖关系,或者强制安装; 语法结构: 更多的参数,请查看man rpm 举例应用: 注: --replacepkgs 参数是以已安装的软件再安装一次;有时没有太大的必要; 测试安装参数--test ,用来检查依赖关系;并不是真正的安装; 由新版本降级为旧版本,要加--oldpackage 参数; 为软件包指定安装目录:要加-relocate 参数;下面的举例是把gaim-1.3.0-1.fc4.i386.rpm指定安装在 /opt/gaim 目录中; 为软件包指定安装目录:要加-relocate 参数;下面的举例是把lynx-2.8.5-23.i386.rpm 指定安装在 /opt/lynx 目录中; 我们安装在指定目录中的程序如何调用呢?一般执行程序,都放在安装目录的bin或者sbin目录中;看下面的例子;如果有错误输出,就做相应的链接,用 ln -s ; 首先您要学会查询rpm包;请看前面的说明; [root@localhost beinan]#rpm -e 软件包名 举例:我想移除lynx包,完整的操作应该是: 如果有依赖关系,您也可以用--nodeps忽略依赖的检查来删除。但尽可能不要这么做,最好用软件包管理器 systerm-config-packages 来删除或者添加软件; 举例: 关于RPM的签名功能,详情请参见man rpm 地址: http://mirrors.kernel.org/fedora/core/4/i386/os/Fedora/RPMS/ 举例: 命令格式: 举一反三吧; 通过updatedb,我们可以用locate 来查询一些软件安装到哪里了;系统初次安装时要执行updatedb ,每隔一段时间也要执行一次;以保持已安装软件库最新;updatedb 是slocate软件包所有;如果您没有这个命令,就得安装slocate ; 举例: 举例: 抽取出来的文件就在当用操作目录中的usr 和etc中; 其实这样抽到文件不如指定安装目录来安装软件来的方便;也一样可以抽出文件; 为软件包指定安装目录:要加-relocate 参数;下面的举例是把gaim-1.3.0-1.fc4.i386.rpm指定安装在 /opt/gaim 目录中; 这样也能一目了然;gaim的所有文件都是安装在/opt/gaim 中,我们只是把gaim 目录备份一下,然后卸掉gaim;这样其实也算提取文件的一点用法; 我们可以通过rpm --showrc 查看;具体的还得我们自己来学习。呵。。。不要问我,我也不懂;只要您看了这篇文章,认为对您有用,您的水平就和我差不多;咱们水平是一样的,所以我不能帮助您了;请理解; 以我的水平来看,写Fedora入门教程是极为费力气的,只能一点一点的完善和补充;我所写的教程是面对的是对Linux一无所知新手;教程中实例应用占大部份;我发现没有实例的情况下,新手不如看man ;能看man了,当然也不是什么新手; 经常在论坛上看一些弟兄的提问,虽然一问话解说过去也能应付;但想让大家更方便一点,不如写系统入门教程;虽然所花的时间要长一点; 发布时间:2007-01-0117:26:00 来源: ChinaUnix博客 作者: ChinaUnix博客 点击:358 2009-03-20 20:09 1259人阅读 评论(0) 收藏 举报 socketserver网络socketsthreadstream 我通过几个采用 CSocket 类编写并基于Client/Server (客户端/ 服务端)的网络聊天和传输文件的程式 ,在调试这些程式的过程中,追踪深入至CSocket 类核心源码SockCore.cpp ,对于CSocket 类的运行机制可谓是一览无遗,并且对于阻塞和非阻塞方式下的socket 程式的编写也是稍有体会。 阅读本文请先注意 : • 这里的阻塞和非阻塞的概念仅适用于Server 端socket 程式。 • socket 意为套接字,他和Socket 不同,请注意首字母的大小写。 说明:客户端和服务端的通信简单来讲:服务端 socket 负责监听,应答,接收和发送消息,而客户端socket 只是连接,应答,接收,发送消息。此外,如果你对于采用CSocket 类编写Client/Server 网络程式的原理不是非常了解,请先查询一下( 详见:参考书籍和在线帮助 )。 在此之前,有必要先讲述一下:网络传输服务提供者, ws2_32.dll , socket 事件和 socket window 。 1. 网络传输服务提供者(网络传输服务进程),Socket 事件, SocketWindow 网络传输服务提供者( transport service provider )是以 DLL 的形式存在的,在 windows操作系统启动时由服务进程svchost.exe 加载。当socket 被创建时,调用API 函数 Socket (在 ws2_32.dll中), Socket 函数会传递三个参数 : 地址族,套接字类型 ( 注 2 )和协议,这三个参数决定了是由哪一个类型的网络传输服务提供者来启动网络传输服务功能。所有的网络通信正是由网络传输服务提供者完成, 这里将网络传输服务提供者称为网络传输服务进程更有助于理解,因为前文已提到网络传输服务提供者是由 svchost.exe 服务进程所加载的。 当 Client 端 socket 和 Server 端 socket 相互通信时,两端均会触发 socket 事件 : 这里仅简要说明两个 socket 事件: FD_CONNECT:连接事件 , 通常 Client 端 socket 调用 socketAPI 函数 Connect时所触发,这个事件发生在Client 端。 FD_ACCEPT:正在引入的连接事件,通常Server 端socket 正在接收来自Client 端socket 连接时触发,这个事件发生在Server 端。 网络传输服务进程将 socket 事件保存至 socket 的事件队列中。 此外,网络传输服务进程还会向 socketwindow 发送消息WM_SOCKET_NOTIFY , 通知有socket 事件产生,见下文对 socketwindow 的周详说明: 调用 CSocket::Create 函数后, socket 被创建。 socket 创建过程中调用CAsyncSocket::AttachHandle(SOCKET hSocket, CAsyncSocket* pSocket, BOOL bDead) 。该函数的作用是: a. 将 socket 实例句柄和 socket 指针添加至当前模块状态( 注 1 )的一个映射表变量 m_pmapSocketHandle 中。 b. 在AttachHandle 过程中,会new 一个CSocketWnd 实例( 基于 CWnd 派生 ) ,这里将这个实例称之为 socketwindow ,进一步理解为他是存放所有sockets 的消息池( window 消息),请仔细查看,这里 socket 后多加了一个 s ,表示创建的多个 socket 将共享一个消息池。 c. 当 Client 端 socket 和 Server 端相互通信时 , 此时网络传输服务进程向 socket window 发送消息WM_SOCKET_NOTIFY ,需要说明的是CSocketWnd 窗口句柄保存在当前模块状态的m_hSocketWindow 变量中。 2. 阻塞模式 阻塞模式下 Server 端和 Client 端之间的通信处于同步状态下。 在 Server 端直接实例化 CSocket类,调用 Create 方法创建 socket ,然后调用方法 Listen 开始侦听,最后用一个 while 循环阻塞调用 Accept 函数用于等待来自 Client 端的连接,如果这个 socket 在主线程(主程式)中运行,这将导致主线程的阻塞。因此,需要创建一个新的线程以运行socket 服务。 调试跟踪至 CSocket::Accept 函数源码: 他不断调用 CAsyncSocket::Accept ( CSocket派生自CAsyncSocket 类)判断Server 端socket 的事件队列中是否存在正在引入的连接事件- FD_ACCEPT (见1 ),换句话说,就是判断是否有来自Client 端socket 的连接请求。 如果当前 Server 端 socket 的事件队列中存在正在引入的连接事件,Accept 返回一个非0 值。否则, Accept 返回 0 ,此时调用GetLastError 将返回错误代码WSAEWOULDBLOCK ,表示队列中无所有连接请求。 注意到在循环体内有一句代码: PumpMessage(FD_ACCEPT); PumpMessage作为一个消息泵使得socket window 中的消息能够维持在活动状态。 实际跟踪进入 PumpMessage 中,发现这个消息泵和 Accept 函数的调用并不相关,他只是使非常少的socket window 消息(典型的是WM_PAINT 窗口重绘消息)处于活动状态,而绝大部分的socket window 消息被阻塞,被阻塞的消息中含有WM_SOCKET_NOTIFY 。 非常显然,如果没有来自 Client 端 socket 的连接请求, CSocket就会不断调用 Accept 产生循环阻塞,直到有来自 Client 端 socket 的连接请求而解除阻塞。 阻塞解除后,表示 Server 端 socket 和 Client 端 socket 已成功连接, Server 端和 Client 端彼此相互调用 Send 和 Receive方法开始通信。 3. 非阻塞模式 在非阻塞模式下利用 socket 事件的消息机制, Server 端和 Client 端之间的通信处于异步状态下。 通常需要从 CSocket 类派生一个新类,派生新类的目的是重载socket 事件的消息函数,然后在 socket 事件的消息函数中添入合适的代码以完成 Client 端和 Server 端之间的通信,和阻塞模式相比,非阻塞模式无需创建一个新线程。 这里将讨论当 Server 端 socket 事件- FD_ACCEPT 被触发后,该事件的处理函数OnAccept 是怎么进一步被触发的。其他事件的处理函数如OnConnect, OnReceive 等的触发方式和此类似。 在 1 中已提到Client/Server 端通信时,Server 端socket 正在接收来自Client 端socket 连接请求,这将会触发FD_ACCEPT 事件,同时Server 端的网络传输服务进程向 Server 端的 socketwindow (CSocketWnd )发送事件通知消息WM_SOCKET_NOTIFY , 通知有FD_ACCEPT 事件产生, CsocketWnd 在收到事件通知消息后,调用消息处理函数OnSocketNotify: 消息参数 wParam 是 socket 的句柄, lParam 是 socket 事件。这里稍作解释一下, CSocketWnd 类是作为 CSocket类的友元类,这意味着他能访问 CSocket类中的保护和私有成员函数和变量,AuxQueueAdd 和ProcessAuxQueue 是CSocket 类的静态成员函数,如果你对友元不熟悉,请迅速找本有关C++ 书看一下友元的使用方法吧! CAsyncSocket* pSocket =CAsyncSocket::LookupHandle((SOCKET)wParam, TRUE); 其实也就是由 socket 句柄得到发送事件通知消息的 socket 指针 pSocket:从m_pmapSocketHandle 中查找(见1 )! 最后, WSAGETSELECTEVENT(lParam) 会取出事件类型,在一个简单的switch 语句中判断事件类型并调用事件处理函数。 本文的结束: Server 端 socket 处于阻塞调用模式下,他必须在一个新创建的线程中工作,防止主线程被阻塞。 当有多个 Client 端 socket 和 Server 端 socket 连接及通信时, Server 端采用阻塞模式就显得不适合了,应该采用非阻塞模式, 利用 socket 事件的消息机制来接受多个 Client 端 socket 的连接请求并进行通信。 在非阻塞模式下,利用 CSocketWnd 作为所有 sockets的消息池,是实现socket 事件的消息机制的关键技术。 1. 当前模块状态 用于保存当前线程和模块状态的一个结构,能通过AfxGetThreadModule() 获得。AFX_MODULE_THREAD_STATE 在CSocket 重新定义为_AFX_SOCK_THREAD_STATE 。 2.socket 类型 在 TCP/IP 协议中,Client/Server 网络程式采用TCP 协议:即 socket 类型为 SOCK_STREAM,他是可靠的连接方式。在这里不采用UDP 协议:即 socket 类型为SOCK_DGRAM ,他是不可靠的连接方式。 分类: 4.网络与云2011-09-2210:32 2151人阅读 评论(1) 收藏 举报 socket数据结构服务器网络serverunix 一、问题的引入——socket的引入是为了解决不同计算机间进程间通信的问题 1.socket与进程的关系 1).socket与进程间的关系:socket 用来让一个进程和其他的进程互通信息(IPC),而Socket接口是TCP/IP网络的API接口函数。 2).进程间通信(本机内) 进程间通信(不同计算机,要联网) 2、socket与文件的关系——如何理解socket是种特殊的I/O? 1)Socket最先应用于Unix操作系统,如果了解Unix系统的I/O的话,就很容易了解Socket了,因为Socket数据传输其实就是一种特殊的I/O。 2)可对其进行文件操作 3)有文件描述符。而文件描述符的本质是一个非负整数。只是用于区分。类似的还有进程ID。 3.服务器端口与连接个数的关系 3.Socket是网络上运行的两个程序间双向通讯的一端,它既可以接受请求,也可以发送请求,利用它可以较为方便的编写网络上数据的传递。 5.问:现在server与client想建立socket连接,server仅知道client的IP,端口号不知道,能建立连接吗?怎么建立呢?有没有代码看看? 6.精彩问答 问:看到的文章上说“每个网络通信循环地进出主计算机的TCP 应用层。它被两个所连接的号码唯一地识别。这两个号码合起来叫做套接字.组成套接字的这两个号码就是机器的IP 地址和TCP 软件所使用的端口号。” 二、socket和端口理解 一个socket句柄代表两个地址对 “本地ip:port”--“远程ip:port” 三、客户/服务器模式模式的理解 客户/服务器模式采取的是主动请求方式: 首先服务器方要先启动,并根据请求提供相应服务: 1. 打开一通信通道并告知本地主机,它愿意在某一公认地址上(周知口,如FTP为21)接收客户请求; 2. 等待客户请求到达该端口; 3. 接收到重复服务请求,处理该请求并发送应答信号。接收到并发服务请求,要激活一新进程来处理这个客户请求(如UNIX系统中用fork、exec)。新进程处理此客户请求,并不需要对其它请求作出应答。服务完成后,关闭此新进程与客户的通信链路,并终止。 4. 返回第二步,等待另一客户请求。 5. 关闭服务器 客户方: 1. 打开一通信通道,并连接到服务器所在主机的特定端口; 2. 向服务器发服务请求报文,等待并接收应答;继续提出请求...... 3. 请求结束后关闭通信通道并终止。 从上面所描述过程可知: 1. 客户与服务器进程的作用是非对称的,因此编码不同。 2. 服务进程一般是先涌纪纪户请求而启动的。只要系统运行,该服务进程一直存在,直到正常或强迫终止。 即Half-duplexCommunication。这种通信方式可以实现双向的通信,但不能在两个方向上同时进行,必须轮流交替地进行。也就是说,通信信道的每一段都可以是发送端,也可以是接收端。但同一时刻里,信息只能有一个传输方向。如日常生活中的例子有步话机通信,对讲机等。 半双工传输的协议是称为线路规程的过程的一部分,它是OSI模型的第二层,数据链路层所包含的一项功能 CFileDialog LPCTSTRlpszDefExt = NULL, LPCTSTRlpszFileName = NULL, DWORDdwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTRlpszFilter = NULL, CWnd*pParentWnd = NULL , DWORDdwSize = 0 dwSize The size of the OPENFILENAME structure. This value is dependent onthe operating system version, so MFC can determine the appropriate kind ofdialog box to create (for example, new Windows 2000 dialogs as opposed to NT4dialogs). The default size of 0 means that the MFC code will determine theproper dialog box size to use based on the operating system version on whichthe program is run. //dlg.m_ofn.lpstrInitialDir=_T("d:\\"); //这里就设置了对话框的默认目录d盘 if(dlg.DoModal()==IDOK) setsockopt的各种使用 2011-05-27 09:55 2028人阅读 评论(0) 收藏 举报 mfcfilewindowsc磁盘api CFile类提供了对文件进行打开,关闭,读,写,删除,重命名以及获取文件信息等文件操作的基本功能,足以处理任意类型的文件操作 虽然使用CArchive类内建的序列化功能是保存和加载持久性数据的便捷方式,但有时在程序中需要对文件处理过程拥有更多的控制权,对于这种文件输入输出(I/O)服务的需求,Windows提供了一系列相关的API函数,并由MFC将其封装为CFile类,提供了对文件进行打开,关闭,读,写,删除,重命名以及获取文件信息等文件操作的基本功能,足以处理任意类型的文件操作。CFile类是MFC文件类的基类,支持无缓冲的二进制输入输出,也可以通过与CArchive类的配合使用而支持对MFC对象的带缓冲的序列化。 CFile类包含有一个公有型数据成员m_hFile,该数据成员包含了同CFile类对象相关联的文件句柄。如果没有指定句柄,则该值为CFile::hFileNull。由于该数据成员所包含的意义取决于派生的类,因此一般并不建议使用m_hFile。 通过CFile类来打开文件可以采取两种方式:一种方式是先构造一个CFile类对象然后再调用成员函数Open()打开文件,另一种方式则直接使用CFile类的构造函数去打开一个文件。下面的语句分别演示了用这两种方法打开磁盘文件“C:/TestFile.txt”的过程: // 先构造一个实例,然后再打开文件 其中参数CFile::modeReadWrite是打开文件的模式标志,CFile类中与之类似的标志还有十几个,现集中列表如下: 文件模式标志说明 这些标志可以通过“或”运算符而同时使用多个,并以此来满足多种需求。例如,需要以读写方式打开文件,如果文件不存在就创建一个新的,如果文件已经存在则不将其文件长度截断为0。为满足此条件,可用CFile::modeCreate、CFile::modeReadWrite和CFile::modeNoTruncate等几种文件模式标志来打开文件: CFilefile ("C://TestFile.txt", CFile::modeCreate | CFile::modeReadWrite |CFile::modeNoTruncate); 在打开的文件不再使用时需要将其关闭,即可以用成员函数Close()关闭也可以通过CFile类的析构函数来完成。当采取后一种方式时,如果文件还没有被关闭,析构函数将负责隐式调用Close()函数去关闭文件,这也表明创建在堆上的CFile类对象在超出范围后将自动被关闭。由于调用了对象的析构函数,因此在文件被关闭的同时CFile对象也被销毁,而采取Close()方式关闭文件后,CFile对象仍然存在。所以,在显式调用Close()函数关闭一个文件后可以继续用同一个CFile对象去打开其他的文件。 文件读写是最常用的文件操作方式,主要由CFile类成员函数Read()、Write()来实现。其函数原型分别为: UINTRead( void* lpBuf, UINT nCount ); 参数lpBuf为指向存放数据的缓存的指针,nCount为要读入或写入的字节数,Read()返回的为实际读取的字节数,该数值小于或等于nCount,如果小于nCount则说明已经读到文件末尾,可以结束文件读取,如继续读取,将返回0。因此通常可以将实际读取字节数是否小于指定读取的字节数或是否为0作为判断文件读取是否到达结尾的依据。下面这段代码演示了对文件进行一次性写入和循环多次读取的处理过程: // 创建、写入方式打开文件 Write()和Read()函数执行完后将自动移动文件指针,因此不必再显示调用Seek()函数去定位文件指针。包含有文件定位函数的完整代码如下所示: // 创建、写入方式打开文件 补充: 使用CFile类对文件进行按结构读取,如: CFilefileRead,fileWrite; VIDEOHEADER *videoheader=new VIDEOHEADER(); charbuf[sizeof(VIDEOHEADER)*8]; · CCriticalSection 临界区:在用户模式工作 ( 遇到加锁等待时会进入内核模式 ) ,使用与保护线程间共享资源,一个线程可以多次 Lock 不会错。不支持在多进程之间工作。将一段代码置入临界区,只允许最多一个线程进入执行这段代码。一个临界区仅在创建它的进程中有效。 · CMutex 互斥量:在内核模式工作,除支持临界区的功能外,还可以为互斥量命名,以便在多进程中工作。互斥量比临界区耗资源。一个时刻至多只允许一个线程访问某资源,未被占用时处于有信号状态,可以实现对共享资源的互斥访问。 · CEvent 事件:在内核模式工作,适用于某一线程等待某事件发生才执行的场合。 · CSemaphore 信号量 ( 信号灯 ) :在内核模式工作,适用于允许特定个数的线程执行某任务。允许一定数目的线程访问某个共享资源,常用来控制访问共享资源的线程数量。 · 1.如果某个线程必须等待某些事件发生后才能存取相应的资源,则用 CEvent · 2.如果一个应用程序同时可以有多个线程存取相应资源,则用 CSemaphore · 3.如果多个应用程序 ( 多个线程 ) 同时存取相应资源,则用 CMutex ,否则用CCriticalSection 一个 CSingleLock 类对象代表一种访问控制机制,这种机制用于控制在一个多线程程序中对一个资源的访问。为了使用同步类 CSemaphore 、 CMutex 、 CCriticalSection 、CEvent 所创建的同步对象,你必须创建一个 CSingleLock 或者 CMultiLock 对象来等待和释放这个同步对象。当你只需要每次等待一个对象时,则用 CsingleLock ,否则用CMultiLock 。 要使用一个 CSingleLock 对象,在被控制资源的类中的一个成员函数内部调用CSingleLock 的构造函数。然后调用 IsLock 成员函数来确定这个资源是否可用。如果资源是可用的,则继续该成员函数的其余部分。 在 MFC 中,具有等待功能的类 CSingleLock 和 CMultiLock 封装了 Win32 中的等待函数WaitForSingleObject() 和 WaitForMultipleObjects() 在嵌入式QT版本中,程序需要服务器或自己作为服务器程序。服务器程序构造的方法是构造一个QApplication::GuiServe类型的QApplication对象。或者使用-qws命令选项启动程序。 Using a Single Display 使用-qws选项 接下来的程序可以当做客户端来运行,只要不使用-qws选项。那么客户端程序就会自动连接到服务程序中。 using Mutiple Displays 嵌入式版本中运行多个县市同时运行。两种方式可以实现,要么多次运行服务程序,要么使用read-mae Multi screen driver 当多个服务程序运行时,每个程序必须使用-display选项指定显示驱动,或者使用QWS_DISPLAY环境变量来指定。 服务程序运行时: ./mysecondserverapplication -qws -display"QVFb:2" 客户程序运行时: ./myclientapplication -display"QVFb:2" 若想在不同显示器移动应用程序,则只能通过Muti显示器实现。 ./myserverapplication -qws -display"Multi: QVFb:0 QVFb:1:offset=0,0 VNC:offset=640,0 :2" 程序启动命令选项: -fn 定义程序的字体,例如./myapplication -fn helvetica -bg -btn -fg -name -title -geometry 设置窗口大小, ./myapplication-geometry 300x200+50+50 -keyboard 启动键盘 -nokeyboard 关闭键盘 -mouse 启动鼠标 -nomouse 关闭鼠标 -qws 设置为服务程序 -display 设置显示器驱动 -decoration 设置程序的风格,例如./myapplication-decoration windows,只支持windows default styled 2011-04-29 18:31:41| 分类: 教程技巧|字号 订阅 经常在一行之后回车,或者在if else之后回车,光标不是和上一行对齐,而是向后移动了4个空格(如果是设置是4的话),这样看起来让人很不爽, 自动缩进的修改点,有三个地方,作用不一,自己去体会 1.高级-》配置-》编辑器显示-》格式化 2.找到wordfile.uew 文件进行修改, 一般wordfile.uew 文件的目录是C:\Documents andSettings\***\Application Data\IDMComp\UltraEdit\而不是你的安装目录 把 if, else, :去掉就可以了 然后重新启动ultraedit. 3.艺术样式 http://hi.baidu.com/begin_think/blog/item/a40f4a2eeb9af7341e308976.html 工作的时候,要经常拿到别人的代码来改,有些人不太注重编程风格,代码看上去很乱,所以不得不用一些自动排格式的软件先来“格式化”一下,以前我总用VC来排,可是用VC始终感觉太麻烦,而且它的排版功能太死板了,跟自己的风格有些差别,所以想到用UE来试试,没想到真的能改出和自己编程的风格类似的代码。下面就说说我是如何设置UE让它能按我的想法来排版的: 在网上搜索函数指针,看到一个例子。开始没看懂,想放弃,可是转念一想,这个用法迟早要弄懂的,现在多花点时间看懂它,好过以后碰到了要再花一倍时间来弄懂它。其实很多时候都是这样,如果每次到难一点的内容,总想着下次我再来解决它,那就永远也学不到东西。 后面那个例子加了注释,是我对这种用法的理解,希望对新手有所帮助。 进入正文: 代码简化, 促进跨平台开发的目的. typedef 行为有点像 #define 宏,用其实际类型替代同义字。 不同点:typedef 在编译时被解释,因此让编译器来应付超越预处理器能力的文本替换。 用法一: typedef int (*MYFUN)(int, int); 用法二: typedef给变量类型定义一个别名. typedef struct{ 第二种用法:typedef 原变量类型 别名 简单的函数指针的用法 //形式1:返回类型(*函数名)(参数表) char(*pFun)(int); //typedef char(*pFun)(int) //跟上一行功能等同 /*typedef的功能是定义新的类型。第一句就是定义了一种PTRFUN的类型,并定义这种类型为指向某种函数的指针,这种函数以一个int为参数并返回char类型。*/ char glFun(int a){return;} void main() { pFun =glFun; (*pFun)(2); } 第一行定义了一个指针变量pFun.它是一个指向某种函数的指针,这种函数参数是一个int类型,返回值是char类型。只有第一句我们还无法使用这个指针,因为我们还未对它进行赋值。 第二行定义了一个函数glFun().该函数正好是一个以int为参数返回char的函数。我们要从指针的层次上理解函数-函数的函数名实际上就是一个指针,函数名指向该函数的代码在内存中的首地址。 下面是一个例子: C代码 1. //#include 2. #include 3. 4. typedef int (*FP_CALC)(int, int); 5. //注意这里不是函数声明而是函数定义,它是一个地址,你可以直接输出add看看 6. int add(int a, int b) 7. { 8. return a + b; 9. } 10. int sub(int a, int b) 11. { 12. return a - b; 13. } 14. int mul(int a, int b) 15. { 16. return a * b; 17. } 18. int div(int a, int b) 19. { 20. return b? a/b : -1; 21. } 22. //定义一个函数,参数为op,返回一个指针。该指针类型为拥有两个int参数、 23. //返回类型为int 的函数指针。它的作用是根据操作符返回相应函数的地址 24. FP_CALC calc_func(char op) 25. { 26. switch (op) 27. { 28. case '+': return add;//返回函数的地址 29. case '-': return sub; 30. case '*': return mul; 31. case '/': return div; 32. default: 33. return NULL; 34. } 35. return NULL; 36. } 37. //s_calc_func为函数,它的参数是 op, 38. //返回值为一个拥有两个int参数、返回类型为int 的函数指针 39. int (*s_calc_func(char op)) (int, int) 40. { 41. return calc_func(op); 42. } 43. //最终用户直接调用的函数,该函数接收两个int整数,和一个算术运算符,返回两数的运算结果 44. int calc(int a, int b, char op) 45. { 46. FP_CALC fp =calc_func(op); //根据预算符得到各种运算的函数的地址 47. int (*s_fp)(int, int) = s_calc_func(op);//用于测试 48. // ASSERT(fp ==s_fp); // 可以断言这俩是相等的 49. if (fp) return fp(a, b);//根据上一步得到的函数的地址调用相应函数,并返回结果 50. else return -1; 51. } 52. 53. void main() 54. { 55. int a = 100, b = 20; 56. 57. printf("calc(%d, %d, %c)= %d\n", a, b, '+', calc(a, b, '+')); 58. printf("calc(%d, %d, %c)= %d\n", a, b, '-', calc(a, b, '-')); 59. printf("calc(%d, %d, %c)= %d\n", a, b, '*', calc(a, b, '*')); 60. printf("calc(%d, %d, %c)= %d\n", a, b, '/', calc(a, b, '/')); 61. } //#include #include typedef int(*FP_CALC)(int, int); //注意这里不是函数声明而是函数定义,它是一个地址,你可以直接输出add看看 int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } int mul(int a, int b) { return a * b; } int div(int a, int b) { return b? a/b : -1; } //定义一个函数,参数为op,返回一个指针。该指针类型为 拥有两个int参数、 //返回类型为int 的函数指针。它的作用是根据操作符返回相应函数的地址 FP_CALCcalc_func(char op) { switch (op) { case '+': returnadd;//返回函数的地址 case '-': return sub; case '*': return mul; case '/': return div; default: return NULL; } return NULL; } //s_calc_func为函数,它的参数是 op, //返回值为一个拥有 两个int参数、返回类型为int 的函数指针 int(*s_calc_func(char op)) (int, int) { return calc_func(op); } //最终用户直接调用的函数,该函数接收两个int整数,和一个算术运算符,返回两数的运算结果 int calc(int a, intb, char op) { FP_CALC fp =calc_func(op); //根据预算符得到各种运算的函数的地址 int (*s_fp)(int, int)= s_calc_func(op);//用于测试 // ASSERT(fp ==s_fp); // 可以断言这俩是相等的 if (fp) return fp(a,b);//根据上一步得到的函数的地址调用相应函数,并返回结果 else return -1; } void main() { int a = 100, b = 20; printf("calc(%d,%d, %c) = %d\n", a, b, '+', calc(a, b, '+')); printf("calc(%d,%d, %c) = %d\n", a, b, '-', calc(a, b, '-')); printf("calc(%d,%d, %c) = %d\n", a, b, '*', calc(a, b, '*')); printf("calc(%d,%d, %c) = %d\n", a, b, '/', calc(a, b, '/')); } 运行结果 calc(100, 20, +) = 120 calc(100, 20, -) = 80 calc(100, 20, *) = 2000 calc(100, 20, /) = 5 来自: http://hi.baidu.com/%D6%EC%CF%E9/blog/item/482290cb4b6dfeed53664fda.html 个人总结:最近看网络编程是碰到了TRACE语句,不知道在哪里输出,查了一晚上资料也没找出来,今天终于在CSDN上找到了,真是个高地方啊,方法如下: 1.在MFC中加入TRACE语句 2.在TOOLS->MFCTRACER中选择“ENABLE TRACING”点击OK 3.进行调试运行,GO(F5)(特别注意:不是执行‘!’以前之所以不能看到TRACE内容,是因为不是调试执行,而是‘!’了,切记,切记) 4.然后就会在OUTPUT中的DEBUG窗口中看到TRACE内容了,调试执行会自动从BUILD窗口跳到DEBUG窗口,在那里就看到TRACE的内容了,^_^ 以下是找的TRACE的详细介绍: ============================== TRACE宏对于VC下程序调试来说是很有用的东西,有着类似printf的功能;该宏仅仅在程序的DEBUG版本中出现,当RELEASE的时候该宏就完全消息了,从而帮助你调式也在RELEASE的时候减少代码量。 使用非常简单,格式如下: TRACE("DDDDDDDDDDD"); TRACE("wewe%d",333); 同样还存在TRACE0,TRACE1,TRACE2。。。分别对应0,1,2。。个参数 TRACE信息输出到VC IDE环境的输出窗口(该窗口是你编译项目出错提示的哪个窗口),但仅限于你在VC中运行你的DEBUG版本的程序。 TRACE信息还可以使用DEBUGVIEW来捕获到。这种情况下,你不能在VC的IDE环境中运行你的程序,而将BUILD好的DEBUG版本的程序单独运行,这个时候可以在DEBUGVIEW的窗口看到DEBUGVIE格式的输出了。 VC中TRACE的用法有以下四种: 1: TRACE ,就是不带动态参数输出字符串, 类似C的printf("输出字符串"); TRACE 中的字符串可以带一个参数输出 , 类似C的printf("...%d",变量); 3: TRACE 可以带两个参数输出,类似C的printf("...%d...%f",变量1,变量2); 4: TRACE 可以带三个参数输出,类似C的printf("...%d,%d,%d",变量1,变量2,变量3); TRACE 宏有点象我们以前在C语言中用的Printf函数,使程序在运行过程中输出一些调试信息,使我们能了解程序的一些状态。但有一点不同的是: int x = 1; 要注意的是TRACE宏只对Debug 版本的工程产生作用,在Release版本的工程中,TRACE宏将被忽略。 linux定时器的使用使用定时器的目的无非是为了周期性的执行某一任务,或者是到了一个指定时间去执行某一个任务。要达到这一目的,一般有两个常见的比较有效的方法。一个是用linux内部的三个定时器,另一个是用sleep,usleep函数让进程睡眠一段时间,其实,还有一个方法,那就是用gettimeofday,difftime等自己来计算时间间隔,然后时间到了就执行某一任务,但是这种方法效率低,所以不常用。 首先来看看linux操作系统为每一个进程提供的3个内部计时器。 ITIMER_REAL: 给一个指定的时间间隔,按照实际的时间来减少这个计数,当时间间隔为0的时候发出SIGALRM信号 ITIMER_VIRTUAL: 给定一个时间间隔,当进程执行的时候才减少计数,时间间隔为0的时候发出SIGVTALRM信号 ITIMER_PROF: 给定一个时间间隔,当进程执行或者是系统为进程调度的时候,减少计数,时间到了,发出SIGPROF信号,这个和ITIMER_VIRTUAL联合,常用来计算系统内核时间和用户时间。 用到的函数有: #include it_interval用来指定每隔多长时间执行任务,it_value用来保存当前时间离执行任务还有多长时间。比如说,你指定it_interval为2秒(微秒为0),开始的时候我们把it_value的时间也设定为2秒(微秒为0),当过了一秒,it_value就减少一个为1,再过1秒,则it_value又减少1,变为0,这个时候发出信号(告诉用户时间到了,可以执行任务了),并且系统自动把it_value的时间重置为it_interval的值,即2秒,再重新计数。 为了帮助你理解这个问题,我们来看一个例子: 1. #include 2. #include 3. #include 4. #include 5. #include 6. 7. static char msg[] = “time isrunning out\n”; 8. static int len; 9. 10. // 向标准错误输出信息,告诉用户时间到了 11. void prompt_info(int signo) 12. { 13. write(STDERR_FILENO, msg,len); 14. } 15. 16. // 建立信号处理机制 17. void init_sigaction(void) 18. { 19. struct sigaction tact; 20. 21. /*信号到了要执行的任务处理函数为prompt_info*/ 22. tact.sa_handler = prompt_info; 23. tact.sa_flags = 0; 24. /*初始化信号集*/ 25. sigemptyset(&tact.sa_mask); 26. /*建立信号处理机制*/ 27. sigaction(SIGALRM, &tact,NULL); 28. } 29. 30. void init_time() 31. { 32. struct itimerval value; 33. 34. /*设定执行任务的时间间隔为2秒0微秒*/ 35. value.it_value.tv_sec = 2; 36. value.it_value.tv_usec = 0; 37. /*设定初始时间计数也为2秒0微秒*/ 38. value.it_interval =value.it_value; 39. /*设置计时器ITIMER_REAL*/ 40. setitimer(ITIMER_REAL,&value, NULL); 41. } 42. 43. int main() 44. { 45. len = strlen(msg); 46. init_sigaction(); 47. init_time(); 48. while ( 1 ); 49. exit(0); 50. } 该程序的ITMER_REAL定时器,每隔2秒钟都会发送一个SIGALRM信号,当主函数接收到了这个信号之后,调用信号处理函数prompt_info在标准错误上输出timeis running out这个字符串。 对于ITIMER_VIRTUAL和ITIMER_PROF的使用方法类似,当你在setitimer里面设置的定时器为ITIMER_VIRTUAL的时候,你把sigaction里面的SIGALRM改为SIGVTALARM,同理,ITIMER_PROF对应SIGPROF。 不过,你可能会注意到,当你用ITIMER_VIRTUAL和ITIMER_PROF的时候,你拿一个秒表,你会发现程序输出字符串的时间间隔会不止2秒,甚至5-6秒才会输出一个,至于为什么,自己好好琢磨一下^_^ 下面我们来看看用sleep以及usleep怎么实现定时执行任务。 1. #include 2. #include 3. #include 4. #include 5. 6. static char msg[] = “Ireceived a msg.\n”; 7. int len; 8. void show_msg(int signo) 9. { 10. write(STDERR_FILENO, msg,len); 11. } 12. int main() 13. { 14. struct sigaction act; 15. union sigval tsval; 16. 17. act.sa_handler = show_msg; 18. act.sa_flags = 0; 19. sigemptyset(&act.sa_mask); 20. sigaction(50, &act, NULL); 21. 22. len = strlen(msg); 23. while ( 1 ) 24. { 25. sleep(2); /*睡眠2秒*/ 26. /*向主进程发送信号,实际上是自己给自己发信号*/ 27. sigqueue(getpid(), 50, tsval); 28. } 29. return 0; 30. } 看到了吧,这个要比上面的简单多了,而且你用秒表测一下,时间很准,指定2秒到了就给你输出一个字符串。所以,如果你只做一般的定时,到了时间去执行一个任务,这种方法是最简单的。 下面我们来看看,通过自己计算时间差的方法来定时: 1. #include 2. #include 3. #include 4. #include 5. #include 6. 7. static char msg[] = “Ireceived a msg.\n”; 8. int len; 9. static time_t lasttime; 10. void show_msg(int signo) 11. { 12. write(STDERR_FILENO, msg,len); 13. } 14. int main() 15. { 16. struct sigaction act; 17. union sigval tsval; 18. 19. act.sa_handler = show_msg; 20. act.sa_flags = 0; 21. sigemptyset(&act.sa_mask); 22. sigaction(50, &act, NULL); 23. 24. len = strlen(msg); 25. time(&lasttime); 26. while ( 1 ) 27. { 28. time_t nowtime; 29. /*获取当前时间*/ 30. time(&nowtime); 31. /*和上一次的时间做比较,如果大于等于2秒,则立刻发送信号*/ 32. if (nowtime – lasttime >=2) 33. { 34. /*向主进程发送信号,实际上是自己给自己发信号*/ 35. sigqueue(getpid(), 50, tsval); 36. lasttime = nowtime; 37. } 38. } 39. return 0; 40. } 这个和上面不同之处在于,是自己手工计算时间差的,如果你想更精确的计算时间差,你可以把time 函数换成gettimeofday,这个可以精确到微妙。 上面介绍的几种定时方法各有千秋,在计时效率上、方法上和时间的精确度上也各有不同,采用哪种方法,就看你程序的需要 itimerval时钟的使用#include 来源:芽雨快跑 时间: 2009-02-09 18:07:41 浏览: 102518 次 评论: 1853 篇 Tags : df du 文件大小 当磁盘大小超过标准时会有报警提示,这时如果掌握df和du命令是非常明智的选择。 df可以查看一级文件夹大小、使用比例、档案系统及其挂入点,但对文件却无能为力。 两者配合使用,非常有效。比如用df查看哪个一级目录过大,然后用df查看文件夹或文件的大小,如此便可迅速确定症结。 下面分别简要介绍 df命令可以显示目前所有文件系统的可用空间及使用情形,请看下列这个例子: 以下是代码片段: [yayug@yayu ~]$ df -h 参数 -h 表示使用「Human-readable」的输出,也就是在档案系统大小使用 GB、MB 等易读的格式。 上面的命令输出的第一个字段(Filesystem)及最后一个字段(Mounted on)分别是档案系统及其挂入点。我们可以看到 /dev/sda1 这个分割区被挂在根目录下。 接下来的四个字段 Size、Used、Avail、及 Use% 分别是该分割区的容量、已使用的大小、剩下的大小、及使用的百分比。 FreeBSD下,当硬盘容量已满时,您可能会看到已使用的百分比超过 100%,因为 FreeBSD 会留一些空间给 root,让 root 在档案系统满时,还是可以写东西到该档案系统中,以进行管理。 du:查询文件或文件夹的磁盘使用空间 如果当前目录下文件和文件夹很多,使用不带参数du的命令,可以循环列出所有文件和文件夹所使用的空间。这对查看究竟是那个地方过大是不利的,所以得指定深入目录的层数,参数:--max-depth=,这是个极为有用的参数!如下,注意使用“*”,可以得到文件的使用空间大小. 提醒:一向命令比linux复杂的FreeBSD,它的du命令指定深入目录的层数却是比linux简化,为 -d。 以下是代码片段: [root@bsso yayu]# du -h --max-depth=1 work/testing [root@bsso yayu]# du -h --max-depth=1 work/testing/* [root@bsso yayu]# du -h --max-depth=1 work/testing/logs/ [root@bsso yayu]# du -h --max-depth=1 work/testing/logs/* 值得注意的是,看见一个针对du和df命令异同的文章:《du df 差异导致文件系统误报解决》。 du 统计文件大小相加 如果有一个进程在打开一个大文件的时候,这个大文件直接被rm 或者mv掉,则du会更新统计数值,df不会更新统计数值,还是认为空间没有释放。直到这个打开大文件的进程被Kill掉。 如此一来在定期删除/var/spool/clientmqueue下面的文件时,如果没有杀掉其进程,那么空间一直没有释放。 使用下面的命令杀掉进程之后,系统恢复。 du查看目录大小,df查看磁盘使用情况。 1,ls 分类: linux2011-08-2610:41 4390人阅读 评论(8) 收藏 举报 threadpthreadsstructnulljoin 目录(?)[+] 线程具有属性,用pthread_attr_t表示,在对该结构进行处理之前必须进行初始化,在使用后需要对其去除初始化。我们用pthread_attr_init函数对其初始化,用pthread_attr_destroy对其去除初始化。 1. 名称:: pthread_attr_init/pthread_attr_destroy 功能: 对线程属性初始化/去除初始化 头文件: #include 函数原形: int pthread_attr_init(pthread_attr_t*attr); int pthread_attr_destroy(pthread_attr_t*attr); 参数: Attr 线程属性变量 返回值: 若成功返回0,若失败返回-1。 调用pthread_attr_init之后,pthread_t结构所包含的内容就是操作系统实现支持的线程所有属性的默认值。 如果要去除对pthread_attr_t结构的初始化,可以调用pthread_attr_destroy函数。如果pthread_attr_init实现时为属性对象分配了动态内存空间,pthread_attr_destroy还会用无效的值初始化属性对象,因此如果经pthread_attr_destroy去除初始化之后的pthread_attr_t结构被pthread_create函数调用,将会导致其返回错误。 线程属性结构如下: typedefstruct { int detachstate; 线程的分离状态 int schedpolicy; 线程调度策略 structsched_param schedparam; 线程的调度参数 int inheritsched; 线程的继承性 int scope; 线程的作用域 size_t guardsize; 线程栈末尾的警戒缓冲区大小 int stackaddr_set; void* stackaddr; 线程栈的位置 size_t stacksize; 线程栈的大小 }pthread_attr_t; 每个个属性都对应一些函数对其查看或修改。下面我们分别介绍。 线程的分离状态决定一个线程以什么样的方式来终止自己。在默认情况下线程是非分离状态的,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。 而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。所以如果我们在创建线程时就知道不需要了解线程的终止状态,则可以pthread_attr_t结构中的detachstate线程属性,让线程以分离状态启动。 2. 名称:: pthread_attr_getdetachstate/pthread_attr_setdetachstate 功能: 获取/修改线程的分离状态属性 头文件: #include 函数原形: int pthread_attr_getdetachstate(const pthread_attr_t *attr,int *detachstate); int pthread_attr_setdetachstate(pthread_attr_t *attr,intdetachstate); 参数: Attr 线程属性变量 Detachstate 线程的分离状态属性 返回值: 若成功返回0,若失败返回-1。 可以使用pthread_attr_setdetachstate函数把线程属性detachstate设置为下面的两个合法值之一:设置为PTHREAD_CREATE_DETACHED,以分离状态启动线程;或者设置为PTHREAD_CREATE_JOINABLE,正常启动线程。可以使用pthread_attr_getdetachstate函数获取当前的datachstate线程属性。 以分离状态创建线程 #iinclude void *child_thread(void *arg) { printf(“child thread run!\n”); } int main(int argc,char *argv[ ]) { pthread_ttid; pthread_attr_tattr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); pthread_create(&tid,&attr,fn,arg); pthread_attr_destroy(&attr); sleep(1); } 函数pthread_attr_setinheritsched和pthread_attr_getinheritsched分别用来设置和得到线程的继承性,这两个函数的定义如下: 3. 名称:: pthread_attr_getinheritsched pthread_attr_setinheritsched 功能: 获得/设置线程的继承性 头文件: #include 函数原形: int pthread_attr_getinheritsched(const pthread_attr_t*attr,int *inheritsched); int pthread_attr_setinheritsched(pthread_attr_t *attr,intinheritsched); 参数: attr 线程属性变量 inheritsched 线程的继承性 返回值: 若成功返回0,若失败返回-1。 这两个函数具有两个参数,第1个是指向属性对象的指针,第2个是继承性或指向继承性的指针。继承性决定调度的参数是从创建的进程中继承还是使用在schedpolicy和schedparam属性中显式设置的调度信息。Pthreads不为inheritsched指定默认值,因此如果你关心线程的调度策略和参数,必须先设置该属性。 继承性的可能值是PTHREAD_INHERIT_SCHED(表示新现成将继承创建线程的调度策略和参数)和PTHREAD_EXPLICIT_SCHED(表示使用在schedpolicy和schedparam属性中显式设置的调度策略和参数)。 如果你需要显式的设置一个线程的调度策略或参数,那么你必须在设置之前将inheritsched属性设置为PTHREAD_EXPLICIT_SCHED. 下面我来讲进程的调度策略和调度参数。我会结合下面的函数给出本函数的程序例子。 函数pthread_attr_setschedpolicy和pthread_attr_getschedpolicy分别用来设置和得到线程的调度策略。 4. 名称:: pthread_attr_getschedpolicy pthread_attr_setschedpolicy 功能: 获得/设置线程的调度策略 头文件: #include 函数原形: int pthread_attr_getschedpolicy(const pthread_attr_t*attr,int *policy); int pthread_attr_setschedpolicy(pthread_attr_t *attr,intpolicy); 参数: attr 线程属性变量 policy 调度策略 返回值: 若成功返回0,若失败返回-1。 这两个函数具有两个参数,第1个参数是指向属性对象的指针,第2个参数是调度策略或指向调度策略的指针。调度策略可能的值是先进先出(SCHED_FIFO)、轮转法(SCHED_RR),或其它(SCHED_OTHER)。 SCHED_FIFO策略允许一个线程运行直到有更高优先级的线程准备好,或者直到它自愿阻塞自己。在SCHED_FIFO调度策略下,当有一个线程准备好时,除非有平等或更高优先级的线程已经在运行,否则它会很快开始执行。 SCHED_RR(轮循)策略是基本相同的,不同之处在于:如果有一个SCHED_RR 策略的线程执行了超过一个固定的时期(时间片间隔)没有阻塞,而另外的SCHED_RR或SCHBD_FIPO策略的相同优先级的线程准备好时,运行的线程将被抢占以便准备好的线程可以执行。 当有SCHED_FIFO或SCHED_RR策赂的线程在一个条件变量上等持或等持加锁同一个互斥量时,它们将以优先级顺序被唤醒。即,如果一个低优先级的SCHED_FIFO线程和一个高优先织的SCHED_FIFO线程都在等待锁相同的互斥且,则当互斥量被解锁时,高优先级线程将总是被首先解除阻塞。 函数pthread_attr_getschedparam 和pthread_attr_setschedparam分别用来设置和得到线程的调度参数。 5. 名称:: pthread_attr_getschedparam pthread_attr_setschedparam 功能: 获得/设置线程的调度参数 头文件: #include 函数原形: int pthread_attr_getschedparam(const pthread_attr_t*attr,struct sched_param *param); int pthread_attr_setschedparam(pthread_attr_t *attr,conststruct sched_param *param); 参数: attr 线程属性变量 param sched_param结构 返回值: 若成功返回0,若失败返回-1。 这两个函数具有两个参数,第1个参数是指向属性对象的指针,第2个参数是sched_param结构或指向该结构的指针。结构sched_param在文件/usr/include/bits/sched.h中定义如下: struct sched_param { intsched_priority; }; 结构sched_param的子成员sched_priority控制一个优先权值,大的优先权值对应高的优先权。系统支持的最大和最小优先权值可以用sched_get_priority_max函数和sched_get_priority_min函数分别得到。 注意:如果不是编写实时程序,不建议修改线程的优先级。因为,调度策略是一件非常复杂的事情,如果不正确使用会导致程序错误,从而导致死锁等问题。如:在多线程应用程序中为线程设置不同的优先级别,有可能因为共享资源而导致优先级倒置。 _vsnprintf,C语言库函数之一,属于可变参数。用于向字符串中打印数据、数据格式用户自定义。 1函数简介 2用法实例 头文件: #include 函数声明: 参数说明: 1. char *str [out],把生成的格式化的字符串存放在这里. 2. size_t size [in], buffer可接受的最大字节数,防止产生数组越界. 3. const char *format [in], 指定输出格式的字符串,它决定了你需要提供的可变参数的类型、个数和顺序。 4. va_list ap [in],va_list变量.va:variable-argument:可变参数 函数功能:将可变参数格式化输出到一个字符数组。 用法类似于vsprintf,不过加了size的限制,防止了内存溢出(size为str所指的存储空间的大小)。 返回值:执行成功,返回写入到字符数组str中的字符个数(不包含终止符),最大不超过size;执行失败,返回负值,并置errno.[1] 备注: linux环境下是:vsnprintf VC6环境下是:_vsnprintf int mon_log(char* format, ...) { va_listvArgList; //定义一个va_list型的变量,这个变量是指向参数的指针. va_start(vArgList, format); //用va_start宏初始化变量,这个宏的第二个参数是第一个可变参数的前一个参数,是一个固定的参数. _vsnprintf(str_tmp, 3, format, vArgList);//注意,不要漏掉前面的_ va_end(vArgList); //用va_end宏结束可变参数的获取 return 0; } //调用上面的函数 mon_log("%d,%d,%d,%d", 1,2,3,4); 返回值用法: #include #include #include char * make_message(const char *fmt, ...) { /* 初始时假设我们只需要不超过100字节大小的空间 */ int n, size = 100; char *p; va_list ap; if ((p = (char *)malloc(size)) == NULL) return NULL; while (1) { /* 尝试在申请的空间中进行打印操作 */ va_start(ap, fmt); n = vsnprintf (p, size, fmt, ap); va_end(ap); /* 如果vsnprintf调用成功,返回该字符串*/ if (n > -1 && n < size) return p; /* vsnprintf调用失败(n<0)或者p的空间不足够容纳size大小的字符串(n>=size),尝试申请更大的空间*/ size *= 2; /* 两倍原来大小的空间 */ if ((p = (char *)realloc(p, size)) == NULL) return NULL; } } int main() { /* 调用上面的函数 */ char* str = make_message("%d,%d,%d,%d",5,6,7,8); printf("%s\n",str); free(str); // we allocate the memory in the make_messagefunction, so we should release it by caller(main function). return 0; } 1基本结构 2参数说明 3经典案例 在windows/linux下有下面结构: sockaddr结构 struct sockaddr { unsigned short sa_family; /* address family, AF_xxx */ char sa_data[14]; /* 14 bytes of protocol address */ }; sa_family是地址家族,一般都是“AF_xxx”的形式。通常大多用的是都是AF_INET,代表TCP/IP协议族。 sa_data是14字节协议地址。 此数据结构用做bind、connect、recvfrom、sendto等函数的参数,指明地址信息。但一般编程中并不直接针对此数据结构操作,而是使用另一个与sockaddr等价的数据结构 sockaddr_in(在netinet/in.h中定义): struct sockaddr_in { short sin_family; /* Address family */ unsigned short sin_port; /* Port number */ struct in_addr sin_addr; /* Internetaddress */ unsigned char sin_zero[8]; /* Same size as struct sockaddr */ }; 在linux下: in_addr结构 typedef struct in_addr{ unsigned long s_addr; }; 在windows下: typedef struct in_addr{ union{ struct{unsigned char s_b1,s_b2,s_b3,s_b4;} S_un_b; struct{unsigned short s_w1,s_w2;} S_un_w; unsigned long S_addr; } S_un; } IN_ADDR; sin_family指代协议族,在socket编程中只能是AF_INET sin_port存储端口号(使用网络字节顺序),在linux下,端口号的范围0~65535,同时0~1024范围的端口号已经被系统使用或保留。 sin_addr存储IP地址,使用in_addr这个数据结构 sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。 s_addr按照网络字节顺序存储IP地址 sockaddr_in和sockaddr是并列的结构,指向sockaddr_in的结构体的指针也可以指向 sockaddr的结构体,并代替它。也就是说,你可以使用sockaddr_in建立你所需要的信息, 然后用bzero函数初始化就可以了bzero((char*)&mysock,sizeof(mysock));//初始化 sockaddr_in mysock; bzero((char*)&mysock,sizeof(mysock)); mysock.sa_family=AF_INET; mysock.sin_port=htons(1234);//1234是端口号 mysock.sin_addr.s_addr=inet_addr("192.168.0.1"); 相关函数:inet_addr,inet_aton, inet_ntoa, htonl, htons, MAKEWORD,WSASocket, WSAHtons…… 3经典案例 服务端: int main() 客户端: int main() 在写网络程序的时候,建立TCP socket: 相关头文件中的定义:AF = Address Family 所以在windows中AF_INET与PF_INET完全一样. 而在Unix/Linux系统中,在不同的版本中这两者有微小差别.对于BSD,是AF,对于POSIX是PF. 理论上建立socket时是指定协议,应该用PF_xxxx,设置地址时应该用AF_xxxx。当然AF_INET和PF_INET的值是相同的,混用也不会有太大的问题。 在函数socketpair与socket的domain参数中有AF_UNIX,AF_LOCAL,AF_INET,PF_UNIX,PF_LOCAL,PF_INET. 计算机科学中的进程I/O函数,与pclose函数一起使用。 目 录 1头文件 2函数定义 3函数说明 4返回值 5返回错误 6使用举例 7真实示例 1头文件 1 2函数定义 1 2 3函数说明 popen() 函数通过创建一个管道,调用 fork 产生一个子进程,执行一个shell 以运行命令来开启一个进程。这个进程必须由 pclose() 函数关闭,而不是 fclose() 函数。pclose() 函数关闭标准 I/O 流,等待命令执行结束,然后返回 shell 的终止状态。如果 shell 不能被执行,则 pclose() 返回的终止状态与 shell 已执行 exit 一样。 type 参数只能是读或者写中的一种,得到的返回值(标准 I/O 流)也具有和 type 相应的只读或只写类型。如果 type 是 "r" 则文件指针连接到 command 的标准输出;如果 type 是 "w" 则文件指针连接到 command 的标准输入。 command 参数是一个指向以 NULL 结束的 shell 命令字符串的指针。这行命令将被传到 bin/sh 并使用-c 标志,shell 将执行这个命令。 popen 的返回值是个标准 I/O 流,必须由 pclose 来终止。前面提到这个流是单向的。所以向这个流写内容相当于写入该命令的标准输入;命令的标准输出和调用 popen 的进程相同。与之相反的,从流中读数据相当于读取命令的标准输出;命令的标准输入和调用 popen 的进程相同。 4返回值 如果调用 fork() 或 pipe() 失败,或者不能分配内存将返回NULL,否则返回标准 I/O 流。 5返回错误 popen 没有为内存分配失败设置 errno 值。 如果调用 fork() 或 pipe() 时出现错误,errno 被设为相应的错误类型。 如果 type 参数不合法,errno将返回EINVAL。 6使用举例 1 2 3 4 5 6 7 8 9 10 7真实示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 原文: http://apps.hi.baidu.com/share/detail/19786281 http://hi.baidu.com/boobleoo0/blog/item/5f935039a37c58f8b311c77f.html http://topic.csdn.net/u/20110105/16/12717238-9816-4571-a03d-e8b603724946.html pthread_cond_wait() 用于阻塞当前线程,等待别的线程使用pthread_cond_signal()或pthread_cond_broadcast来唤醒它。 pthread_cond_wait() 必须与pthread_mutex 配套使用。pthread_cond_wait()函数一进入wait状态就会自动release mutex。当其他线程通过pthread_cond_signal()或pthread_cond_broadcast,把该线程唤醒,使pthread_cond_wait()通过(返回)时,该线程又自动获得该mutex。 pthread_cond_signal函数的作用是发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行.如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。 使用pthread_cond_signal一般不会有“惊群现象”产生,他最多只给一个线程发信号。假如有多个线程正在阻塞等待着这个条件变量的话,那么是根据各等待线程优先级的高低确定哪个线程接收到信号开始继续执行。如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号。但无论如何一个pthread_cond_signal调用最多发信一次。 但是pthread_cond_signal在多处理器上可能同时唤醒多个线程,当你只能让一个线程处理某个任务时,其它被唤醒的线程就需要继续 wait,而且规范要求pthread_cond_signal至少唤醒一个pthread_cond_wait上的线程,其实有些实现为了简单在单处理器上也会唤醒多个线程. 另外,某些应用,如线程池,pthread_cond_broadcast唤醒全部线程,但我们通常只需要一部分线程去做执行任务,所以其它的线程需要继续wait.所以强烈推荐对pthread_cond_wait() 使用while循环来做条件判断. 以下就是一个来自MAN的示例 Consider two shared variables xand y, protected by the mutex mut, and a condition vari- able condthat is to be signaled whenever x becomes greater than y. int x,y; pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; Waiting untilx is greater than y is performed as follows: pthread_mutex_lock(&mut); while(x <= y){ pthread_cond_wait(&cond,&mut); } /* operate on x and y */ pthread_mutex_unlock(&mut); Modificationson x and y that may cause x to become greater than y should signal the con- dition ifneeded: pthread_mutex_lock(&mut); /* modify x and y */ if(x > y)pthread_cond_broadcast(&cond); pthread_mutex_unlock(&mut); pthread_cond_signal函数与条件变量的典型应用就是用来实现producer/consumer模型。 示例1 #include #include #include #include #define BUFFER_SIZE 8 structProducts { int buffer[BUFFER_SIZE]; /*保证存取操作的原子性 互斥性*/ pthread_mutex_t locker; /*是否可读*/ pthread_cond_t notEmpty; /*是否可写*/ pthread_cond_t notFull; int posReadFrom; int posWriteTo; }; intBufferIsFull(structProducts* products) { if((products->posWriteTo +1)% BUFFER_SIZE == products->posReadFrom) { return(1); } return(0); } intBufferIsEmpty(structProducts* products) { if(products->posWriteTo ==products->posReadFrom) { return(1); } return(0); } /*制造产品*/。 voidProduce(structProducts* products,int item) { /*原子操作*/ pthread_mutex_lock(&products->locker); /*无空间可写入*/ while(BufferIsFull(products)) { pthread_cond_wait(&products->notFull,&products->locker); } /*写入数据*/ products->buffer[products->posWriteTo]= item; products->posWriteTo++; if(products->posWriteTo >=BUFFER_SIZE) products->posWriteTo =0; /*发信*/ pthread_cond_signal(&products->notEmpty); /*解锁*/ pthread_mutex_unlock(&products->locker); } intConsume(structProducts* products) { int item; pthread_mutex_lock(&products->locker); /*为空时持续等待,无数据可读*/ while(BufferIsEmpty(products)) { pthread_cond_wait(&products->notEmpty,&products->locker); } /*提取数据*/ item = products->buffer[products->posReadFrom]; products->posReadFrom++; /*如果到末尾,从头读取*/ if(products->posReadFrom >=BUFFER_SIZE) products->posReadFrom =0; pthread_cond_signal(&products->notFull); pthread_mutex_unlock(&products->locker); return item; } #define END_FLAG (-1) structProducts products; void*ProducerThread(void*data) { int i; for(i =0; i <16;++i) { printf("producer: %d\n", i); Produce(&products, i); } Produce(&products,END_FLAG); return NULL; } void*ConsumerThread(void*data) { int item; while(1) { item =Consume(&products); if(END_FLAG == item) break; printf("consumer: %d\n", item); } return(NULL); } int main(int argc,char* argv[]) { pthread_t producer; pthread_t consumer; int result; pthread_create(&producer, NULL,&ProducerThread,NULL); pthread_create(&consumer,NULL,&ConsumerThread,NULL); pthread_join(producer,(void*)&result); pthread_join(consumer,(void*)&result); exit(EXIT_SUCCESS); } 示例2 pthread_cond_broadcast的是使用 pthread_mutex_t mymutex1 = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t mymutex2 = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t mycond = PTHREAD_COND_INITIALIZER; void*mythread1(void*param) { pthread_mutex_lock(&mymutex1); pthread_cond_wait(&mycond,&mymutex1); fprintf(stderr,"this is mythread1.\n"); pthread_mutex_unlock(&mymutex1); returnNULL; } void*mythread2(void*param) { pthread_mutex_lock(&mymutex2); pthread_cond_wait(&mycond,&mymutex2); fprintf(stderr,"this is mythread2.\n"); pthread_mutex_unlock(&mymutex2); return NULL; } int main(int argc,char* argv[],char*envp[]) { int i; pthread_ttid1,tid2; pthread_create(&tid1,NULL,mythread1,NULL); pthread_create(&tid2,NULL,mythread2,NULL); sleep(2) if(pthread_cond_broadcast(&mycond)){ printf("error\n"); return1; } void*res; pthread_join(tid1,&res); pthread_join(tid2,&res); printf("thisis main thread.\n"); return0; } 为了让设备能访问另一个子网,需要在设备里增加路由到子网络,下面是一些资料。基本操作如下: 一般来说,都是为了能访问别的子网才设置路由的,比如说,你的主机处于192.168.10.0/24,而你想访问192.168.20.0/24网的主机,当然你知道一个网关IP,例如192.168.10.1(必须和你主机处于同一子网),那么,你可以这样配置路由。 添加路由 route add -net 192.168.20.0 netmask 255.255.255.0 gw 192.168.10.1 查看路由状态 route -n 删除路由 route del -net 192.168.20.0 netmask 255.255.255.0 摘自鸟哥的私房菜 · Destination, Genmask:这两个玩意儿就是分别是 network 与netmask 啦!所以这两个咚咚就组合成为一个完整的网域囉! · Gateway:该网域是通过那个 gateway 连接出去的? 如果显示 0.0.0.0 表示该路由是直接由本机传送,亦即可以透过区域网路的 MAC 直接传讯;如果有显示 IP 的话,表示该路由需要经过路由器 (通讯闸) 的帮忙才能够传送出去。 · Flags:总共有多个旗标,代表的意义如下: o U (route is up):该路由是启动的; o H (target is a host):目标是一部主机 (IP) 而非网域; o G (use gateway):需要透过外部的主机 (gateway) 来转递封包; o R (reinstate route for dynamic routing):使用动态路由时,恢复路由资讯的旗标; o D (dynamically installed by daemon or redirect):已经由服务或转 port 功能设定为动态路由 o M (modified from routing daemon or redirect):路由已经被修改了; o ! (reject route):这个路由将不会被接受(用来抵挡不安全的网域!) · Iface:这个路由传递封包的介面。 此外,观察一下上面的路由排列顺序喔,依序是由小网域(192.168.10.0/24 是 Class C),逐渐到大网域(169.254.0.0/16 Class B) 最后则是预设路由 (0.0.0.0/0.0.0.0)。然后当我们要判断某个网路封包应该如何传送的时候,该封包会经由这个路由的过程来判断喔!举例来说,我上头仅有三个路由,若我有一个传往 192.168.10.20 的封包要传递,那首先会找 192.168.10.0/24 这个网域的路由,找到了!所以直接由 eth0 传送出去;如果是传送到 Yahoo 的主机呢? Yahoo 的主机 IP 是 202.43.195.52,我通过判断 1)不是 192.168.10.0/24, 3)0/0 时,OK!传出去了,透过 eth0 将封包传给 192.168.10.30那部 gateway 主机啊!所以说,路由是有顺序的。因此当你重复设定多个同样的路由时,例如在你的主机上的两张网路卡设定为相同网域的 IP 时,会出现什么情况?会出现如下的情况: # route 命令添加的路由,机器重启或者网卡重启后就没掉了,在linux下设置永久路由的方法: =========================================================================================== WINDOWS下的route命令: 简单的的操作如下, 查看路由状态:route print 只查看ipv4(ipv6)路由状态:routeprint -4(-6) 添加路由:route add 目的网络 mask 子网掩码 网关 ——重启机器或网卡失效 route add 192.168.20.0 mask 255.255.255.0 192.168.10.1 添加永久:route -p add 目的网络 mask 子网掩码网关 route -p add 192.168.20.0 mask 255.255.255.0 192.168.10.1 删除路由:route delete 目的网络 mask 子网掩码 route delete 192.168.20.0 mask 255.255.255.0 来源: ChinaUnix博客 日期: 2007.04.09 15:09 (共有条评论) 我要评论 2. select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型,原型: select函数的接口比较简单: 功能: —— nfds 返回值: Remarks: 举个例子,比如recv(), 在没有数据到来调用它的时候,你的线程将被阻塞,如果数据一直不来,你的线程就要阻塞很久.这样显然不好. 理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。 以下是一个测试单个文件描述字可读性的例子: 本函数影响由fd 参数引用的一个打开的文件。 #include int ioctl( int fd, int request, .../* void *arg */ ); 返回0 :成功 -1 :出错 第三个参数总是一个指针,但指针的类型依赖于request 参数。 我们可以把和网络相关的请求划分为6 类: 套接口操作 文件操作 接口操作 ARP 高速缓存操作 路由表操作 流系统 下表列出了网络相关ioctl 请求的request 参数以及arg 地址必须指向的数据类型: 类别 Request 说明 数据类型 套 接 口 SIOCATMARK SIOCSPGRP SIOCGPGRP 是否位于带外标记 设置套接口的进程ID 或进程组ID 获取套接口的进程ID 或进程组ID int int int 文 件 FIONBIN FIOASYNC FIONREAD FIOSETOWN FIOGETOWN 设置/ 清除非阻塞I/O 标志 设置/ 清除信号驱动异步I/O 标志 获取接收缓存区中的字节数 设置文件的进程ID 或进程组ID 获取文件的进程ID 或进程组ID int int int int int 接 口 SIOCGIFCONF SIOCSIFADDR SIOCGIFADDR SIOCSIFFLAGS SIOCGIFFLAGS SIOCSIFDSTADDR SIOCGIFDSTADDR SIOCGIFBRDADDR SIOCSIFBRDADDR SIOCGIFNETMASK SIOCSIFNETMASK SIOCGIFMETRIC SIOCSIFMETRIC SIOCGIFMTU SIOCxxx 获取所有接口的清单 设置接口地址 获取接口地址 设置接口标志 获取接口标志 设置点到点地址 获取点到点地址 获取广播地址 设置广播地址 获取子网掩码 设置子网掩码 获取接口的测度 设置接口的测度 获取接口MTU (还有很多取决于系统的实现) struct ifconf struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq ARP SIOCSARP SIOCGARP SIOCDARP 创建/ 修改ARP 表项 获取ARP 表项 删除ARP 表项 struct arpreq struct arpreq struct arpreq 路 由 SIOCADDRT SIOCDELRT 增加路径 删除路径 struct rtentry struct rtentry 流 I_xxx 套接口操作: 明确用于套接口操作的ioctl 请求有三个, 它们都要求ioctl 的第三个参数是指向某个整数的一个指针。 SIOCATMARK: 如果本套接口的的度指针当前位于带外标记,那就通过由第三个参数指向的整数返回一个非0 值;否则返回一个0 值。POSIX 以函数sockatmark 替换本请求。 SIOCGPGRP : 通过第三个参数指向的整数返回本套接口的进程ID 或进程组ID ,该ID 指定针对本套接口的SIGIO 或SIGURG 信号的接收进程。本请求和fcntl 的F_GETOWN 命令等效,POSIX 标准化的是fcntl 函数。 SIOCSPGRP : 把本套接口的进程ID 或者进程组ID 设置成第三个参数指向的整数,该ID 指定针对本套接口的SIGIO 或SIGURG 信号的接收进程,本请求和fcntl 的F_SETOWN 命令等效,POSIX 标准化的是fcntl 操作。 文件操作: 以下5 个请求都要求ioctl 的第三个参数指向一个整数。 FIONBIO : 根据ioctl 的第三个参数指向一个0 或非0 值分别清除或设置本套接口的非阻塞标志。本请求和O_NONBLOCK 文件状态标志等效,而该标志通过fcntl 的F_SETFL 命令清除或设置。 FIOASYNC : 根据iocl 的第三个参数指向一个0 值或非0 值分别清除或设置针对本套接口的信号驱动异步I/O 标志,它决定是否收取针对本套接口的异步I/O 信号(SIGIO )。本请求和O_ASYNC 文件状态标志等效,而该标志可以通过fcntl 的F_SETFL 命令清除或设置。 FIONREAD : 通过由ioctl 的第三个参数指向的整数返回当前在本套接口接收缓冲区中的字节数。本特性同样适用于文件,管道和终端。 FIOSETOWN : 对于套接口和SIOCSPGRP 等效。 FIOGETOWN : 对于套接口和SIOCGPGRP 等效。 接口配置: 得到系统中所有接口由SIOCGIFCONF 请求完成,该请求使用ifconf 结构,ifconf 又使用ifreq 结构,如下所示: Struct ifconf{ intifc_len; // 缓冲区的大小 union{ caddr_tifcu_buf; // input fromuser->kernel struct ifreq*ifcu_req; // return of structures returned }ifc_ifcu; }; #define ifc_buf ifc_ifcu.ifcu_buf //buffer address #define ifc_req ifc_ifcu.ifcu_req //array of structures returned #define IFNAMSIZ 16 struct ifreq{ charifr_name[IFNAMSIZ]; // interface name, e.g., “le0” union{ structsockaddr ifru_addr; structsockaddr ifru_dstaddr; structsockaddr ifru_broadaddr; shortifru_flags; intifru_metric; caddr_tifru_data; }ifr_ifru; }; #define ifr_addr ifr_ifru.ifru_addr // address #define ifr_dstaddr ifr_ifru.ifru_dstaddr // otner end of p-to-p link #define ifr_broadaddrifr_ifru.ifru_broadaddr // broadcast address #define ifr_flags ifr_ifru.ifru_flags // flags #define ifr_metric ifr_ifru.ifru_metric // metric #define ifr_data ifr_ifru.ifru_data // for use byinterface 再调用ioctl 前我们必须先分撇一个缓冲区和一个ifconf 结构,然后才初始化后者。如下图 展示了一个ifconf 结构的初始化结构,其中缓冲区的大小为1024 ,ioctl 的第三个参数指向 这样一个ifconf 结构。 ifc_len Ifc_buf 1024 ---------------------> 缓存 假设内核返回2 个ifreq 结构,ioctl 返回时通过同一个ifconf 结构缓冲区填入了那2 个ifreq 结构,ifconf 结构的ifc_len 成员也被更新,以反映存放在缓冲区中的信息量 一般来讲ioctl在用户程序中的调用是: ioctl(int fd,int command, (char*)argstruct) ioctl调用与网络编程有关(本文只讨论这一点),文件描述符fd实际上是由socket()系统调用返回的。参数command的取值由/usr/include/linux/sockios.h 所规定。这些command的由于功能的不同,可分为以下几个小类: 与网络接口有关的ioctl调用使用的command参数通常看起来像SIOCxIFyyyy的形式,这里x要 么是S(设定set,写write),要么是G(得到get,读read)。在getifinfo.c程序中就使用了这种形式的command参数来读 IP地址,硬件地址,广播地址和得到与网络接口有关的一些标志(flag)。在这些ioctl调用中,第三个参数是ifreq结构,它在/usr /include/linux/if.h中定义。在某些情况下, ioctrl调用可能会使用到在sockios.h之外的新的定义,例如,WaveLAN无线网络卡会保 分类: 程序语言2008-10-27 15:14 16926人阅读 评论(8) 收藏 举报 语言cstructmatrixfloat编程 以前在学校学习C语言的时候一直搞不懂那个共用体union有什么用的。工作之后才发现它的一些妙用,现举例如下: 如果是littleendian字节序的话,那个i= 1;的内存从小到大依次放的是:0x010x00 0x00 0x00,如是,按照i的起始地址变成按照char*方式(1字节)存取,即得c= 0x01; 这里用的是union来控制这个共享布局,有个知识点就是union里面的成员c和i都是从低地址开始对齐的。同样可以得到如此结果,而且不用转换,清晰一些。 只要把内存结构的草图画出来就比较容易明白了。 不能在类Test中增加代码,给对象中的f赋值7.0f. 说明:因为在增加类的成员函数时候,那个类的对象的布局基本不变。因此可以写一个与Test类一样结构的类Test_Cpy,而多了一个成员函数setVal,再用uinon结构对齐,就可以给私有变量赋值了。(这种方法在有虚机类和虚函数机制时可能失灵,故不可移植)至于详细的讨论,网上有,这个例子在实际中没有用途,只是用来考察这个内存布局的使用而已. 功能描述: int getsockopt(int sock,int level, int optname, void *optval, socklen_t *optlen); int setsockopt(int sock,int level, int optname, const void *optval, socklen_t optlen); 参数: level指定控制套接字的层次.可以取三种值: optval获得或者是设置套接字选项.根据选项名称的数据类型进行转换 返回说明: SO_RCVBUF和SO_SNDBUF每个套接口都有一个发送缓冲区和一个接收缓冲区,使用这两个套接口选项可以改变缺省缓冲区大小。 // 接收缓冲区 注意: 当设置TCP套接口接收缓冲区的大小时,函数调用顺序是很重要的,因为TCP的窗口规模选项是在建立连接时用SYN与对方互换得到的。对于客户,SO_RCVBUF选项必须在connect之前设置;对于服务器,SO_RCVBUF选项必须在listen前设置。 阅读:651次 时间:2012-07-3120:41:12 字体:[大 中 小] 刚接触SVN的时候,因为对它不了解,又在Windows下面,被它的多版本库配置问题困扰很久,一直找不到完美解决方案,今天无意中在Linux下配置SVN时,发现它本身是支持的,通过配置--config-file参数指定全局的配置文件实现。写下此文来纠正以前错误的配置方式(主要是Windows系统下),分享给大家。 Linux和Windows下处理基本上一样的,先来看Linux下的svnserve的帮助信息: 1. [root@localhost ~]# svnserve --help 2. usage: svnserve [-d | -i | -t | -X] [options] 3. 4. Valid options: 5. -d [--daemon] : daemon mode 6. -i [--inetd] : inetd mode 7. -t [--tunnel] : tunnel mode 8. -X [--listen-once] : listen-once mode (useful for debugging) 9. -r [--root] ARG : root of directory to serve 10. -R [--read-only] : force read only, overriding repository config file 11. --config-file ARG : read configuration from file ARG 12. --listen-port ARG : listen port 13. [mode: daemon, listen-once] 14. --listen-host ARG : listen hostname or IP address 15. [mode: daemon, listen-once] 16. -T [--threads] : use threads instead of fork [mode: daemon] 17. --foreground : run in foreground (useful for debugging) 18. [mode: daemon] 19. --log-file ARG : svnserve log file 20. --pid-file ARG : write server process ID to file ARG 21. [mode: daemon, listen-once] 22. --tunnel-user ARG : tunnel username (default is current uid's name) 23. [mode: tunnel] 24. -h [--help] : display this help 25. --version : show program version information 通常启动SVN服务,仅指定SVN版本库的根目录,如下: 1. svnserve -d -r /data/svn 然后在/data/svn下创建多个版本库: 1. cd /data/svn 2. svnadmin create repos1 3. svnadmin create repos2 再依次配置repos1和repos2等版本库下的conf/svnserve.conf、conf/passwd、conf/authz文件。 问题便来了,因为大多数的时候,同一个用户需要用相同的帐号和密码去访问不同的版本库,这时的权限配置就不好处理了,以前看其他人的解决方法是在svnserve.conf中指定passwd和authz的路径时用相对路径指到同一个文件。这是一个可行的方法,但新增版本库的时候,就得更改svnserve.conf文件,不方便。 仔细看svnserve的帮助信息,大家都会发现有一个--config-file参数,这个参数就是用来指定svnserve.conf路径的,说到这,问题已经明了,只要在启动SVN服务的时候,指定--config-file参数,只要指定了此参数,所有的权限都由参数指定的svnserve.conf控制,而每个版本库conf目录下的svnserve.conf的配置都会忽略掉。 1. svnserve -d -r /data/svn --config-file /data/svn/svnserve.conf Windows下使用的是CollabNet的Subversion Server,它安装的服务,指定的ImagePath形式为:"d:Program Files (x86)CollabNetSubversionServersvnserve.exe" --service -r "e:svn_repository"--listen-port "3690" ,是没有指定--config-file参数的,因此导致我等刚接触SVN的新手会被多版本库的配置问题纠结,现在只要到注册表更改一下SVN所在服务的ImagePath参数,追加上--config-file参数: 1. "d:Program Files (x86)CollabNetSubversion Serversvnserve.exe" --service -r "e:svn_repository" --listen-port "3690" --config-file "e:svn_repositoryconfsvnserve.conf" 以上中使用的路径等,请自行转换成各自对应的路径。 软件下载 建立多版本库 admin =ren svnservice -install -d-r F:\svn\repository (该操作中可能出现CreateServicefailed - Commandline set: "-d" "-r" "F:\svn\repository"错误,此时执行svnservice-remove命令即可) sc config svnservicestart= auto net start svnservice 在win7下可能出现如下问题 OpenSCManagerFAILED 5: Access is denied. 怀疑是win7和vista的UAC问题,打开开始菜单,找到命令行的快捷方式,右键,以管理员身份运行, svnserivce -install -d-r 。。。。。。 8、建立多版本库 svn 在一个版本库下管理多个工程,不会为每个工程建一个版本库,这样会导致版本库跳跃,所以有时必须建立多个版本库 svn 在一个版本库下管理多个工程会导致版本库跳跃,所以有时必须建立多个版本库 (1)、把所有的版本库的放在一个统一的目录下(如F:\svn),在此目录下我们要建立两个工程的版本库Respontory1和Respontory2(必须先建好这两个文件夹)。 (2)、创建第一个项目project1版本库,命令:svnadmincreate F\svnroot\Respontory1 (3)、创建第二个项目project2版本库,命令:svnadmincreate F:\svnroot\Respontory2,当然这两步也可以用TortoiseSVN建立 (4)、为了便于管理,将所有版本库的密码和权限设置在同一个文件下面,操作步骤如下: A、取出Respontory1下面conf文件夹下的authz和passwd两个文件到svn根目录下面 B、修改每个版本库目录conf文件夹下面的svnserve.conf文件,将 # anon-access = read,#auth-access = write,#password-db = passwd,#authz-db = authz 修改为: anon-access = none,auth-access= write,password-db= ../../passwd,authz-db= ../../authz (password-db = ../../passwd,authz-db= ../../authz代表相对路径而非绝对路径) 如果不需要分角色,那么可以不设置authz-db (5)、dos控制台状态下cd 进入subversion的安装目录的bin目录, svnservice -install -d-r F:\svn\ (该操作中可能出现CreateServicefailed - Commandline set: "-d" "-r" "F:\svn\repository"错误,此时执行svnservice-remove命令即可) sc config svnservicestart= auto net start svnservice 到此为止就配置成功了你可以将两个工程导入到这两个版本库中,而且版本号不相互影响。 15人收藏此文章, 我要收藏发表于1年前(2012-04-15 00:35) , 已有16021次阅读,共3个评论 曾经的曾经,被system()函数折磨过,之所以这样,是因为对system()函数了解不够深入。只是简单的知道用这个函数执行一个系统命令,这远远不够,它的返回值、它所执行命令的返回值以及命令执行失败原因如何定位,这才是重点。当初因为这个函数风险较多,故抛弃不用,改用其他的方法。这里先不说我用了什么方法,这里必须要搞懂system()函数,因为还是有很多人用了system()函数,有时你不得不面对它。 先来看一下system()函数的简单介绍: system() executes a command specified in command bycalling /bin/sh -c command, and returns after the command has been completed.During execution of the command, SIGCHLD will be blocked, and SIGINT andSIGQUIT will be ignored. system()函数调用/bin/sh来执行参数指定的命令,/bin/sh 一般是一个软连接,指向某个具体的shell,比如bash,-c选项是告诉shell从字符串command中读取命令; 在该command执行期间,SIGCHLD是被阻塞的,好比在说:hi,内核,这会不要给我送SIGCHLD信号,等我忙完再说; 在该command执行期间,SIGINT和SIGQUIT是被忽略的,意思是进程收到这两个信号后没有任何动作。 再来看一下system()函数返回值: The value returned is -1 on error (e.g. fork(2) failed), and thereturn status of the command otherwise. This latter return status is in theformat specified in wait(2). Thus, the exit code of the command will beWEXITSTATUS(status). In case /bin/sh could not be executed, the exit statuswill be that of a command that does exit(127). If the value of command is NULL, system() returns nonzero if theshell is available, and zero if not. 为了更好的理解system()函数返回值,需要了解其执行过程,实际上system()函数执行了三步操作: 1.fork一个子进程; 2.在子进程中调用exec函数去执行command; 3.在父进程中调用wait去等待子进程结束。 对于fork失败,system()函数返回-1。 如果exec执行成功,也即command顺利执行完毕,则返回command通过exit或return返回的值。 (注意,command顺利执行不代表执行成功,比如command:"rm debuglog.txt",不管文件存不存在该command都顺利执行了) 如果exec执行失败,也即command没有顺利执行,比如被信号中断,或者command命令根本不存在,system()函数返回127. 如果command为NULL,则system()函数返回非0值,一般为1. 看一下system()函数的源码 看完这些,我想肯定有人对system()函数返回值还是不清楚,看源码最清楚,下面给出一个system()函数的实现: 仔细看完这个system()函数的简单实现,那么该函数的返回值就清晰了吧,那么什么时候system()函数返回0呢?只在command命令返回0时。 看一下该怎么监控system()函数执行状态 这里给我出的做法: 到于取得子进程返回值的相关介绍可以参考另一篇文章:http://my.oschina.net/renhc/blog/35116 system()函数用起来很容易出错,返回值太多,而且返回值很容易跟command的返回值混淆。这里推荐使用popen()函数替代,关于popen()函数的简单使用也可以通过上面的链接查看。 popen()函数较于system()函数的优势在于使用简单,popen()函数只返回两个值: 这篇文章只涉及了system()函数的简单使用,还没有谈及SIGCHLD、SIGINT和SIGQUIT对system()函数的影响,事实上,之所以今天写这篇文章,是因为项目中因有人使用了system()函数而造成了很严重的事故。现像是system()函数执行时会产生一个错误:“No child processes”。 关于这个错误的分析,感兴趣的朋友可以看一下:http://my.oschina.net/renhc/blog/54582 返回值:若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返 #include #include pid_t fork( void); (pid_t是一个宏定义,其实质是int 被定义在#include<sys/types.h>中) 返回值:若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1 一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(childprocess)。fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。 子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间。 UNIX将复制父进程的地址空间内容给子进程,因此,子进程有了独立的地址空间。在不同的UNIX(Like)系统下,我们无法确定fork之后是子进程先运行还是父进程先运行,这依赖于系统的实现。所以在移植代码的时候我们不应该对此作出任何的假设。 由于在复制时复制了父进程的堆栈段,所以两个进程都停留在fork函数中,等待返回。因此fork函数会返回两次,一次是在父进程中返回,另一次是在子进程中返回,这两次的返回值是不一样的。过程如下图
调用fork之后,数据、堆栈有两份,代码仍然为一份但是这个代码段成为两个进程的共享代码段都从fork函数中返回,箭头表示各自的执行处。当父子进程有一个想要修改数据或者堆栈时,两个进程真正分裂。 示例代码: #include<sys/types.h> //对于此程序而言此头文件用不到 #include<unistd.h> #include<stdio.h> #include int main(int argc, char **argv ) { pid_t pid= fork(); if (pid< 0) { fprintf(stderr, "error!"); } else if(0 == pid ) { printf("This is the child process!"); _exit(0); } else { printf("This is the parent process! child process id =%d", pid); } //可能需要时候wait或waitpid函数等待子进程的结束并获取结束状态 exit(0); } 注意!样例代码仅供参考,样例代码存在着父进程在子进程结束前结束的可能性。必要的时候可以使用wait或 waitpid函数让父进程等待子进程的结束并获取子进程的返回状态。 fork()在Linux系统中的返回值是没有NULL的. Error Codes 出错返回错误信息如下: EAGAIN 达到进程数上限. ENOMEM 没有足够空间给一个新进程分配. fork函数的特点概括起来就是“调用一次,返回两次”,在父进程中调用一次,在父进程和子进程中各返回一次。 fork的另一个特性是所有由父进程打开的描述符都被复制到子进程中。父、子进程中相同编号的文件描述符在内核中指向同一个file结构体,也就是说,file结构体的引用计数要增加。 分类: Java2012-07-1502:22 7896人阅读 评论(0) 收藏 举报 linuxlinux内核filesystemsprotocolscachetimer 1. /proc目录 用户和应用程序可以通过proc得到系统的信息,并可以改变内核的某些参数。由于系统的信息,如进程,是动态改变的,所以用户或应用程序读取proc文件时,proc文件系统是动态从系统内核读出所需信息并提交的。下面列出的这些文件或子文件夹,并不是都是在你的系统中存在,这取决于你的内核配置和装载的模块。另外,在/proc下还有三个很重要的目录:net,scsi和sys。 Sys目录是可写的,可以通过它来访问或修改内核的参数,而net和scsi则依赖于内核配置。例如,如果系统不支持scsi,则scsi 目录不存在。 除了以上介绍的这些,还有的是一些以数字命名的目录,它们是进程目录。系统中当前运行的每一个进程都有对应的一个目录在/proc下,以进程的 PID号为目录名,它们是读取进程信息的接口。而self目录则是读取进程本身的信息接口,是一个link。 2. 子文件或子文件夹 /proc/cmdline 启动时传递给kernel的参数信息 /proc/cpuinfo cpu的信息 /proc/crypto 内核使用的所有已安装的加密密码及细节 /proc/devices 已经加载的设备并分类 /proc/execdomains Linux内核当前支持的execution domains /proc/fb 帧缓冲设备列表,包括数量和控制它的驱动 /proc/filesystems 内核当前支持的文件系统类型 /proc/interrupts x86架构中的每个IRQ中断数 /proc/iomem 每个物理设备当前在系统内存中的映射 /proc/ioports 一个设备的输入输出所使用的注册端口范围 /proc/kcore 代表系统的物理内存,存储为核心文件格式,里边显示的是字节数,等于RAM大小加上4kb /proc/kmsg 记录内核生成的信息,可以通过/sbin/klogd或/bin/dmesg来处理 /proc/loadavg 根据过去一段时间内CPU和IO的状态得出的负载状态,与uptime命令有关 /proc/locks 内核锁住的文件列表 /proc/mdstat 多硬盘,RAID配置信息(md=multipledisks) /proc/meminfo RAM使用的相关信息 /proc/misc 其他的主要设备(设备号为10)上注册的驱动 /proc/modules 所有加载到内核的模块列表 /proc/mounts 系统中使用的所有挂载 /proc/mtrr 系统使用的Memory Type Range Registers (MTRRs) /proc/partitions 分区中的块分配信息 /proc/pci 系统中的PCI设备列表 /proc/slabinfo 系统中所有活动的 slab 缓存信息 /proc/stat 所有的CPU活动信息 /proc/sysrq-trigger 使用echo命令来写这个文件的时候,远程root用户可以执行大多数的系统请求关键命令,就好像在本地终端执行一样。要写入这个文件,需要把/proc/sys/kernel/sysrq不能设置为0。这个文件对root也是不可读的 /proc/uptime 系统已经运行了多久 /proc/swaps 交换空间的使用情况 /proc/version Linux内核版本和gcc版本 /proc/bus 系统总线(Bus)信息,例如pci/usb等 /proc/driver 驱动信息 /proc/fs 文件系统信息 /proc/ide ide设备信息 /proc/irq 中断请求设备信息 /proc/net 网卡设备信息 /proc/scsi scsi设备信息 /proc/tty tty设备信息 /proc/net/dev 显示网络适配器及统计信息 /proc/vmstat 虚拟内存统计信息 /proc/vmcore 内核panic时的内存映像 /proc/diskstats 取得磁盘信息 /proc/schedstat kernel调度器的统计信息 /proc/zoneinfo 显示内存空间的统计信息,对分析虚拟内存行为很有用 以下是/proc目录中进程N的信息 /proc/N pid为N的进程信息 /proc/N/cmdline 进程启动命令 /proc/N/cwd 链接到进程当前工作目录 /proc/N/environ 进程环境变量列表 /proc/N/exe 链接到进程的执行命令文件 /proc/N/fd 包含进程相关的所有的文件描述符 /proc/N/maps 与进程相关的内存映射信息 /proc/N/mem 指代进程持有的内存,不可读 /proc/N/root 链接到进程的根目录 /proc/N/stat 进程的状态 /proc/N/statm 进程使用的内存的状态 /proc/N/status 进程状态信息,比stat/statm更具可读性 /proc/self 链接到当前正在运行的进程 3. 例子 1 16819 21242 2180 2494 8768 interrupts partitions 116 16820 21244 2181 2524 885 iomem sched_debug 11740 17901 21245 21810 2525 acpi ioports scsi 11742 17903 21247 21812 3 asound irq self 11743 17904 2131 21813 39 buddyinfo kallsyms slabinfo 13452 18362 21319 21923 4 bus kcore stat 13454 18364 2132 2193 41 cgroups key-users swaps 13455 18365 2139 21933 42 cmdline kmsg sys 149 19451 2142 2209 5 cpuinfo kpagecount sysrq-trigger 150 19453 21572 2212 5330 crypto kpageflags sysvipc 151 19454 21574 2219 596 devices loadavg timer_list 152 2 21575 2243 597 diskstats locks timer_stats 15771 2083 2158 2260 6 dma meminfo tty 15773 2092 21625 2261 617 driver misc uptime 15774 2101 21627 2262 619 execdomains modules version 16232 21112 21628 2263 7 fb mounts vmallocinfo 16234 21115 2165 2264 804 filesystems mtrr vmstat 16235 21116 2167 2265 8765 fs net zoneinfo 16811 2112 2177 2338 8767 ide pagetypeinfo 3.2 /proc/sys yafang@QA:~$ ls/proc/sys debug dev fs kernel net vm 3.3 /proc/net yafang@QA:~$ ls/proc/net anycast6 ip6_flowlabel netfilter raw6 sockstat6 udplite arp ip6_mr_cache netlink route softnet_stat udplite6 dev ip6_mr_vif netstat rt6_stats stat unix dev_mcast ip_mr_cache packet rt_acct tcp vlan dev_snmp6 ip_mr_vif protocols rt_cache tcp6 wireless if_inet6 ipv6_route psched snmp tr_rif igmp mcfilter ptype snmp6 udp igmp6 mcfilter6 raw sockstat udp6 3.4 /proc/scsi yafang@QA:~$ ls/proc/scsi device_info scsi 3.5 /proc/modules root@BDSP-A-2-1-2:~# cat /proc/modules bdspboard 8486 2 dspcontrol, Live 0xe134c000 dspcontrol 9575 1 clkmon, Live 0xe135b000 clkmon 6765 1 - Live 0xe136c000 diagint 6635 1 - Live 0xe1379000 bdsprio 10775 2 srioif,tsi577, Live 0xe9389000 tsi577 17998 1 srioif, Live 0xe939e000 srioif 7329 0 - Live 0xe93b2000 linux_kernel_bde 54666 1 linux_user_bde, Live 0xf1417000(P) linux_user_bde 17849 0 - Live 0xf1427000 (P) root@BDSP-A-2-1-2:~# 3.6 /proc/devices root@BCNMB-A:~#cat /proc/devices 1 mem 2 pty 3 ttyp 4 /dev/vc/0 4 tty 4 ttyS 5 /dev/tty 5 /dev/console 5 /dev/ptmx 7 vcs 10 misc 13 input 89 i2c 90 mtd 116 linux-user-bde2 117 linux-kernel-bde2 126 linux-user-bde 127 linux-kernel-bde 128 ptm 136 pts 180 usb 189 usb_device 245 ext_alarm 251 ipmidev 252 usb_endpoint 253 usbmon 254 rtc Block devices: 1 ramdisk 8 sd 31 mtdblock 65 sd 66 sd 67 sd 68 sd 69 sd 70 sd 71 sd 128 sd 129 sd 130 sd 131 sd 132 sd 133 sd 134 sd 135 sd 3.7 /proc/partitions root@BDSP-A-2-1-2:~# cat /proc/partitions major minor #blocks name 31 0 512 mtdblock0 31 1 512 mtdblock1 31 2 123904 mtdblock2 31 3 4096 mtdblock3 31 4 1024 mtdblock4 31 5 1024 mtdblock5 31 6 512 mtdblock6 31 7 512 mtdblock7 31 8 123904 mtdblock8 31 9 4096 mtdblock9 31 10 1024 mtdblock10 31 11 1024 mtdblock11 31 12 1048576 mtdblock12 root@BDSP-A-2-1-2:~# 3.8 /proc/version root@BDSP-A-2-1-2:~# cat /proc/version Linux version 2.6.34.6-WR4.0.0.0_standard (satomi@CharlieBrown)(gcc version 4.4.1 (Wind River Linux Sourcery G++ 4.4-291) ) #1 SMP PREEMPT FriNov 26 16:07:47 CST 2010 root@BDSP-A-2-1-2:~# 3.9 /proc/sys/fs/file-max 改变内核的参数,用vi编辑或echo参数重定向到文件中。 # cat /proc/sys/fs/file-max 4096 # echo 8192 > /proc/sys/fs/file-max # cat /proc/sys/fs/file-max 8192 如果优化了参数,则可以把它们写成添加到文件rc.local中,使它在系统启动时自动完成修改 shell的条件分支语句: 1. $if true; then echo TRUE; fi 2. TRUE 3. 4. $if true; false; then echo TRUE; else echo FALSE; fi 5. FALSE 6. 7. $if true; false; true; then echo TRUE; else echo FALSE; fi 8. TRUE 复制代码 内核在引导时要求某些设备节点必须存在(特别是 console 和 null ),这些设备节点必须创建在硬盘上才能使得内核在 udev 尚未启动之前就可以使用它们,特别是当系统以单用户模式启动(仅允许使用 console)的时候更是如此。使用下面的命令来创建这些节点: 推荐的向 /dev 目录填充设备的方法是在 /dev 上挂载一个虚拟文件系统(比如 tmpfs),然后在设备被检测到或被访问到的时候(通常是在系统引导的过程中)动态创建设备节点。既然现在新的系统尚未被引导,那么就有必要通过挂载 /dev 来手工完成 LFS-Bootscripts 将来要做的事情: Udev软件包是实际用于在 /dev 目录中添加设备的工具。但是由于它要在后面的步骤中才被安装,我们现在必须手动创建一个必需的设备文件的最小集合,以便继续构建我们的系统。[注意]前面创建的 console 和 null 设备文件(保存在硬盘上)被新挂载的 tmpfs 文件系统隐藏了,所以这里还要再创建一次。 有一些在系统启动的时候由 LFS-Bootscripts 创建的符号连接和目录是 LFS 系统所必须的。既然目前只是 chroot 后的环境而不是真实启动后的环境,那么就需要在这里先创建他们: 最后在新建的目录中挂载虚拟内核文件系统: 上面的 mount 命令可能会导致下面的警告信息: 因为在挂载文件系统时需要 /etc/fstab 文件的指示,但是该文件目前尚未被创建,不过你可以安全的忽略它,该文件系统仍然会被正确的挂载。 linux 目录树 2013-05-31 19:54:24 分类: LINUX linux根文件系统中一般有下面的几个目录: /bin 该目录下的命令可以被root与一般账号所使用,由于这些命令在挂接其它文件系统之前就可以使用,所以/bin目录必须和根文件系统在同一个分区中。 /bin目录下常用的命令有:cat、chgrp、chmod、cp、ls、sh、kill、mount、umount、mkdir、[、test等。其中“[”命令就是test命令,我们在利用Busybox制作根文件系统时,在生成的bin目录下,可以看到一些可执行的文件,也就是可用的一些命令。 /sbin 目录 该目录下存放系统命令,即只有系统管理员(俗称最高权限的root)能够使用的命令,系统命令还可以存放在/usr/sbin,/usr/local/sbin目录下,/sbin目录中存放的是基本的系统命令,它们用于启动系统和修复系统等,与/bin目录相似,在挂接其他文件系统之前就可以使用/sbin,所以/sbin目录必须和根文件系统在同一个分区中。 /sbin目录下常用的命令有:shutdown、reboot、fdisk、fsck、init等,本地用户自己安装的系统命令放在/usr/local/sbin目录下。 /dev目录 该目录下存放的是设备与设备接口的文件,设备文件是Linux中特有的文件类型,在Linux系统下,以文件的方式访问各种设备,即通过读写某个设备文件操作某个具体硬件。比如通过"dev/ttySAC0"文件可以操作串口0,通过"/dev/mtdblock1"可以访问MTD设备的第2个分区。比较重要的文件有/dev/null, /dev/zero, /dev/tty,/dev/lp*等。 /etc目录 该目录下存放着系统主要的配置文件,例如人员的账号密码文件、各种服务的其实文件等。一般来说,此目录的各文件属性是可以让一般用户查阅的,但是只有root有权限修改。对于PC上的Linux系统,/etc目录下的文件和目录非常多,这些目录文件是可选的,它们依赖于系统中所拥有的应用程序,依赖于这些程序是否需要配置文件。在嵌入式系统中,这些内容可以大为精减。 /lib目录 该目录下存放共享库和可加载(驱动程序),共享库用于启动系统。运行根文件系统中的可执行程序,比如:/bin /sbin 目录下的程序。 /home目录 系统默认的用户文件夹,它是可选的,对于每个普通用户,在/home目录下都有一个以用户名命名的子目录,里面存放用户相关的配置文件 /root目录 系统管理员(root)的主文件夹,即是根用户的目录,与此对应,普通用户的目录是/home下的某个子目录。 /usr目录 /usr目录的内容可以存在另一个分区中,在系统启动后再挂接到根文件系统中的/usr目录下。里面存放的是共享、只读的程序和数据,这表明/usr目录下的内容可以在多个主机间共享,这些主要也符合FHS标准的。/usr中的文件应该是只读的,其他主机相关的,可变的文件应该保存在其他目录下,比如/var。/usr目录在嵌入式中可以精减。 /var目录 与/usr目录相反,/var目录中存放可变的数据,比如spool目录(mail,news),log文件,临时文件。 /proc目录 这是一个空目录,常作为proc文件系统的挂接点,proc文件系统是个虚拟的文件系统,它没有实际的存储设备,里面的目录,文件都是由内核 临时生成的,用来表示系统的运行状态,也可以操作其中的文件控制系统。 /mnt目录 用于临时挂载某个文件系统的挂接点,通常是空目录,也可以在里面创建一引起空的子目录,比如/mnt/cdram /mnt/hda1 。用来临时挂载光盘、移动存储设备等。 /tmp目录 用于存放临时文件,通常是空目录,一些需要生成临时文件的程序用到的/tmp目录下,所以/tmp目录必须存在并可以访问。 内核分区表: 0x000000000000-0x000000100000 : "mtdblock0 u-boot1MB" 0x000000100000-0x000001000000 : "mtdblock1 kernel15MB" 0x000001000000-0x000002400000 : "mtdblock2 ramdisk20MB" 0x000002400000-0x000003800000 : "mtdblock3 cramfs20MB" 0x000003800000-0x000006000000 : "mtdblock4 jffs220MB" 0x000006000000-0x000008800000 : "mtdblock5 yaffs240MB" 0x000008800000-0x00000b000000 : "mtdblock6 ubifs40MB" 0x00000b000000-0x00000e200000 : "mtdblock7 apps50MB" 0x00000e200000-0x000011400000 : "mtdblock8 data50MB" 1.首先创建目录树框架 [lingyun@localhost rootfs]$ tree . |-- apps |-- bin |-- data |-- dev |-- etc | `-- init.d |-- lib |-- mnt | |-- dev | |-- nfs | |-- sdc | `-- usb |-- proc |-- root |-- sbin |-- sys |-- tmp |-- usr | |-- bin | |-- lib | |-- sbin | `--share `-- var 2.在/dev目录下创建设备节点: [lingyun@localhost dev]$ sudo mknod -m 755 console c 5 1 [lingyun@localhost dev]$ sudo mknod -m 755 null c 1 3 [lingyun@localhost dev]$ sudo mknod -m 755 ttyS0 c 4 64 [lingyun@localhost dev]$ sudo mknod mtdblock0 b 31 0 [lingyun@localhost dev]$ sudo mknod mtdblock1 b 31 1 [lingyun@localhost dev]$ sudo mknod mtdblock2 b 31 2 [lingyun@localhost dev]$ sudo mknod mtdblock3 b 31 3 [lingyun@localhost dev]$ sudo mknod mtdblock4 b 31 4 [lingyun@localhost dev]$ sudo mknod mtdblock5 b 31 5 [lingyun@localhost dev]$ sudo mknod mtdblock6 b 31 6 [lingyun@localhost dev]$ sudo mknod mtdblock7 b 31 7 [lingyun@localhost dev]$ sudo mknod mtdblock8 b 31 8 3.在var 目录下创建符号链接文件: [lingyun@localhost var]$ ln -s ../tmp lock [lingyun@localhost var]$ ln -s ../tmp log [lingyun@localhost var]$ ln -s ../tmp run [lingyun@localhost var]$ ln -s ../tmp tmp [lingyun@localhost var]$ ls -l total 0 lrwxrwxrwx 1 lingyun trainning 6 May 27 13:39 lock ->../tmp lrwxrwxrwx 1 lingyun trainning 6 May 27 13:39 log ->../tmp lrwxrwxrwx 1 lingyun trainning 6 May 27 13:39 run ->../tmp lrwxrwxrwx 1 lingyun trainning 6 May 27 13:39 tmp ->../tmp /var/tmp /var/lock /var/log /var/run 4.在/etc目录下增加fstab,hostname,inittab,mdev.conf,passwd,protocols,resolv.conf,services, group,hosts,issue,mtab,profile,shadow文件 inittab文件: # /etc/inittab # mount all the file systems specified in /etc/fstab ::sysinit:/bin/mount –a //系统启动后自动挂载fstab文件中指定的虚拟文件系统 #Use mdev as hotplug to auto mount USB storage or SD card //系统使能hotplug功能,当然还需要一些挂载脚本文件 ::sysinit:/bin/echo /sbin/mdev >/proc/sys/kernel/hotplug ::sysinit:/sbin/mdev -s #make shm, pts support ::sysinit:/bin/mkdir -p /dev/pts ::sysinit:/bin/mkdir -p /dev/shm ::sysinit:/bin/mount -t devpts devpts /dev/pts #Mount our apps/infopartition //挂载nand flash 分区 null::wait:/bin/mount -o sync,noatime,ro -t jffs2/dev/mtdblock7 /apps null::wait:/bin/mount -o sync,noatime,ro -t jffs2/dev/mtdblock8 /data #Set hostname null::sysinit:/bin/hostname -F /etc/hostname #Initialize the user account files #null::wait:/usr/sbin/initpwd #Enable console logon null::respawn:/sbin/getty -L ttyS0 115200vt100 null::sysinit:/bin/mkdir -p /tmp/logs null::sysinit:/bin/mkdir -p /tmp/stat # now run any rc scripts null::wait:/etc/init.d/rcS # system daemon null::respawn:/sbin/syslogd -n null::respawn:/sbin/klogd -n # Stuff to do before rebooting null::shutdown:/bin/umount /apps null::shutdown:/bin/umount /data null::shutdown:/bin/killall klogd null::shutdown:/bin/killall syslogd null::shutdown:/bin/umount -a -r fstab文件: /etc/fstab: static file system information. # /dev/root / ext2 rw,noauto 0 1 proc /proc proc defaults 0 0 tmpfs /dev tmpfs defaults 0 0 ramfs /tmp ramfs defaults 0 0 sysfs /sys sysfs defaults 0 0 hostname文件: MINI2440 shadow文件: 修改虚拟机root用户的密码: [lingyun@localhost etc]$ sudo passwd root Changing password for user root. New password: BAD PASSWORD: it is based on a dictionary word BAD PASSWORD: is too simple Retype new password: passwd: all authentication tokens updated successfully. [lingyun@localhost etc]$ sudo head -n 2 /etc/shadow root:$6$4o1aUL4K$fexvf1D74DjTIAfXZkEyrPuQWu38iPx2CrfC91Sx09331RBDDV0iEZ/GIX9vWMaIRij6VSaHbhuEM98X6ag9B.:15852:0:99999:7::: bin:*:15513:0:99999:7::: 将以上两行内容拷贝到制作的根文件系统/etc/shadow中。注意:我现在是在虚拟机上做文件系统。 group文件: root:x:0: //为root用户创建一个分组 passwd文件: root:x:0:0:root:/root:/bin/sh //每个系统都必需有个root用户 hosts文件: 127.0.0.1 localhost //pinglocalhost就相当于ping 127.0.0.1 issue文件: //文件中的内容为系统登录时的提示信息 Welcome to LinuxWorld!!! mdev.conf文件: //系统自动挂载文件,比如我们插上U盘后,自动挂载 sd[a-h]*[0-9] 0:00660 *(/usr/sbin/hotplug /media/usb) sd[a-h] 0:00660 *(/usr/sbin/hotplug /media/usb) ub[a-h]*[0-9] 0:00660 *(/usr/sbin/hotplug /media/usb) ub[a-h] 0:00660 *(/usr/sbin/hotplug /media/usb) mmcblk[0-9]p[0-9] 0:00660 @(/bin/mount /dev/$MDEV /media/mmc) mmcblk[0-9] 0:00660 $(/bin/umount/media/mmc) profile文件: export PATH=\ /bin:\ /sbin:\ /usr/bin:\ /usr/sbin:\ /apps/tools:\ /apps/bin:\ export PS1='\w >: ' export USER=`id -un` export LOGNAME=$USER export HOSTNAME=`/bin/hostname` export HISTSIZE=500 export HISTFILESIZE=500 export PAGER='/bin/more ' export EDITOR='/bin/vi' export INPUTRC=/etc/inputrc export network_cfg_dir=/apps/etc/network export LD_LIBRARY_PATH=/lib:/usr/lib:/apps/lib:/apps/libs ### Some aliases alias vim='vi' alias ll='ls -l' alias l.='ls -d .*' alias df='df -h' protocols文件: //指定协议端口号 # /etc/protocols: ip 0 IP icmp 1 ICMP igmp 2 IGMP ggp 3 GGP ipencap 4 IP-ENCAP st 5 ST tcp 6 TCP egp 8 EGP pup 12 PUP udp 17 UDP hmp 20 HMP xns-idp 22 XNS-IDP rdp 27 RDP iso-tp4 29 ISO-TP4 xtp 36 XTP ddp 37 DDP idpr-cmtp 39 IDPR-CMTP rspf 73 RSPF vmtp 81 VMTP ospf 89 OSPFIGP ipip 94 IPIP encap 98 ENCAP resolv.conf文件: nameserver 4.2.2.2 nameserver 8.8.8.8 servers文件: 这个文件我们直接用虚拟机中/etc/servers文件,主要是提供的相关服务,比如ftp mtab文件: 不需要加任何内容,它记载的是现在系统已经装载的文件系统,包括操作系统建立的虚拟文件等;而/etc/fstab是系统准备装载的。直接使用mount就是通过查询它而来的。 4.在init.d目录下增加文件rcS S01_network S99_rcsApp [lingyun@localhost init.d]$ cat rcS #!/bin/sh for i in /etc/init.d/S??* ;do $i done [lingyun@localhost init.d]$ cat S99_rcsApp #!/bin/sh if (test -d /apps/etc/init.d) then for i in /apps/etc/init.d/S??* ;do $i done fi 5.编译busybox,根文件系统的命令都来自busybox 指定命令的安装路径 ramdisk即虚拟内存盘(虚拟内存盘)。 虚拟内存盘是通过软件将一部分内存(RAM)模拟为硬盘来使用的一种技术。相对于直接的硬盘文件访问来说,这种技术可以极大的提高在其上进行的文件访问的速度。但是RAM的易失性也意味着当关闭电源后这部分数据将会丢失。但是在一般情况下,传递到RAM盘上的数据都是在硬盘或别处永久贮存的文件的一个拷贝。经由适当的配置,可以实现当系统重启后重新建立虚拟盘。 简介 [1]虚拟内存盘是通过软件将一部分内存(RAM)模拟为硬盘来使用的一种技术。相对于直接的硬盘文件访问来说,这种技术可以极大的提高在其上进行的文件访问的速度。但是RAM的易失性也意味着当关闭电源后这部分数据将会丢失。但是在一般情况下,传递到RAM盘上的数据都是在硬盘或别处永久贮存的文件的一个拷贝。经由适当的配置,可以实现当系统重启后重新建立虚拟盘。 原理和用途 虚拟内存盘使用计算机内存的一部分来模拟一个硬盘。在DOS/windows下由相应的软件利用系统分配给它的内存空间来实现这种模拟。linux系统可以使用其内核支持的机制来实现。 虚拟内存盘还可以使用带有压缩机制的文件系统,例如:cramfs。这是因为一般的RAM盘的容量一般都较小,且RAM的存储空间比硬盘的要宝贵得多,价格也比硬盘要来得高,所以这样做是很合理的。 虚拟内存盘的一个用途是做为Web缓存,这样可以提高加载页面的速度,因为硬盘的存取速度远小于内存(RAM)的存取速度[2]。由于RAM的易失性,这一措施还带来了安全性上的好处[3]。 实现及软件 DOS系统:XMSDSK; Windows系统:VSuite Ramdisk; linux系统:直接格式化并挂载/dev/ramX即可(X是内存盘序号) 2010-11-0415:06:56| 分类: Linux file syste|字号 订阅 1. 准备根文件系统 创建工作目录:$mkdir /rootfs 创建根文件系统的目录:$cd /rootfs $mkdirbin dev etc home lib mnt proc sbin sys tmp var usr (12个目录) $mkdir etc/init.d 2. 创建设备文件 复制当前系统的设备文件:$cp -dpR /dev/rootfs/dev 如果使用 linux 2.6.x.x 内核,应该有节点 console、null 。如果在 /rootfs/dev 目录下没有这些节点,则转到 /rootfs/dev/目录来创建: $mknodconsole c 5 1 $mknod null c 1 3 缺少这些设备,会在启动 shell 时出现提示“Warning: unable to open an initialconsole. Kernel panic- not syncing: Attempted to kill init!” 的错误。 3. 准备目录系统启动所要的文件:linuxrc、rcS、inittab、fstab 四个文件。 linuxrc 文件(位于 "/")的内容如下: #!/bin/sh echo "mount /etc as ramfs" /bin/mount -f -t cramfs -o remount, ro /dev/bon/2 / /bin/mount -t ramfs ramfs /var /bin/mkdir -p /var/tmp /bin/mkdir -p /var/run /bin/mkdir-p /var/log /bin/mkdir -p /var/lock /bin/mkdir-p /var/empty #/bin/mount-t usbdevfs none /proc/bus/usb exec /sbin/init rcS 文件位于/etc/init.d/)的内容如下: #!/bin/sh /bin/mount-a 这连个文件生成后,应该使其具有执行的权限,用chmod 来修改 inittab 文件位于 /etc 的内容如下: #This is run first except when booting ::sysinit:/etcinit.d/rcS #Start an "askfirst" shell on the console #::askfirst: ~/bin/bash ::askfirst:~/bin/sh #Stuffto do when restarting the init process ::restart: /sbin/init #Stuff to do before rebooting :: ctrlaltdel: /sbin/reboot :: shutdown: /bin/umount -a -r fstab 文件位于 /etc 的内容如下所示: none /proc proc defaults 0 0 none /dev/pts devpts mode=0622 0 0 tmpfs /dev/shm tmpfs defaults 0 0 4. 将编译好的 BusyBox 的_install 目录下的三个文件夹用 tar 命令打包复制到 /rootfs 目录,解压后删除打包文件。 5. 将一些常用的 lib 文件复制到 /rootfs/lib/ 目录下,例如: ld-2.5.so、libc-2.5.so等文件以及其符号链接。注意这些lib 文件指的是交叉编译工具链的 lib 文件,即位于 /arm-linux-gcc/lib 下的lib 文件。在复制时应该注意采用打包后解包方式复制,以保证符号链接的正确性和完整性。 6. 生成CramFS 文件系统映像文件 cram.img $ mkcramfs/rootfs cram.img 将工作目录 rootfs 作为根目录制作 CramFS 文件系统,这将经历一个处理和压缩的过程。压缩完成后,就可以测试下生成的 cram.img 文件了。 下面命令挂载 CramFS 的文件系统: $mount -o loop-t cramfs /cram.img/mnt 将cram.img 文件系统加载到 /mnt $ ls /mnt 7. 将映像文件 cram.img 下载并写入目标板的 root 分区,正确配置 Linux 的内核启动参数,启动。 2009-11-0811:39:34| 分类: c语言杂记|字号 订阅 写一个函数,完成内存移动,并为其写一个简单的测试用例来进行测试。 够简单的吧?有的同学很快就写出了答案,详见程序清单1与程序清单2。 程序清单 1 V0.1版程序 void MyMemMove(char *dst,char *src,intcount) { while(count--) { *dst++ = *src++; } } 程序清单 2 测试用例 void Test() { char p1[256] = ”hello,world!”; char p2[256] = {0}; MyMemMove(p2,p1,strlen(p1)); printf(“%s”,p2); } 客观地讲,相比那些交白卷或者函数声明都不会写的同学来说,能够写出这段代码的同学已经非常不错了,至少在C语言这门课程上已经达到了现行高校的教育目标,但是离企业的用人要求还有一定的距离。我们不妨将上面的程序称为V0.1版本,看看还有没有什么地方可以改进。 首先我们看看函数声明是否合理,V0.1版的程序将源地址和目的地址都用char *来表示,这样当然也没有什么问题,但是让其他人使用起来却很不方便,假如现在要将count个连续的结构体对象移动到另外一个地方去,如果要使用v0.1的程序的话,正确的写法如下: MyMemMove((char *)dst,(char *)src,sizeof(TheStruct)*count) 也就是说我们需要将结构体指针强制转换成char * 才能够正常工作,这样除了字符串以外其它的类型都不可避免地要进行指针强制转换,否则编译器就会呱呱叫,比如在VC++2008下就会出现这样的错误: error C2664: 'MyMemMove' : cannot convertparameter 1 from 'TheStruct *' to 'char *' 那么如何解决这个问题呢?其实很简单,我们知道有一种特别的指针,任何类型的指针都可以对它赋值,那就是void *,所以应该将源地址和目的地址都用void*来表示。当然函数体的内容也要作相应的改变,这样我们就得到了V0.2版的程序。 程序清单 3 V0.2版程序 void MyMemMove(void *dst,void *src,intcount) { while (count--) { *(char *)dst = *(char *)src; dst = (char *)dst + 1; src = (char *)src + 1; } } 有的同学可能会问,这里面不是还有指针强制转换吗?只不过是换了地方。没错,强制指针转换确实是从使用者的代码转移到了库的代码里,但我们可以将MyMemMove理解为库,而将Test理解为使用者,事实上通过调整之后的效果却有天壤之别,V0.1是一逸永劳,而V0.2是一劳永逸! 还有几个细节需要注意,为了实现链式表达式,我们应该将返回值也改为void *。此外,如果我们不小心将“*(char*)dst = *(char *)src;”写反了,写成“*(char *)src = *(char *)dst;”编译照样通过,而为了找出这个错误又得花费不少时间。注意到src所指向的内容在这个函数内不应该被改变,所有对src所指的内容赋值都应该被禁止,所以这个参数应该用const修饰,如果有类似的错误在编译时就能够被发现: error C3892: 'src' : you cannot assign to avariable that is const 作为程序员犯错误在所难免,但是我们可以利用相对难犯错误的机器,也就是编译器来降低犯错误的概率,这样我们就得到了V0.3版的程序。 程序清单 4 V0.3版程序 void * MyMemMove(void *dst,const void*src,int count) { void *ret=dst; while (count--) { *(char *)dst = *(char *)src; dst = (char *)dst + 1; src = (char *)src + 1; } return ret; } 现在再来考虑这样一种情况,有使用者这样调用库:MyMemMove(NULL,src, count),这是完全可能的,因为一般来说这些地址都是程序计算出来的,那就难免会算错,出现零地址或者其它的非法地址也不足为奇。可以预料的是,如果出现这种情况的话,则程序马上就会down掉,更糟糕的是你不知道错误出在哪里,于是不得不投入大量的精力在浩瀚的代码中寻找bug。解决这类问题的通用办法是对输入参数作合法性检查,也就是V0.4版程序。 程序清单 5 V0.4版程序 void * MyMemMove(void *dst,const void*src,int count) { void *ret=dst; if (NULL==dst||NULL ==src) { return dst; } while (count--) { *(char *)dst = *(char *)src; dst = (char *)dst + 1; src = (char *)src + 1; } return ret; } 上面之所以写成“if(NULL==dst||NULL ==src)”而不是写成“if (dst == NULL || src == NULL)”,也是为了降低犯错误的概率。我们知道,在C语言里面“==”和“=”都是合法的运算符,如果我们不小心写成了“if (dst = NULL || src = NULL)”还是可以编译通过,而意思却完全不一样了,但是如果写成“if (NULL=dst||NULL =src)”,则编译的时候就通不过了,所以我们要养成良好的程序设计习惯:常量与变量作条件判断时应该把常量写在前面。 V0.4版的代码首先对参数进行合法性检查,如果不合法就直接返回,这样虽然程序dwon掉的可能性降低了,但是性能却大打折扣了,因为每次调用都会进行一次判断,特别是频繁的调用和性能要求比较高的场合,它在性能上的损失就不可小觑。 如果通过长期的严格测试,能够保证使用者不会使用零地址作为参数调用MyMemMove函数,则希望有简单的方法关掉参数合法性检查。我们知道宏就有这种开关的作用,所以V0.5版程序也就出来了。 程序清单 6 V0.5版程序 void * MyMemMove(void *dst,const void*src,int count) { void *ret=dst; #ifdef DEBUG if (NULL==dst||NULL ==src) { return dst; } #endif while (count--) { *(char *)dst = *(char *)src; dst = (char *)dst + 1; src = (char *)src + 1; } return ret; } 如果在调试时我们加入“#defineDEBUG”语句,增强程序的健壮性,那么在调试通过�#pragma pack
编辑本段对齐方式
gcc 错误搜集1
#pragma pack(1)
{
*****如何解决warning: no newline at end of file?
a.h:1:2: warning: no newline at end of file结构体初始化赋值={0},GCC打开-Wall选项编译会警告,大家探讨一下
警告:‘char [100]’的初始值设定周围缺少花括号
警告:‘struct_a_s’的初始值设定周围缺少花括号
因为本来数组或者结构体就得使用{}赋值,但是被嵌套了以后应该使用={{第一个成员变量的指定初始化值},第二个成员变量的指定初始化值}来初始化。故而报警了。
基于此,我分析有两种情况:
一、指定初始化只是第一个成员变量的第一个值,比如数组的第一个值,或者嵌套的小结构体的第一个成员变量的值被指定初始化为0,其他未指定的值根据gcc的编译器来初始化,但是在gcc下gdb走读代码发现并不符合“静态区初始化为0,动态区随机”规则,而是未指定的都初始化为0;
二、指定初始化只是第一个成员变量的值。windows 如何查看端口占用情况?
经常,我们在启动应用的时候发现系统需要的端口被别的程序占用,如何知道谁占有了我们需要的端口,很多人都比较头疼,下面就介绍一种非常简单的方法,希望对大家有用
假如我们需要确定谁占用了我们的9050端口
1、Windows平台
在windows命令行窗口下执行:
1.查看所有的端口占用情况
TCP 127.0.0.1:5679 0.0.0.0:0 LISTENING 4168
TCP 127.0.0.1:7438 0.0.0.0:0 LISTENING 4168
TCP 127.0.0.1:8015 0.0.0.0:0 LISTENING 1456
TCP 192.168.3.230:139 0.0.0.0:0 LISTENING 4
TCP 192.168.3.230:1957 220.181.31.225:443 ESTABLISHED 3068
TCP 192.168.3.230:2020 183.62.96.189:1522 ESTABLISHED 1456
TCP 192.168.3.230:2927 117.79.91.18:80 ESTABLISHED 4732
TCP 192.168.3.230:2929 117.79.91.18:80 ESTABLISHED 4732
TCP 192.168.3.230:2930 117.79.91.18:80 ESTABLISHED 4732
TCP 192.168.3.230:2931 117.79.91.18:80 ESTABLISHED 4732
C:\>netstat -aon|findstr "9050"
C:\>tasklist|findstr "2016"
========================= ======== ================
tor.exe 2016Console 0 16,064 K
P:很清楚吧,tor占用了你的端口。memmove、memcpy和memccpy简介
memmove(void *dest,void*src,int count)
memcpy(void *dest,void *src,int count)
memccpy(void*dest,void*src,int ch,int count)
表头文件: #include
定义函数: void *memcpy(void *dest, const void *src, size_t n)
函数说明: memcpy()用来拷贝src所指的内存内容前n个字节到dest所指的内存地址上。与strcpy()不同的是,memcpy()会完整的复制n个字节,不会因为遇到字符串结束'\0'而结束
返回值: 返回指向dest的指针
表头文件: #include
定义函数: void *memccpy(void *dest, const void *src, int c, size_tn);
函数说明: memccpy()用来拷贝src所指的内存内容前n个字节到dest所指的地址上。与memcpy()不同的是,memccpy()如果在src中遇到某个特定值(int c)立即停止复制。
返回值: 返回指向dest中值为c的下一个字节指针。返回值为0表示在src所指内存前n个字节中没有值为c的字节。
表头文件: #include
定义函数: void *memmove(void *dest, const void *src, size_t n);
函数说明:memmove()是从一个缓冲区移动到另一个缓冲区中。
返回值: 返回指向dest指针。
当dest <= src-count 或dest>= src+count时,以上三个函数均不会产生覆盖问题,即源数据不会被更改。
若不在以上范围内,则源数据会被更改。
如:
char a[]={'a','b'};
char b[]={'c','d','e','f','g','h'};
memmove(a,b,sizeof(b));
或是直接char *p=b+2;memmove(p,b,sizeof(b));
输出数据会发现b中数据输出已被更改。
发现即使a数组指向的空间不够存储数据,也能够移动成功。
原因|dest - src |
如果在使用这些函数时,分配给足够的空间,然后再使用就不会出现覆盖问题。也就是说如果外部分配给的空间不足以存储要拷贝的数据时,就有可能出现源数据被覆盖更改的问题。
#include
#include
#include
void main(void)
{
int i=0;
chara[9]={'a','b','c','d','e','f','g','h','\0'};
char p[2]={'q','w'};//或char *p=a+2;
memmove(p,a,sizeof(a));
puts(a);
printf("_____________________________________________\n");
puts(p);
printf("_____________________________________________\n");
for(i =0;i<10;i++)
printf("%c %d\n",*(a+i),a+i);
printf("_____________________________________________\n");
for(i =0;i<8;i++)
printf("%c %d\n",*(p+i),p+i);
}
观察输出结果。
把memmove(p,a,sizeof(a));改为memcpy(p,a,sizeof(a));或memccpy(p,a,'e',sizeof(a));再观察输出结果。
可以看出在目的存储空间不足时,便会出现源数据被覆盖改变的问题。
如果目的存储空间分配足够的空间,则便不会出现覆盖问题。流控制
字符串操作函数
使用pthread_mutex_t锁的例子
posix下抽象了一个锁类型的结构:ptread_mutex_t。通过对该结构的操作,来判断资源是否可以访问。顾名思义,加锁(lock)后,别人就无法打开,只有当锁没有关闭(unlock)的时候才能访问资源。
它主要用如下5个函数进行操作。
1:pthread_mutex_init(pthread_mutex_t * mutex,constpthread_mutexattr_t *attr);
初始化锁变量mutex。attr为锁属性,NULL值为默认属性。
2:pthread_mutex_lock(pthread_mutex_t *mutex);加锁
3:pthread_mutex_tylock(pthread_mutex_t *mutex);加锁,但是与2不一样的是当锁已经在使用的时候,返回为EBUSY,而不是挂起等待。
4:pthread_mutex_unlock(pthread_mutex_t *mutex);释放锁
5:pthread_mutex_destroy(pthread_mutex_t *mutex);使用完后释放
下面经典例子为创建两个线程对sum从1加到100。前面第一个线程从1-49,后面从50-100。主线程读取最后的加值。为了防止资源竞争,用了pthread_mutex_t 锁操作。
#include
#include
#include
#include
typedef struct ct_sum
{ int sum;
pthread_mutex_t lock;
}ct_sum;
void * add1(void * cnt)
{
pthread_mutex_lock(&(((ct_sum*)cnt)->lock));
int i;
for( i=0;i<50;i++)
{(*(ct_sum*)cnt).sum+=i;
}
pthread_mutex_unlock(&(((ct_sum*)cnt)->lock));
pthread_exit(NULL);
return 0;
}
void * add2(void *cnt)
{
int i;
cnt= (ct_sum*)cnt;
pthread_mutex_lock(&(((ct_sum*)cnt)->lock));
for( i=50;i<101;i++)
{ (*(ct_sum*)cnt).sum+=i;
}
pthread_mutex_unlock(&(((ct_sum*)cnt)->lock));
pthread_exit(NULL);
return 0;
}
int main(void)
{ int i;
pthread_t ptid1,ptid2;
int sum=0;
ct_sum cnt;
pthread_mutex_init(&(cnt.lock),NULL);
cnt.sum=0;
pthread_create(&ptid1,NULL,add1,&cnt);
pthread_create(&ptid2,NULL,add2,&cnt);
pthread_mutex_lock(&(cnt.lock));
printf("sum %d\n",cnt.sum);
pthread_mutex_unlock(&(cnt.lock));
pthread_join(ptid1,NULL);
pthread_join(ptid2,NULL);
pthread_mutex_destroy(&(cnt.lock));
return 0;
}linux下select 和 poll的用法
ioctl
套接口操作:
文件操作:
编辑本段定义
编辑本段必要性
编辑本段实现操作
编辑本段其他信息
Mkdir函数
返回0表示成功,返回-1表述出错。使用该函数需要包含头文件sys/stat.h
mode 表示新目录的权限,可以取以下值:
S_IRUSR
S_IREAD
Read permission bit for the owner of the file. On many systems this bit is 0400. S_IREAD is an obsolete synonym provided for BSD compatibility.
S_IWUSR
S_IWRITE
Write permission bit for the owner of the file. Usually 0200. S_IWRITE is an obsolete synonym provided for BSD compatibility.
S_IXUSR
S_IEXEC
Execute (for ordinary files) or search (for directories) permission bit for the owner of the file. Usually 0100. S_IEXEC is an obsolete synonym provided for BSD compatibility.
S_IRWXU
This is equivalent to (S_IRUSR | S_IWUSR | S_IXUSR).
S_IRGRP
Read permission bit for the group owner of the file. Usually 040.
S_IWGRP
Write permission bit for the group owner of the file. Usually 020.
S_IXGRP
Execute or search permission bit for the group owner of the file. Usually 010.
S_IRWXG
This is equivalent to (S_IRGRP | S_IWGRP | S_IXGRP).
S_IROTH
Read permission bit for other users. Usually 04.
S_IWOTH
Write permission bit for other users. Usually 02.
S_IXOTH
Execute or search permission bit for other users. Usually 01.
S_IRWXO
This is equivalent to (S_IROTH | S_IWOTH | S_IXOTH).
S_ISUID
This is the set-user-ID on execute bit, usually 04000. See How Change Persona.
S_ISGID
This is the set-group-ID on execute bit, usually 02000. See How Change Persona.
S_ISVTX
This is the sticky bit, usually 01000.信号量 sem_wait sem_post
#include
int sem_init (sem_t *sem, int pshared, unsigned int value);
这两个函数控制着信号量的值,它们的定义如下所示:
#include
int sem_wait(sem_t * sem);
int sem_post(sem_t * sem);
sem_post函数的作用是给信号量的值加上一个“1”,它是一个“原子操作”---即同时对同一个信号量做加“1”操作的两个线程是不会冲突的;而同时对同一个文件进行读、加和写操作的两个程序就有可能会引起冲突。信号量的值永远会正确地加一个“2”--因为有两个线程试图改变它。
sem_wait函数也是一个原子操作,它的作用是从信号量的值减去一个“1”,但它永远会先等待该信号量为一个非零值才开始做减法。也就是说,如果你对一个值为2的信号量调用sem_wait(),线程将会继续执行,介信号量的值将减到1。如果对一个值为0的信号量调用sem_wait(),这个函数就会地等待直到有其它线程增加了这个值使它不再是0为止。如果有两个线程都在sem_wait()中等待同一个信号量变成非零值,那么当它被第三个线程增加一个“1”时,等待线程中只有一个能够对信号量做减法并继续执行,另一个还将处于等待状态。
信号量这种“只用一个函数就能原子化地测试和设置”的能力下正是它的价值所在。还有另外一个信号量函数sem_trywait,它是sem_wait的非阻塞搭档。
最后一个信号量函数是sem_destroy。这个函数的作用是在我们用完信号量对它进行清理。下面的定义:
#include
int sem_destroy (sem_t *sem);
与其它的函数一样,这些函数在成功时都返回“0”。
#include
#include
#include
#include
#include
void *thread_function1(void *arg)
{
printf("thread_function1--------------sem_wait\n");
sem_wait(&bin_sem);
printf("sem_wait\n");
while (1)
{
}
}
{
printf("thread_function2--------------sem_post\n");
sem_post(&bin_sem);
printf("sem_post\n");
while (1)
{
}
}
int main()
{
int res;
pthread_t a_thread;
void *thread_result;
res = sem_init(&bin_sem, 0, 0);
if (res != 0)
{
perror("Semaphore initialization failed");
}
printf("sem_init\n");
res = pthread_create(&a_thread, NULL, thread_function1, NULL);
if (res != 0)
{
perror("Thread creation failure");
}
printf("thread_function1\n");
sleep (5);
printf("sleep\n");
res = pthread_create(&a_thread, NULL, thread_function2, NULL);
if (res != 0)
{
perror("Thread creation failure");
}
while (1)
{
}
}
sem_init
thread_function1
thread_function1--------------sem_wait
sleep
thread_function2--------------sem_post
sem_wait
sem_postLinux下开启/关闭防火墙命令
开启: chkconfig iptables on
关闭: chkconfig iptables off
2) 即时生效,重启后复原
开启: service iptables start
关闭: service iptables stop
需要说明的是对于Linux下的其它服务都可以用以上命令执行开启和关闭操作。
在当开启了防火墙时,做如下设置,开启相关端口,
修改/etc/sysconfig/iptables 文件,添加以下内容:
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -i ! PPP0 -j ACCEPT
#/sbin/iptables -I INPUT -p tcp –dport 80 -j ACCEPT
#/sbin/iptables -I INPUT -p tcp –dport 22 -j ACCEPT
#/etc/rc.d/init.d/iptables save
这样重启计算机后,防火墙默认已经开放了80和22端口
这里应该也可以不重启计算机:
#/etc/init.d/iptables restart
查看防火墙信息:
#/etc/init.d/iptables status
关闭防火墙服务:
#/etc/init.d/iptables stop
永久关闭?不知道怎么个永久法:
#chkconfig –level 35 iptables offLinux下配置ip、子网掩码、网关,并把他们保存在指定的文件中,每次启动后不用重新设置。
ifconfig eth0 192.168.10.50 netmask 255.255.0.0
route add default gw 192.168.1.1
2、配置网卡的配置文件
创建或修改此文件/etc/sysconfig/network-scripts/ifcfg-eth0#ifcfg-eth0
DEVICE=eth0
BOOTPROTO=static
BROADCAST=192.168.10.255
IPADDR=192.168.10.50
NETMASK=255.255.255.0
NETWORK=192.168.10.0
ONBOOT=yes
TYPE=Ethernet
USERCTL=no
PEERDNS=no
GATEWAY=
##Add to tail of /etc/sysconfig/static-routes
eth0 192.168.1.1Linux的关机与重启命令
1、reboot
2、shutdown -r now 立刻重启(root用户使用)
3、shutdown -r 10 过10分钟自动重启(root用户使用)
4、shutdown -r 20:35 在时间为20:35时候重启(root用户使用)
如果是通过shutdown命令设置重启的话,可以用shutdown -c命令取消重启
1、halt 立刻关机
2、poweroff 立刻关机
3、shutdown -h now 立刻关机(root用户使用)
4、shutdown -h 10 10分钟后自动关机
如果是通过shutdown命令设置关机的话,可以用shutdown -c命令取消重启安装RPM包或者安装源码包
Linux rpm 命令参数使用详解[介绍和应用]
二进制包(Binary)以及源代码包(Source)两种。二进制包可以直接安装在计算机中,而源代码包将会由RPM自动编译、安装。源代码包经常以src.rpm作为后缀名。
-Uvh:升级软件包--Update;
-qpl:列出RPM软件包内的文件信息[Query Package list];
-qpi:列出RPM软件包的描述信息[Query Package install package(s)];
-qf:查找指定文件属于哪个RPM软件包[Query File];
-Va:校验所有的RPM软件包,查找丢失的文件[View Lost];
-e:删除包
rpm -ivh --relocate /=/opt/gaim gaim-1.3.0-1.fc4.i386.rpm //指定安装目录rpm -ivh --test gaim-1.3.0-1.fc4.i386.rpm //用来检查依赖关系;并不是真正的安装;
rpm -Uvh --oldpackage gaim-1.3.0-1.fc4.i386.rpm //新版本降级为旧版本rpm -qa | grep httpd #[搜索指定rpm包是否安装]--all搜索*httpd*
rpm -ql httpd #[搜索rpm包]--list所有文件安装目录rpm -qpi Linux-1.4-6.i368.rpm #[查看rpm包]--query--package--install package信息
rpm -qpf Linux-1.4-6.i368.rpm #[查看rpm包]--file
rpm -qpR file.rpm #[查看包]依赖关系
rpm2cpio file.rpm |cpio -div #[抽出文件]
rpm -ivh
rpm -e file.rpm #[删除一个rpm包]--erase
-v, --verbose provide more detailed output
-h, --hash print hash marks as package installs (good with -v)
-e, --erase erase (uninstall) package
-U, --upgrade=
--test 安装测试,并不实际安装
--nodeps 忽略软件包的依赖关系强行安装
--force 忽略软件包及文件的冲突Query options (with -q or --query):
-a, --all query/verify all packages
-p, --package query/verify a package file
-l, --list list files in package
-d, --docfiles list all documentation files
-f, --file query/verify package(s) owning file
来自:LinuxSir.Org
提要:RPM 是 Red Hat PackageManager 的缩写,原意是Red Hat 软件包管理;本文介绍RPM,并结合实例来解说RPM手工安装、查询等应用;
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
正文:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
本文使用范围:
1、本文是对RPM管理的软件的说明,对通过file.tar.gz 或file.tar.bz2源码包用 make ;make install 安装的软件无效;
2、安装软件时,最好用各自发行版所提供的系统软件包管理工具,对于Fedora/Redhat 您可以参考如下文章;
4)yum 软件包在线安装、升级、移除工具;用法:《Fedora/Redhat 在线安装更新软件包,yum 篇》
一、RPM包管理的用途;
2、通过RPM包管理能知道软件包包含哪些文件,也能知道系统中的某个文件属于哪个软件包;
3、可以在查询系统中的软件包是否安装以及其版本;
4、作为开发者可以把自己的程序打包为RPM 包发布;
5、软件包签名GPG和MD5的导入、验证和签名发布
6、依赖性的检查,查看是否有软件包由于不兼容而扰乱了系统;
二、RPM的使用权限;
RPM软件的安装、删除、更新只有root权限才能使用;对于查询功能任何用户都可以操作;如果普通用户拥有安装目录的权限,也可以进行安装;
三、rpm的一点简单用法;
我们除了软件包管理器以外,还能通过rpm 命令来安装;是不是所有的软件包都能通过rpm 命令来安装呢?不是的,文件以.rpm 后缀结尾的才行;有时我们在一些网站上找到file.rpm ,都要用 rpm 来安装;
一)初始化rpm数据库;
通过rpm 命令查询一个rpm 包是否安装了,也是要通过rpm 数据库来完成的;所以我们要经常用下面的两个命令来初始化rpm 数据库;[root@localhostbeinan]# rpm --initdb
[root@localhost beinan]# rpm --rebuilddb
注:这个要花好长时间;
二)RPM软件包管理的查询功能:
命令格式rpm {-q|--query}[select-options] [query-options]
1、对系统中已安装软件的查询;语法:
rpm-q
软件名
[root@localhostbeinan]# rpm -q gaim
gaim-1.3.0-1.fc4
[root@localhostRPMS]# rpm -qa
[root@localhostRPMS]# rpm -qa |more
[root@localhostRPMS]# rpm -qa |grep gaim
语法
rpm -qf
文件名
[root@localhostRPMS]# rpm -qf /usr/lib/libacl.la
libacl-devel-2.2.23-8
语法:
rpm-ql
软件名
或
rpm rpmquery -ql
软件名
[root@localhostRPMS]# rpm -ql lynx
[root@localhost RPMS]# rpmquery -ql lynx
语法格式:
rpm -qi
软件名
[root@localhostRPMS]# rpm -qi lynx
语法格式:
rpm-qc
软件名
[root@localhostRPMS]# rpm -qc lynx
语法格式:
rpm -qd
软件名
[root@localhostRPMS]# rpm -qd lynx
语法格式:
rpm -qR
软件名
[root@localhostbeinan]# rpm -qR rpm-python
[root@localhostRPMS]# rpm -qil lynx
2、对于未安装的软件包的查看:
查看的前提是您有一个.rpm 的文件,也就是说对既有软件file.rpm的查看等;语法:
rpm -qpi file.rpm
[root@localhostRPMS]# rpm -qpi lynx-2.8.5-23.i386.rpm
语法:
rpm -qpl file.rpm
[root@localhostRPMS]# rpm -qpl lynx-2.8.5-23.i386.rpm
语法:
rpm -qpd file.rpm
[root@localhostRPMS]# rpm -qpd lynx-2.8.5-23.i386.rpm
语法:
rpm -qpc file.rpm
[root@localhostRPMS]# rpm -qpc lynx-2.8.5-23.i386.rpm
语法:
rpm -qpR file.rpm
[root@localhostarchives]# rpm -qpR yumex_0.42-3.0.fc4_noarch.rpm
/bin/bash
/usr/bin/python
config(yumex) = 0.42-3.0.fc4
pygtk2
pygtk2-libglade
rpmlib(CompressedFileNames) <= 3.0.4-1
rpmlib(PayloadFilesHavePrefix) <= 4.0-1
usermode
yum >= 2.3.2
三)软件包的安装、升级、删除等;
1、安装和升级一个rpm 包;[root@localhostbeinan]#rpm -vih file.rpm
注:这个是用来安装一个新的
rpm
包;
[root@localhost beinan]#rpm -Uvh file.rpm
注:这是用来升级一个
rpm
包;
[root@localhostbeinan]# rpm -ivh file.rpm --nodeps --force
[root@localhost beinan]# rpm -Uvh file.rpm --nodeps --force
[root@localhostRPMS]# rpm -ivh lynx-2.8.5-23.i386.rpm
Preparing... ########################################### [100%]
1:lynx########################################### [100%]
[root@localhost RPMS]# rpm -ivh --replacepkgslynx-2.8.5-23.i386.rpm
Preparing... ########################################### [100%]
1:lynx########################################### [100%]
[root@localhostRPMS]# rpm -ivh --test gaim-1.3.0-1.fc4.i386.rpm
Preparing... ########################################### [100%]
[root@localhostRPMS]# rpm -qa gaim
gaim-1.5.0-1.fc4
[root@localhost RPMS]# rpm -Uvh --oldpackagegaim-1.3.0-1.fc4.i386.rpm
Preparing... ########################################### [100%]
1:gaim########################################### [100%]
[root@localhost RPMS]# rpm -qa gaim
gaim-1.3.0-1.fc4
[root@localhostRPMS]# rpm -ivh --relocate /=/opt/gaim gaim-1.3.0-1.fc4.i386.rpm
Preparing... ########################################### [100%]
1:gaim########################################### [100%]
[root@localhost RPMS]# ls /opt/
gaim
[root@localhost RPMS]# rpm -ivh --relocate /=/opt/lynx --badreloclynx-2.8.5-23.i386.rpm
Preparing... ########################################### [100%]
1:lynx ########################################### [100%][root@localhostRPMS]# /opt/lynx/usr/bin/lynx
Configuration file /etc/lynx.cfg is not available.
[root@localhost RPMS]# ln -s /opt/lynx/etc/lynx.cfg/etc/lynx.cfg
[root@localhost RPMS]# /opt/lynx/usr/bin/lynx www.linuxsir.org
2、删除一个rpm 包;[root@localhostRPMS]# rpm -e lynx
[root@localhostbeinan]# rpm -e lynx --nodeps
四、导入签名:
[root@localhost RPMS]# rpm --import 签名文件[root@localhostfc40]# rpm --import RPM-GPG-KEY
[root@localhost fc40]# rpm --import RPM-GPG-KEY-fedora
五、RPM管理包管理器支持网络安装和查询;
比如我们想通过 Fedora Core 4.0 的一个镜像查询、安装软件包;rpm
参数
rpm
包文件的
http
或者
ftp
的地址
# rpm -qpi
http://mirrors.kernel.org/fedora/core/4/i386/os/
Fedora/RPMS/gaim-1.3.0-1.fc4.i386.rpm
# rpm -ivh
http://mirrors.kernel.org/fedora/core/4/i386/os/
Fedora/RPMS/gaim-1.3.0-1.fc4.i386.rpm
六、对已安装软件包查询的一点补充;[root@localhostRPMS]# updatedb
[root@localhost RPMS]# locate
软件名或文件名
[root@localhostRPMS]# locate gaim
七、从rpm软件包抽取文件;命令格式:
rpm2cpio file.rpm |cpio -div
[root@localhostRPMS]# rpm2cpio gaim-1.3.0-1.fc4.i386.rpm |cpio -div
[root@localhostRPMS]# rpm -ivh --relocate /=/opt/gaim gaim-1.3.0-1.fc4.i386.rpm
Preparing... ########################################### [100%]
1:gaim########################################### [100%]
[root@localhost RPMS]# ls /opt/
gaim
八、RPM的配置文件;
RPM包管理,的配置文件是 rpmrc ,我们可以在自己的系统中找到;比如Fedora Core 4.0中的rpmrc 文件位于;[root@localhostRPMS]# locate rpmrc
/usr/lib/rpm/rpmrc
/usr/lib/rpm/redhat/rpmrc
九、src.rpm的用法:
《file.src.rpm 使用方法的简介》
后记:Fedora/Redhat 入门教程中的软件包管理篇,我已经写了很多了;目前还缺少通过源码包安装软件我方法以及一篇总结性的文档;我想在最近两天补齐,这两篇我以前写过;重新整理一下贴出来就行了;GCC警告选项例解
程序员是追求完美的一族,即使是一般的程序员大多也都不想看到自己的程序中有甚至那么一点点的瑕疵。遇到任意一条编译器警告都坚决不放过。有人会说:我们可以使用比编译器更加严格的静态代码检查工具,如
splint
。 这个建议也很不错。不过lint工具使用起来较繁琐,有时候还需要记住一些特定符号并插入到你自己的代码中才行,门槛较高,这也让很多人止步于此。那么我 们就从此放弃么?不,如今的编译器做得都很好,它可以帮助我们的找到绝大多数可能出现问题的代码,前提是你要学会控制编译器去找到这些问题代码,而熟悉编 译器的警告选项恰恰是体现控制力的好方法。当你可以自如控制编译器警告输出的时候,你就算是’入道’了,同时你对语言的理解也更进一步了。
有人说:我就是用一个-Wall选项就可以了,一般选手可以这么做,而且他可以不知道-Wall会跟踪哪些类型的问题;但是高级选手是不会只使用-Wall的,他会把每条警告都研究的很透彻,会在Makefile中列出他想让编译器输出哪些类型的警告以替代-Wall,他会屏蔽掉那些对他的代码’毫 无用处’的警告(很可能他使用了编译器对语言的扩展功能),他会有个和编译器交流的过程。
俗话说:’工欲善其事,必先利其器’,一直在工作中使用
GNU C
编译器(以下简称GCC),这里对GCC的一些警告选项细致的分析,并列举几个简单的例子[注1]供分析参考。
1. -Wall集合警告选项
我们平时可能大多数情况只使用-Wall编译警告选项,实际上-Wall选项是一系列警告编译选项的集合。下面逐一分析这一集合中的各个选项:
[-Wchar-subscripts]
如果数组使用char类型变量做为下标值的话,则发出警告。因为在某些平台上char可能默认为signedchar,一旦溢出,就可能导致某些意外的结果。
e.g.
/* test_signed_char.c */
#include
int main () {
char c = 255; // 我们以为char是无符号的,其范围应该是[0,255]
int i = 0;
int a[256];
for (i = 0; i type `char’
其输出结果:
-1
-41Array7476
1
从输出结果来看SolarisArray/gcc 3.2上char默认实现类型为signedchar;在WindowsXP/gcc-3.4.2上也是一样。
Windows上的输出结果:
-1
16 (随机值)
1
[-Wcomment]
当’/*’出现在 ’/* ...*/’注释中,或者’\’出现在’// ...’注释结尾处时,使用-Wcomment会给出警告。不要小觑这些马虎代码,它很可能会影响程序的运行结果。如下面的例子:
e.g.
/*
* test_comment.c
* gcc -Wcomment test_comment.c
*/
#include
int main() {
int a = 1;
int b = 2;
int c = 0; // ok just test\
c = a + b;
/*
* 这里我们期待c = 3
* /* 但实际上输出c = 0
*/
printf("the c is %d\n",c);
return 0;
}
gcc -Wcomment test_comment.c
test_comment.c:10:30: warning: multi-line comment
test_comment.c:15:12: warning: "/*" within comment
输出:
the c is 0
[-Wformat]
检查printf和scanf等格式化输入输出函数的格式字符串与参数类型的匹配情况,如果发现不匹配则发出警告。某些时候格式字符串与参数类型的不匹配会导致程序运行错误,所以这是个很有用的警告选项。
e.g.
/*
* test_format.c
*/
#include
int main() {
long l = 1;
double d = 55.67;
printf("%d\n",l);
printf("%d\n",d);
return 0;
}
gcc -Wformat test_format.c
test_format.c: In function `main’:
test_format.c:10: warning: int format, long int arg (arg 2)
test_format.c:11: warning: int format, double arg (arg 2)
输出:
1
1078711746
[-Wimplicit]
该警告选项实际上是-Wimplicit-int和-Wimplicit-function-declaration两个警告选项的集合。前者在声明函数却未指明函数返回类型时给出警告,后者则是在函数声明前调用该函数时给出警告。
e.g.
/*
* test_implicit.c
*/
#include
add(int a, int b) { //函数没有声明返回类型
return a + b;
}
int test() {
int a = 0;
int b = 0;
int c = 0;
int d = 0;
c = add(a, b);
d = sub(a, b); //未声明sub的函数原型
return 0;
}
gcc -Wimplicit -c test_implicit.c
test_implicit.c:7: warning: return type defaults to `int’
test_implicit.c: In function `test’:
test_implicit.c:18: warning: implicit declaration of function `sub’
[-Wmissing-braces]
当聚合类型或者数组变量的初始化表达式没有’充分’用括号{}括起时,给出警告。文字表述很难理解,举例说明则清晰些。看下面的例子:
e.g.
/*
* test_missing_braces.c
*/
struct point {
int x;
int y;
};
struct line {
struct point start;
struct point end;
};
typedef struct line line;
int main() {
int array1[2][2] = {11, 12, 13, 14};
int array2[2][2] = {{11, 12}, {13, 14}}; // ok
line l1 = {1, 1, 2, 2};
line l2 = {{2, 2}, {3, 3}};// ok
return 0;
}
gcc -Wmissing-braces test_missing_braces.c
test_missing_braces.c: In function `main’:
test_missing_braces.c:1Array: warning: missing braces around initializer
test_missing_braces.c:1Array: warning: (near initialization for `array1[0]’)
test_missing_braces.c:21: warning: missing braces around initializer
test_missing_braces.c:21: warning: (near initialization for `l1.start’)
[-Wparentheses]
这是一个很有用的警告选项,它能帮助你从那些看起来语法正确但却由于操作符优先级或者代码结构’障眼’而导致错误运行的代码中解脱出来。好长的一个长句,还是看例子理解吧!:)
e.g.
/*
* test_parentheses.c
* gcc -Wparentheses test_parentheses.c
*/
#include
int main() {
int a = 1;
int b = 1;
int c = 1;
int d = 1;
if (a && b || c) { // 人们很难记住逻辑操作符的操作顺序,所以编译器建议加上()
;
}
if (a == 12)
if(b)
d = Array;
else
d= 10; //从代码的缩进上来看,这句仿佛是if(a == 12)的else分支
printf("the d is %d\n",d); //期待d= 10, 而结果却是1
return 0;
}
gcc -Wparentheses test_parentheses.c
test_parentheses.c: In function `main’:
test_parentheses.c:13: warning: suggest parentheses around && within ||
test_parentheses.c:17: warning: suggest explicit braces to avoid ambiguous`else’
输出:
the d is 1
[-Wsequence-point]
关于顺序点(sequencepoint),在C标准中有解释,不过很晦涩。我们在平时编码中尽量避免写出与实现相关、受实现影响的代码便是了。而-Wsequence-point选项恰恰可以帮我们这个忙,它可以帮我们查出这样的代码来,并给出其警告。
e.g.
/*
* test_sequence_point.c
* gcc -Wsequence-point test_sequence_point.c
*/
#include
int main() {
int i = 12;
i = i--;
printf("the i is %d\n",i);
return 0;
}
gcc -Wsequence-point test_sequence_point.c
test_sequence_point.c: In function `main’:
test_sequence_point.c:10: warning: operation on `i’ may be undefined
在两个平台上给出的编译警告都是一致的,但是输出结果却大相径庭。
Solaris输出:
the i is 11
Windows输出:
the i is 12
类似的像这种与顺序点相关的代码例子有:
i = i++;
a =b[i++]
a[i++] = i
等等...
[-Wswitch]
这个选项的功能浅显易懂,通过文字描述也可以清晰的说明。当以一个枚举类型(enum)作为switch语句的索引时但却没有处理default情况,或者没有处理所有枚举类型定义范围内的情况时,该选项会给处警告。
e.g.
/*
* test_switch1.c
*/
enum week {
SUNDAY,
MONDAY,
TUESDAY /* only an example , we omittedthe others */
};
int test1() {
enum week w = SUNDAY;
switch(w) {
case SUNDAY:
break; // without default or the other casehandlings
};
return 0;
}
int test2() { // Ok, won’t invoke even a warning
enum week w = SUNDAY;
switch(w) {
case SUNDAY:
break;
default:
break;
};
return 0;
}
int test3() { // Ok, won’t invoke even a warning
enum week w = SUNDAY;
switch(w) {
case SUNDAY:
break;
case MONDAY:
break;
case TUESDAY:
break;
};
return 0;
}
gcc -Wswitch -c test_switch.c
test_switch.c: In function `test1’:
test_switch.c:16: warning: enumeration value `MONDAY’ not handled in switch
test_switch.c:16: warning: enumeration value `TUESDAY’ not handled in switch
[-Wunused]
-Wunused是-Wunused-function、-Wunused-label、-Wunused-variable、-Wunused-value选项的集合,-Wunused-parameter需单独使用。
(1) -Wunused-function用来警告存在一个未使用的static函数的定义或者存在一个只声明却未定义的static函数,参见下面例子中的func1和func2;
(2) -Wunused-label用来警告存在一个使用了却未定义或者存在一个定义了却未使用的label,参加下面例子中的func3和func7;
(3) -Wunused-variable用来警告存在一个定义了却未使用的局部变量或者非常量static变量;参见下面例子中func5和var1;
(4) -Wunused-value用来警告一个显式计算表达式的结果未被使用;参见下面例子中func6
(5) -Wunused-parameter用来警告一个函数的参数在函数的实现中并未被用到,参见下面例子中func4。
下面是一个综合的例子
e.g.
/*
* test_unused.c
*/
static void func1(); //to prove function used but never defined
static void func2(); //to prove function defined but not used
static void func3(); //to prove label used but never defined
static void func7(); //to prove label defined but never used
static void func4(int a); //to prove parameter declared but not used
static void func5(); //to prove local variable defined but not used
static void func6(); //to prove value evaluated but not used
static int var1;
void test() {
func1();
func3();
func4(4);
func5();
func6();
}
static void func2() {
; // do nothing
}
static void func3() {
goto over;
}
static void func4(int a) {
; // do nothing
}
static void func5() {
int a = 0;
}
static void func6() {
int a = 0;
int b = 6;
a + b;
}
gcc -Wunused-parameter -c test_unused.c //如果不是用-Wunused-parameter,则func4函数将不被警告。
test_unused.c: In function `func3’:
test_unused.c:30: label `over’ used but not defined
test_unused.c: In function `func7’:
test_unused.c:35: warning: deprecated use of label at end of compound statement
test_unused.c:34: warning: label `over’ defined but not used
test_unused.c: In function `func4’:
test_unused.c:37: warning: unused parameter `a’
test_unused.c: In function `func5’:
test_unused.c:42: warning: unused variable `a’
test_unused.c: In function `func6’:
test_unused.c:48: warning: statement with no effect
test_unused.c: At top level:
test_unused.c:6: warning: `func1’used but never defined
test_unused.c:25: warning: `func2’defined but not used
test_unused.c:14: warning: `var1’defined but not used
[-Wuninitialized]
该警告选项用于检查一个局部自动变量在使用之前是否已经初始化了或者在一个longjmp调用可能修改 一个non-volatileautomatic variable时给出警告。目前编译器还不是那么smart,所以对有些可以正确按照程序员的意思运行的代码还是给出警告。而且该警告选项需要和’-O’选项一起使用,否则你得不到任何uinitialized的警告。
e.g.
/*
* test_uninitialized.c
*/
int test(int y) {
int x;
switch (y) {
case 1:
x = 11;
break;
case 2:
x = 22;
break;
case 3:
x = 33;
break;
}
return x;
}
gcc -Wuninitialized -O -c test_uninitialized.c
test_uninitialized.c: In function `test’:
test_uninitialized.c:6: warning: `x’ might be used uninitialized in thisfunction
2、非-Wall集合警告选项
以下讨论的这些警告选项并不包含在-Wall中,需要程序员显式添加。
[-Wfloat-equal]
该项用来检查浮点值是否出现在相等比较的表达式中。
e.g.
/*
* test_float_equal.c
*/
void test(int i) {
double d = 1.5;
if (d == i) {
;
}
}
gcc -Wfloat-equal -c test_float_equal.c
test_float_equal.c: In function `test’:
test_float_equal.c:8: warning: comparing floating point with == or != is unsafe
[-Wshadow]
当局部变量遮蔽(shadow)了参数、全局变量或者是其他局部变量时,该警告选项会给我们以警告信息。
e.g.
/*
* test_shadow.c
*/
int g;
void test(int i) {
short i;
double g;
}
gcc -Wshadow -c test_shadow.c
test_shadow.c: In function `test’:
test_shadow.c:Array: warning: declaration of `i’ shadows a parameter
test_shadow.c:10: warning: declaration of `g’ shadows a global declaration
test_shadow.c:6: warning: shadowed declaration is here
[-Wbad-function-cast]
当函数(准确地说应该是函数返回类型)被转换为非匹配类型时,均产生警告。
e.g.
/*
* test_bad_func_case.c
*/
int add(int a, int b) {
return a+b;
}
void test() {
char *p = (char*)add(1, 13);
}
gcc -Wbad-function-cast -c test_bad_func_case.c
test_bad_func_case.c: In function `test’:
test_bad_func_case.c:11: warning: cast does not match function type
[-Wcast-qual]
当去掉修饰源Target的限定词(如const)时,给出警告。
e.g.
/*
* test_cast_qual.c
*/
void test() {
char c = 0;
const char *p = &c;
char *q;
q = (char*)p;
}
gcc -Wcast-qual -c test_cast_qual.c
test_cast_qual.c: In function `test’:
test_cast_qual.c:10: warning: cast discards qualifiers from pointer target type
[-Wcast-align]
这是个非常有用的选项,特别是对于在Solaris这样的对内存对齐校验的平台尤其重要。它用于在从对齐系数小的地址(如char*)转换为对齐系数大的地址(如int*)转换时给出警告。
e.g.
/*
* test_cast_align.c
*/
#include
int main() {
char c = 1;
char *p =&c; //ok
int *q =(int*)p; //bad align-cast
printf("the *q is %d\n",*q);
return 0;
}
gcc -Wcast-align test_cast_align.c
test_cast_align.c: In function `main’:
test_cast_align.c:Array: warning: cast increases required alignment of targettype
输出:
总线错误((主存储器)信息转储)//on Solaris Array
[-Wsign-compare]
在有符号数和无符号数进行值比较时,有符号数可能在比较之前被转换为无符号数而导致结果错误。使用该选项会对这样的情况给出警告。
e.g.
/*
* test_sign_compare.c
*/
#include
int main() {
unsigned int i =128;
signed int j =-1;
if (i j\n");
}
return 0;
}
gcc -Wsign-compare test_sign_compare.c
test_sign_compare.c: In function `main’:
test_sign_compare.c:10: warning: comparison between signed and unsigned
输出:
i type
test_unreachable.c:Array: warning: will never be executed
[-Wconvertion]
由于原型定义而引起的定点和浮点数之间的隐式转换(强制转换)或者由有符号数和无符号数之间隐式转换转换引起的警告。
e.g.
/*
* test_conversion.c
*/
#include
void getdouble(double d) {
; // do nothing
}
int main() {
unsigned int k;
int n = 12;
k = -1;
k = (unsigned int)-1; // ok, explicitconversion ,no warning
getdouble(n);
return 0;
}
gcc -Wconversion test_conversion.c
test_conversion.c: In function `main’:
test_conversion.c:15: warning: negative integer implicitly converted tounsigned type
test_conversion.c:18: warning: passing arg 1 of `getdouble’ as floating ratherthan integer due to prototype
3、-Wtraditional和-W
这两个警告选项其实也都是一些组合(大部分都在上面提到过),前者用来在代码中使用了标准C不同于传统C的特性时,发出警告;后者也是针对一些事件打开一个警告集合。关于它们的说明具体可参见’
Using the GNU Compiler CollectionCsocket基本原理
while(!Accept(...))
{
if (GetLastError() == WSAEWOULDBLOCK) // The socket ismarked as nonblocking and no connections are present to be accepted.
PumpMessage(FD_ACCEPT);
else
return FALSE;
}
LRESULT CSocketWnd::OnSocketNotify(WPARAMwParam, LPARAM lParam)
{
CSocket::AuxQueueAdd(WM_SOCKET_NOTIFY, wParam, lParam);
CSocket::ProcessAuxQueue();
return 0L ;
}
ProcessAuxQueue 是实质处理socket 事件的函数,在该函数中有这样一句代码:
在这里,事件类型是FD_ACCEPT ,当然就调用pSocket->OnAccept !
文中存在用词不妥和可能存在的技术问题,请大家原谅,也请批评指正,谢谢!
注:从问题看本质:socket到底是什么?
1)服务端在8088上监听,然后生成一个新的socket与client通讯。(注意:服务器端监听端口是
不变的,但socket连接可以一直生成,一个线程对应一个socket.)
同一时刻,一个端口只能建立一个连接。
在一个端口监听,但是在监听端口的同时,生成一个等待队列,每一个来自客户端的连接都会送入等待队列中,服务器利用一定的算法进行选择相应的连接请求处理,所以在一个端口可以监听多各请求嘛。如果同时的连接过多,服务器相应每个连接的时间就会相应的变长。就会变慢。
2)QQ的实现方法就是在登陆的时候告诉服务器你已经登陆,发送消息的时候,首先你会发送一个包给服务器,服务器查看你需要发送的对方是否在线,如果在线就返回包告诉你对方在线,然后你就会和对方建立一个连接,然后直接发消息给对方,如果对方不在线,那么就会通过服务器转发你这次的消息
3)网络IO数与你的CPU数目一致将是比较好的选择(考虑到多线程多进程可以提高效率)。
没有必要每个客户分配一个端口。绝对是一种无谓的浪费。
4.有人知道socket监听的一个端口.最多可以有多少个客户连接?
1)listen()中有个参数,应该是确定并行连接客户数?!
2)The maximum length of the queue of pending connections. If this value is SOMAXCONN, then the underlying service provider responsible for socket s will set the backlog to a maximum "reasonable " value. There is no standard provision to find out the actual backlog value.
3)linux2.4下,最多可以有1024个socket连接
4)同时连接请求好像是5个(是连接请求,不是连接),可保持的链接理论上是65535(2字节的SOCKET端口号),
答:C和S是相对而言的,发起连接的一方就是C,而监听端口接受连接的一方就是S,C如果不知道S监听的端口,怎么发起连接呢,
另外,对于S而言,端口是S上各个服务的区分标志,如果用错误的端口号去连接,是不能获得正确的服务的。
client的端口是不需要指定的,Server绑定端口,然后监听,client使用server的IP和端口建立socket连接
又说“通过socket()函数可以创建一个套接字,然后再把它绑定到端口号...”
那么套接字socket的概念究竟到哪里为止呢?是仅限于socket()返回的文件描述符?还是是IP和端口号的组合?如果是,那么socket()调用之后产生的套接字描述符的作用是什么呢? 套接字描述符,IP地址,端口号三者间的关系是怎样的?
谢谢各位前辈解答。
答:一个socket句柄代表两个地址对 “本地ip:port”--“远程ip:port”
问:那么socket的概念到底到那里为止呢?比如,利用socket()可以产生一个套接字句柄,可是在bind() 或者 connect () 之前它只是一个文件描述符,和linux中其他的文件描述符一样。
如果说socket代表一个两个地址对,那么句柄的作用是不是仅仅是在bind() 或者 connect () 之后的用于区分和标记这样的地址对呢?因为这样他才能和网络的概念联系起来。这样的话,socket的意义应该是说用文件描述符描述的通信双方的IP地址和端口号地址对?(而文件描述符是区分这些地址对的标记?)
答:socket为内核对象,由操作系统内核来维护其缓冲区,引用计数,并且可以在多个进程中使用。
至于称它为“句柄”“文件描述符”都是一样的,它只不过是内核开放给用户进程使用的整数而已
问:谢谢楼上,是我没描述清楚。对于“句柄”和“文件描述符”我没有异议。
我想我的问题是在于句柄和ip、port的关系,不知道我这样说对否:
1. 每一个socket 本质上都是指一个ip地址和端口对
2. 为了操作这样的地址对,使用了文件描述符
3. socket()函数只创建了一个普通的文件描述符,在进行bind()或者connect()之前并不能说创建了用于网络通讯的套接字
4. 只有在进行了bind()或者connect()之后socket才被创立起来
答:socket()创建了一个socket内核对象。
accept或者connect后,才可以对socket句柄读写。因为只有在 connect或者bind,listen,accept后才会设置好socket内核对象里边的ip和端口
在windows下叫句柄,在linux下叫文件描述符
socket为内核对象,由操作系统内核来维护其缓冲区,引用计数,并且可以在多个进程中使用。 至于称它为“句柄”“文件描述符”都是一样的
我假定读者已经对于socket连接的建立过程和各种状态转换比较熟悉了,因为这篇文档的目的是澄清概念,而不是介绍概念。
在使用socket编程时,我们都知道在网络通信以前首先要建立连接,而连接的建立是通过对socket的一些操作来完成的。那么,建立连接的过程大致可以分为以下几步:
1)建立socket套接字。
2)给套接字赋予地址,这个地址不是通常的网络地址的概念。
3)建立socket连接。
1.建立socket套接字。
使用socket建立套接字的时候,我们实际上是建立了一个数据结构。这个数据结构最主要
的信息是指定了连接的种类和使用的协议,此外还有一些关于连接队列操作的结构字段
(这里就先不涉及他们了)。
当我们使用socket函数以后,如果成功的话会返回一个int型的描述符,它指向前面那个
被维护在内核里的socket数据结构。我们的任何操作都是通过这个描述符而作用到那个数
据结构上的。这就像是我们在建立一个文件后得到一个文件描述符一样,对文件的操作都
是通过文件描述符来进行的,而不是直接作用到inode数据结构上。我之所以用文件描述
符举例,是因为socket数据结构也是和inode数据结构密切相关,它不是独立存在于内核
中的,而是位于一个VFS inode结构中。所以,有一些比较抽象的特性,我们可以用文件
操作来不恰当的进行类比以加深理解。
如前所述,当建立了这个套接字以后,我们可以获得一个象文件描述符那样的套接字描述
符。就象我们对文件进行操作那样,我们可以通过向套接字里面写数据将数据传送到我们
指定的地方,这个地方可以是远端的主机,也可以是本地的主机。如果你有兴趣的话,还
可以用socket机制来实现IPC,不过效率比较低,试试也就行了(我没有试过)。
2.给套接字赋予地址。
依照建立套接字的目的不同,赋予套接字地址的方式有两种:服务器端使用bind,客户端
使用connetc。
Bind:
我们都知道,只要使用IP, prot就可以区分一个tcp/ip连接(当然这个连接指的是一个
连接通道,如果要区分特定的主机间的连接,还需要第三个属性 hostname)。
我们可以使用bind函数来为一个使用在服务器端例程中的套接字赋予通信的地址和端口。
在这里我们称通信的IP地址和端口合起来构成了一个socket地址,而指定一个socket使
用特定的IP和port组合来进行通行的过程就是赋予这个socket一个地址。
要赋予socket地址,就得使用一个数据结构来指明特定的socket地址,这个数据结构就
是struct sockaddr。对它的使用我就不说了,因为这篇文档的目的是澄清概念而不是说
明使用方法。Bind函数的作用就是将这个特定的标注有socket地址信息的数据结构和
socket套接字联系起来,即赋予这个套接字一个地址。但是在具体实现上,他们两个是怎
么联系在一起的,我还不知道。
一个特定的socket的地址的生命期是bind成功以后到连接断开前。你可以建立一个
socket数据结构和socket地址的数据结构,但是在没有bind以前他们两个是没有关系
的,在bind以后他们两个才有了关系。这种关系一直维持到连接的结束,当一个连接结束
时,socket数据结构和socket地址的数据结构还都存在,但是他们两个已经没有关系
了。如果你要是用这个套接字在socket地址上重新进行连接时,需重新bind他们两个。再
注明一次,我说的这个连接是一个连接通道,而不是特定的主机之间的连接。
Bind指定的IP通常是本地IP(一般不特别指定,而使用INADDR_ANY来声明),而最主要
的作用是指定端口。在服务器端的socket进行了bind以后就是用listen来在这个socket
地址上准备进行连接。
connect:
对于客户端来说,是不会使用bind的(并不是不能用,但没什么意义),他们会通过
connet函数来建立socket和socket地址之间的关系。其中的socket地址是它想要连接的
服务器端的socket地址。在connect建立socket和socket地址两者关系的同时,它也在
尝试着建立远端的连接。
3.建立socket连接。
对于准备建立一个连接,服务器端要两个步骤:bind, listen;客户端一个步骤:
connct。如果服务器端accept一个connect,而客户端得到了这个accept的确认,那么
一个连接就建立了。
半双工通信
MFC打开/保存文件对话框:CFileDialog
文件选择对话框的使用:首先构造一个对象并提供相应的参数,构造函数原型如下:
CFileDialog::CFileDialog( BOOL bOpenFileDialog,
);
参数意义如下:
bOpenFileDialog 为TRUE则显示打开对话框,为FALSE则显示保存对话文件对话框。
lpszDefExt 指定默认的文件扩展名。
lpszFileName 指定默认的文件名。
dwFlags 指明一些特定风格。
lpszFilter 是最重要的一个参数,它指明可供选择的文件类型和相应的扩展名。参数格式如:
"Chart Files (*.xlc)|*.xlc|Worksheet Files(*.xls)|*.xls|Data Files (*.xlc;*.xls)|*.xlc; *.xls|All Files (*.*)|*.*||";文件类型说明和扩展名间用 | 分隔,同种类型文件的扩展名间可以用 ; 分割,每种文件类型间用 | 分隔,末尾用 || 指明。
pParentWnd 为父窗口指针。
创建文件对话框可以使用DoModal(),在返回后可以利用下面的函数得到用户选择:
CString CFileDialog::GetPathName( ) 得到完整的文件名,包括目录名和扩展名如:c: est est1.txt
CString CFileDialog::GetFileName( ) 得到完整的文件名,包括扩展名如:test1.txt
CString CFileDialog::GetExtName( ) 得到完整的文件扩展名,如:txt
CString CFileDialog::GetFileTitle ( ) 得到完整的文件名,不包括目录名和扩展名如:test1
POSITION CFileDialog::GetStartPosition( ) 对于选择了多个文件的情况得到第一个文件位置。
CString CFileDialog::GetNextPathName( POSITION& pos ) 对于选择了多个文件的情况得到下一个文件位置,并同时返回当前文件名。但必须已经调用过POSITION CFileDialog::GetStartPosition( )来得到最初的POSITION变量。
例如
{
CString
FilePathName;
CFileDialog dlg(TRUE);///TRUE为OPEN对话框,FALSE为SAVE AS对话框
FilePathName=dlg.GetPathName();
}
相关信息:CFileDialog用于取文件名的几个成员函数:
假如选择的文件是C:WINDOWSTEST.EXE
则:
(1)GetPathName();取文件名全称,包括完整路径。取回C:WINDOWSTEST.EXE
(2)GetFileTitle();取文件全名:TEST.EXE
(3)GetFileName();取回TEST
(4)GetFileExt();取扩展名EXE
1. 如果在已经处于 ESTABLISHED状态下的socket(一般由端口号和标志符区分)调用
closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket:
BOOL bReuseaddr=TRUE;
setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(BOOL));
2. 如果要已经处于连接状态的soket在调用closesocket后强制关闭,不经历
TIME_WAIT的过程:
BOOL bDontLinger = FALSE;
setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(const char*)&bDontLinger,sizeof(BOOL));
3.在send(),recv()过程中有时由于网络状况等原因,发收不能预期进行,而设置收发时限:
int nNetTimeout=1000;//1秒
//发送时限
setsockopt(socket,SOL_S0CKET,SO_SNDTIMEO,(char *)&nNetTimeout,sizeof(int));
//接收时限
setsockopt(socket,SOL_S0CKET,SO_RCVTIMEO,(char *)&nNetTimeout,sizeof(int));
4.在send()的时候,返回的是实际发送出去的字节(同步)或发送到socket缓冲区的字节
(异步);系统默认的状态发送和接收一次为8688字节(约为8.5K);在实际的过程中发送数据
和接收数据量比较大,可以设置socket缓冲区,而避免了send(),recv()不断的循环收发:
// 接收缓冲区
int nRecvBuf=32*1024;//设置为32K
setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
//发送缓冲区
int nSendBuf=32*1024;//设置为32K
setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));
5. 如果在发送数据的时,希望不经历由系统缓冲区到socket缓冲区的拷贝而影响
程序的性能:
int nZero=0;
setsockopt(socket,SOL_S0CKET,SO_SNDBUF,(char *)&nZero,sizeof(nZero));
6.同上在recv()完成上述功能(默认情况是将socket缓冲区的内容拷贝到系统缓冲区):
int nZero=0;
setsockopt(socket,SOL_S0CKET,SO_RCVBUF,(char *)&nZero,sizeof(int));
7.一般在发送UDP数据报的时候,希望该socket发送的数据具有广播特性:
BOOL bBroadcast=TRUE;
setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char*)&bBroadcast,sizeof(BOOL));
8.在client连接服务器过程中,如果处于非阻塞模式下的socket在connect()的过程中可
以设置connect()延时,直到accpet()被呼叫(本函数设置只有在非阻塞的过程中有显著的
作用,在阻塞的函数调用中作用不大)
BOOL bConditionalAccept=TRUE;
setsockopt(s,SOL_SOCKET,SO_CONDITIONAL_ACCEPT,(const char*)&bConditionalAccept,sizeof(BOOL));
9.如果在发送数据的过程中(send()没有完成,还有数据没发送)而调用了closesocket(),以前我们
一般采取的措施是"从容关闭"shutdown(s,SD_BOTH),但是数据是肯定丢失了,如何设置让程序满足具体
应用的要求(即让没发完的数据发送出去后在关闭socket)?
struct linger {
u_short l_onoff;
u_short l_linger;
};
linger m_sLinger;
m_sLinger.l_onoff=1;//(在closesocket()调用,但是还有数据没发送完毕的时候容许逗留)
// 如果m_sLinger.l_onoff=0;则功能和2.)作用相同;
m_sLinger.l_linger=5;//(容许逗留的时间为5秒)
setsockopt(s,SOL_SOCKET,SO_LINGER,(const char*)&m_sLinger,sizeof(linger));
Note:1.在设置了逗留延时,用于一个非阻塞的socket是作用不大的,最好不用;
2.如果想要程序不经历SO_LINGER需要设置SO_DONTLINGER,或者设置l_onoff=0;
10.还一个用的比较少的是在SDI或者是Dialog的程序中,可以记录socket的调试信息:
(前不久做过这个函数的测试,调式信息可以保存,包括socket建立时候的参数,采用的
具体协议,以及出错的代码都可以记录下来)
BOOL bDebug=TRUE;
setsockopt(s,SOL_SOCKET,SO_DEBUG,(const char*)&bDebug,sizeof(BOOL));
11.附加:往往通过setsockopt()设置了缓冲区大小,但还不能满足数据的传输需求,
我的习惯是自己写个处理网络缓冲的类,动态分配内存;下面我将这个类写出,希望对
初学者有所帮助:使用CFile类对文件进行读写
一个读写文件的例子:
文件I/O
CFile file;
file.Open(“C://TestFile.txt”, CFile::modeReadWrite);
……
// 直接通过构造函数打开文件
CFile file(“C://TestFile.txt”, CFile::modeReadWrite);
CFile::modeCreate 创建方式打开文件,如文件已存在则将其长度设置为0
CFile::modeNoInherit 不允许继承
CFile::modeNoTruncate 创建文件时如文件已存在不对其进行截断
CFile::modeRead 只读方式打开文件
CFile::modeReadWrite 读写方式打开文件
CFile::modeWrite 写入方式打开文件
CFile::shareCompat 在使用过程中允许其他进程同时打开文件
CFile::shareDenyNone 在使用过程中允许其他进程对文件进行读写
CFile::shareDenyRead 在使用过程中不允许其他进程对文件进行读取
CFile::shareDenyWrite 在使用过程中不允许其他进程对文件进行写入
CFile::shareExclusive 取消对其他进程的所有访问
CFile::typeBinary 设置文件为二进制模式
CFile::typeText 设置文件为文本模式
void Write( const void* lpBuf, UINT nCount );
CFile file;
file.Open("C://TestFile.txt", CFile::modeWrite | CFile::modeCreate);
// 写入文件
memset(WriteBuf, 'a', sizeof(WriteBuf));
file.Write(WriteBuf, sizeof(WriteBuf));
// 关闭文件
file.Close();
// 只读方式打开文件
file.Open("C://TestFile.txt", CFile::modeRead);
while (true)
{
// 读取文件数据
int ret = file.Read(ReadBuf, 100);
……
// 如果到达文件结尾则中止循环
if (ret < 100)
break;
}
// 关闭文件
file.Close();
CFile file;
file.Open("C://TestFile.txt", CFile::modeWrite | CFile::modeCreate);
// 写入文件
memset(WriteBuf, 'a', sizeof(WriteBuf));
file.SeekToBegin();
file.Write(WriteBuf, sizeof(WriteBuf));
// 关闭文件
file.Close();
// 只读方式打开文件
file.Open("C://TestFile.txt", CFile::modeRead);
while (true)
{
// 文件指针
static int position = 0;
// 移动文件指针
file.Seek(position, CFile::begin);
// 读取文件数据
int ret = file.Read(ReadBuf, 100);
position += ret;
……
// 如果到达文件结尾则中止循环
if (ret < 100)
break;
}
// 关闭文件
file.Close();
fileRead.Open(_T("E://a.dat"),CFile::modeRead);//这里使用宏_T
fileWrite.Open(_T("E://backup.txt"),CFile::modeCreate |CFile::modeWrite);
fileRead.Read(videoheader,sizeof(VIDEOHEADER));
sprintf(buf,"videoheader.cCommandID:%s ,videoheader->cCommandID);通过sprintf对我们需要写入文件中的数据进行格式化,这样在文件中存储的数据就是以这里定义的格式显示的。MFC同步类
同步对象的适用场合
等待类CSingleLock
CSingleLock::CSingleLock( CSyncObject* pObject, BOOL bInitialLock = FALSE );
pObject 指向要被访问的同步对象。不能是NULL
bInitialLock 指示是否要在最初尝试访问所提供的对象
BOOL CSingleLock::IsLocked()
返回值:如果对象被加锁则返回非零值;否则返回0。此成员函数用来确定与CSingleLock对象相关的对象是否没有发信号(不能使用)。
BOOL CSingleLock::Lock( DWORD dwTimeOut = INFINITE );
返回值:如果函数成功则返回非零值;否则返回0。
参数:dwTimeOut 指定等待要被利用的同步对象的时间数量。如果是INFINITE,则Lock等待直到该对象在返回之前可用。
说明:此成员函数用来获取对由同步对象控制的资源的访问,这个访问要提供给CSingleLock构造函数。如果同步对象是可用的,Lock将成功返回,而且线程拥有了该对象。如果此同步对象是不可用的,则Lock将等待此同步对象在dwTimeOut参数指定的时间内变为可用。如果此同步对象在指定的时间内没有变为可用的,则Lock返回失败。
BOOL CSingleLock::Unlock( );
BOOL CSingleLock::Unlock( LONG lCount, LPLONG lPrevCount = NULL );
返回值:如果函数成功则返回非零值;否则返回0。
参数: lCount:要释放的访问数目。必须要大于0。如果指定的数量要导致对象的计数超过它的最大值,则计数不改变,并且函数返回FALSE。
lPrevCount:指向一个用来接收同步对象的先前计数的变量。如果是NULL,则不返回先前的计数。
说明:此成员函数用来释放由CSingleLock拥有的同步对象。由CSingleLock的析构函数类调用这个函数。如果你需要释放一个信号的多于一个的访问计数,可以使用Unlock的第二种形式,并指定要释放的访问数目。
TTS
TTS的全称为Text To Speech,即“从文本到语音”。它是同时运用语言学和心理学的杰出之作,在内置芯片的支持之下,通过神经网络的设计,把文字智能地转化为自然语音流。
TTS技术对文本文件进行实时转换,转换时间之短可以秒计算。在其特有智能语音控制器作用下,文本输出的语音音律流畅,使得听者在听取信息时感觉自然,毫无机器语音输出的冷漠与生涩感。TTS语音合成技术即将覆盖国标一、二级汉字,具有英文接口,自动识别中、英文,支持中英文混读。所有声音采用真人普通话为标准发音,实现了120-150个汉字/秒的快速语音合成,朗读速度达3-4个汉字/秒,使用户可以听到清晰悦耳的音质和连贯流畅的语调。
-qws命令
运行嵌入式程序
ultraedit 自动缩进修改
找到/IndentStrings = "{" "if" "else"":"
首先单击帮助工具栏(根据设置的不同可能在其他工具栏上)上的“艺术样式”按钮,
弹出如下对话框
然后就可以按照自己的习惯设置缩进的方式了,如果对排版没有特别的要求,默认的选择就可以了,在这种设置状态下,switch后的case是不会缩进的,如果要让case缩进,那么需要选中切换或大小写(就是switch和case,翻译的问题),最好不要两个都选,否则case会缩进两个单位,如果要case后的语句也缩进就选切换,如果只缩进case就选大小写,按照我的习惯是选择切换。其他的选择就看程序员自己的习惯了。
样式我选择的是ANSI,除了GUN,到没有感觉和其他样式有什么区别,有时间再来研究下吧。
UE默认情况下制表符是占两个空格,习惯四个空格的可以选菜单栏中的高级-配置-编辑器-自动换行/制表符设置,将缩进空格改为4就可以了。typedef 函数指针的用法
这种用法一般用在给函数定义别名的时候
上面的例子定义MYFUN 是一个函数指针, 函数类型是带两个int 参数, 返回一个int
在分析这种形式的定义的时候可以用下面的方法:
先去掉typedef 和别名, 剩下的就是原变量的类型.
去掉typedef和MYFUN以后就剩:
int (*)(int, int)
int a;
int b;
}MY_TYPE;
这里把一个未命名结构直接取了一个叫MY_TYPE的别名, 这样如果你想定义结构的实例的时候就可以这样:
MY_TYPE tmp;【转】(转)MFC中TRACE的用法
2:
TRACE 宏只有在调试状态下才有所输出,而以前用的Printf函数在任何情况下都有输出。和Printf函数一样,TRACE函数可以接受多个参数如:
int y = 16;
float z = 32.0;
TRACE( "This is a TRACE statement\n" );
TRACE( "The value of x is %d\n", x );
TRACE( "x = %d and y = %d\n", x, y );
TRACE( "x = %d and y = %x and z = %f\n", x, y, z );linux c语言定时器
int getitimer(int which, struct itimerval *value);
int setitimer(int which, struct itimerval*newvalue, struct itimerval*oldvalue);
strcut timeval
{
long tv_sec; /*
long tv_usec; /*微秒*/
};
struct itimerval
{
struct timeval it_interval; /*时间间隔*/
struct timeval it_value; /*当前时间计数*/
};
#include
#include
int handle_count=0;
void set_time(void)
{
struct itimerval itv;
itv.it_interval.tv_sec=10;//自动装载,之后每10秒响应一次
itv.it_interval.tv_usec=0;
itv.it_value.tv_sec=5;//第一次定时的时间
itv.it_value.tv_usec=0;
setitimer(ITIMER_REAL,&itv,NULL);
}
void alarm_handle(int sig)
{
handle_count++;
printf("have handle count is %d\n",handle_count);
}
void main(void)
{
struct itimerval itv;
signal(SIGALRM,alarm_handle);
set_time();
while(1){
getitimer(ITIMER_REAL,&itv);
printf("pass second is %d\n",(int)itv.it_value.tv_sec);
sleep(1);
}
return;
}Linux下查看文件和文件夹大小的df和du命令
du可以查看文件及文件夹的大小。
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 3.9G 300M 3.4G 8% /
/dev/sda7 100G 188M 95G 1% /data0
/dev/sdb1 133G 80G 47G 64% /data1
/dev/sda6 7.8G 218M 7.2G 3% /var
/dev/sda5 7.8G 166M 7.2G 3% /tmp
/dev/sda3 9.7G 2.5G 6.8G 27% /usr
tmpfs 2.0G 0 2.0G 0% /dev/shm
27M work/testing/logs
35M work/testing
8.0K work/testing/func.php
27M work/testing/logs
8.1M work/testing/nohup.out
8.0K work/testing/testing_c.php
12K work/testing/testing_func_reg.php
8.0K work/testing/testing_get.php
8.0K work/testing/testing_g.php
8.0K work/testing/var.php
27M work/testing/logs/
24K work/testing/logs/errdate.log_show.log
8.0K work/testing/logs/pertime_show.log
27M work/testing/logs/show.log
df 统计数据块使用情况
fuser -u /var/spool/clientmqueue
我常使用的命令(必要时,sudo使用root权限),
1.查看某个目录的大小:du -hs /home/master/documents
查看目录下所有目录的大小并按大小降序排列:sudo du -sm /etc/* | sort -nr | less
2.查看磁盘使用情况(文件系统的使用情况):sudo df -h
df --block-size=GB
-h是使输出结果更易于人类阅读;du -s只展示目录的使用总量(不分别展示各个子目录情况),-m是以MB为单位展示目录的大小(当然-k/-g就是KB/GB了)。
更多信息,还是man du 和 man df 来获得吧。linux 查看文件属性命令
ls -a 查看所有文件
ls -l 查看详细的属性
2,lsattr
查看文件的扩展属性,
如果文件被 chattr +i 添加了写保护,
用lsattr可以看到添加的属性
3,file
查看文件的类型
4,stat
查看文件的状态pthread_attr_init线程属性
1.线程属性
2、线程的分离状态
3、线程的继承性
4、线程的调度策略
5、线程的调度参数
vsnprintf
目 录
1函数简介
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
2用法实例
SOCKADDR_IN
目 录
1基本结构
2参数说明
{
//创建socket
int sockfd=socket(PF_LOCAL, SOCK_DGRAM, 0);
if(sockfd==-1)
perror("创建socket失败"),exit(-1);
//准备通信地址
struct sockaddr_un addr;
addr.sun_family = PF_UNIX;
strcpy(addr.sun_path,"a.sock");
//绑定
int res = bind(sockfd,
(struct sockaddr*)&addr, sizeof(addr));
if(res==-1)perror("绑定失败"),exit(-1);
printf("绑定成功\n");
//通信(用读写文件方式)
char buf[100] = {};
read(sockfd, buf, sizeof(buf));
printf("收到信息:%s\n",buf);
//关闭socket
close(sockfd);
}
{
int sockfd=socket(PF_LOCAL, SOCK_DGRAM, 0);
if(sockfd==-1)
perror("创建socket失败"),exit(-1);
struct sockaddr_un addr;
addr.sun_family = PF_UNIX;
strcpy(addr.sun_path,"a.sock");
//连接
int res = connect(sockfd,
(struct sockaddr*)&addr, sizeof(addr));
if(res==-1)perror("失败"),exit(-1);
printf("成功\n");
write(sockfd, "Hello, Socket!", 14);
close(sockfd);
}AF_INET和PF_INET的细微不同
sock = socket(PF_INET, SOCK_STREAM, 0);
然后在绑定本地地址或连接远程地址时需要初始化sockaddr_in结构,其中指定addressfamily时一般设置为AF_INET,即使用IP。
PF = Protocol Family
AF_INET = PF_INET
这几个参数有AF_UNIX=AF_LOCAL,PF_UNIX=PF_LOCAL, AF_LOCAL=PF_LOCAL,AF_INET=PF_INET.
**建议:对于socketpair与socket的domain参数,使用PF_LOCAL系列,
而在初始化套接口地址结构时,则使用AF_LOCAL.
例如:
z = socket(PF_LOCAL, SOCK_STREAM, 0);
adr_unix.sin_family = AF_LOCAL;popen
#include
FILE
* popen ( const
char
* command , const
char
* type );
int
pclose ( FILE
* stream );
if((fp=popen("/usr/bin/uptime","r"))==NULL);
{
sprintf(buf,"error: %s\n", strerror(errno));
....//
异常处理
}
else
{
....
pclose(fp);
}
#define _LINE_LENGTH 300
int
get_path_total(const
char
*path, long
long* total) {
int
err=-1;
FILE
*file;
char
line[_LINE_LENGTH];
char
*p;
char
tmp[100];
char
*token;
sprintf(tmp, "df %s", path);
file = popen(tmp, "r");
if
(file != NULL) {
if
(fgets(line, _LINE_LENGTH, file) != NULL) {
if
(fgets(line, _LINE_LENGTH, file) != NULL) {
token = strtok(line, " ");
if
(token != NULL) {
// printf("token=%s\n", token);
}
token = strtok(NULL, " ");
if
(token != NULL) {
// printf("token=%s\n", token);
*total=atoll(token)/1024;//k/1024
err=0;
}
}
}
pclose(file);
}
return
err;
}
pthread_cond_signal和pthread_cond_wait简介
linux 下route命令
路由修改 route
我们在网路基础的时候谈过关于路由的问题,两部主机之间一定要有路由才能够互通 TCP/IP 的协定,否则就无法进行连线啊!
一般来说,只要有网路介面,该介面就会产生一个路由,例如在鸟哥实验室内部的主机有一个 eth0 及 lo ,所以:
[root@linux ~]# route [-nee]
[root@linux ~]# route add [-net|-host] [网域或主机] netmask [mask] [gw|dev]
[root@linux ~]# route del [-net|-host] [网域或主机] netmask [mask] [gw|dev]
观察的参数:
-n :不要使用通讯协定或主机名称,直接使用 IP 或 port number;
-ee :使用更详细的资讯来显示
增加 (add) 与删除 (del) 路由的相关参数:
-net :表示后面接的路由为一个网域;
-host :表示后面接的为连接到单部主机的路由;
netmask :与网域有关,可以设定 netmask 决定网域的大小;
gw :gateway 的简写,后续接的是 IP 的数值喔,与 dev 不同;
dev :如果只是要指定由那一块网路卡连线出去,则使用这个设定,后面接 eth0 等
范例一:单纯的观察路由状态
[root@linux ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
192.168.10.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
169.254.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
0.0.0.0 192.168.10.30 0.0.0.0 UG 0 0 0 eth0
[root@linux ~]# route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
192.168.10.0 * 255.255.255.0 U 0 0 0 eth0
169.254.0.0 * 255.255.0.0 U 0 0 0 eth0
default server.cluster 0.0.0.0 UG 0 0 0 eth0
由上面的例子当中仔细观察 route 与 route -n 的输出结果,你可以发现有加 -n参数的主要是显示出 IP ,至于使用 route 而已的话,显示的则是『主机名称』喔!也就是说,在预设的情况下, route 会去找出该 IP 的主机名称,如果找不到呢?就会显示的钝钝的(有点小慢),所以说,鸟哥通常都直接使用 route -n 啦!由上面看起来,我们也知道 default = 0.0.0.0/0.0.0.0 ,而上面的资讯有哪些你必须要知道的呢?
2)不是 169.254.0.0/16 结果到达
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
192.168.10.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
192.168.10.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1
也就是说,由于路由是依照顺序来排列与传送的,所以不论封包是由那个介面 (eth0, eth1) 所接收,都会由上述的 eth0 传送出去,所以,在一部主机上面设定两个相同网域的 IP 本身没有什么意义!有点多此一举就是了。除非是类似虚拟主机 (Xen, VMware 等软体) 所架设的多主机时,才会有这个必要~
范例二:路由的增加与删除
[root@linux ~]# route del -net 169.254.0.0 netmask 255.255.0.0 dev eth0
# 上面这个动作可以删除掉 169.254.0.0/16 这个网域!
# 请注意,在删除的时候,需要将路由表上面出现的资讯都写入
# 包括 netmask , dev 等等参数喔!注意注意
[root@linux ~]# route add -net 192.168.100.0 netmask 255.255.255.0 dev eth0
# 透过 route add 来增加一个路由!请注意,这个路由必须要能够与你互通。
# 举例来说,如果我下达底下的指令就会显示错误:
# route add -net 192.168.200.0 netmask 255.255.255.0 gw 192.168.200.254
# 因为我的环境内仅有 192.168.10.100 这个 IP ,所以不能与 192.168.200.254
# 这个网段直接使用 MAC 互通!这样说,可以理解喔!?
[root@linux ~]# route add default gw 192.168.10.30
# 增加预设路由的方法!请注意,只要有一个预设路由就够了喔!
# 在这个地方如果您随便设定后,记得使用底下的指令重新设定你的网路
# /etc/init.d/network restart
如果是要进行路由的删除与增加,那就得要参考上面的例子了,其实,使用 man route 里面的资料就很丰富了!仔细查阅一下囉!你只要记得,当出现『SIOCADDRT: Network is unreachable』这个错误时,肯定是由于 gw 后面接的 IP 无法直接与您的网域沟通 (Gateway 并不在你的网域内),所以,赶紧检查一下是否输入错误啊!加油吧!
1.在/etc/rc.local里添加
2.在/etc/sysconfig/network里添加到末尾
3./etc/sysconfig/static-router :
any net x.x.x.x/24 gw y.y.y.y
UNIX环境高级编程读书笔记(十一)—终端IO (3)
名称::
cfgetispeed/cfgetospeed/cfsetispeed/cfsetospeed
功能:
波特率函数
头文件:
#include
函数原形:
speed_t cfgetispeed(const struct termios *termptr);
speed_t cfgetospeed(const struct termios *termptr);
int cfsetispeed(struct termios *termotr,speed_t speed);
int cfsetospeed(struct termios *termotr,speed_t speed);
参数:
返回值:
返回波特率( cfgetispeed, cfgetospeed)
若成功返回0,若出错则返回-1( cfsetispeed, cfsetospeed)
波特率是一个以往采用的术语,现在它指的是“位/秒”。虽然大多数终端设备对输入和输出使用同一波特率,但是只要硬件许可,可以将它们设置为两个不同值。
3.
名称::
tcdrain/tcflow/tcflush/tcsendbreak
功能:
行控制函数
头文件:
#include
函数原形:
int tcdrain(int filedes);
int tcglow(int files,int action);
int tcflush(int files,int queue);
int tcsendbread(int filedes,int duration);
参数:
filedes 终端I/O所对应的文件的文件描述符
返回值:
若成功返回0,若出错则返回-1
tcdrain函数等待所有输出都被发送。tcflow用于对输入和输出流控制进行控制。action参数应当是下列四个值:
TCOOFF 输出被挂起
TCOON 以前被挂起的输出被重新启动
TCIOFF 系统发送一个STOP字符。这将使终端设备暂停发送数据。
TCION 系统发送一个START字符。这将使终端恢复发送数据。
tcflush函数刷清输入缓存或输出缓存。queue参数应当是下列三个参数之一:
TCIFLUSH 刷清输入队列
TCOFLUSH 刷清输出队列
TCIOFLUSH 刷清输入、输出队列
tcsendbread函数在一个指定的时间区间内发送连续的0位流。若duration参数为0,则此种发送延续0.25~0.5秒之间。
4.
名称::
ctermid(char *ptr);
功能:
决定终端的名字
头文件:
#include
函数原形:
char *ctermid(char *ptr);
参数:
ptr 存放控制终端名的数组
返回值:
若成功则返回指向控制终端名的指针,若出错则返回指向空字符串的指针。
ctermid可却定终端的名字。
如果ptr是非空,则它被认为是一个指针,指向长度至少为L_ctermid字节的数组,进程的控制终端名存放在该数组中。参数L_ctermid定义在中。若ptr是是一个空指针,则该函数为数组分配空间。同样,进程的控制终端名存放在该数组中。
大部分UNIX系统中,控制终端的名称是/dev/tty/。所以此函数的主要作用是帮助提高向其他操作系统的可移植性。
5.
名称::
isatty
功能:
判断文件是不是一个终端设备文件
头文件:
#include
函数原形:
int isatty(int filedes);
参数:
filedes 终端I/O所对应的文件的文件描述符
返回值:
若终端设备则为1,否则为0。
isatty函数用于判断文件是不是一个终端设备。下面是实例:
/*11_2.c*/
#include
int main(void)
{
printf(“fd 0:%s\n”,isatty(0)?”tty”:”not a tty”);
printf(“fd 1:%s\n”,isatty(1)?”tty”,”not a tty”);
printf(“fd2:%s\n”,isatty(2)?”tty”,”not a tty”);
exit(0);
}
下面是运行结果:
#./11_2
fd 0:tty
fd 1:tty
fd 2:tty
#./11_2 /dev/null
fd 0:not a tty
fd: 1:tty
fd2:not a tty
6.
名称::
ttyname
功能:
判断是不是终端设备文件如果是打印路径名
头文件:
#include
函数原形:
char *ttyname(int filedes);
参数:
filedes 终端I/O所对应的文件的文件描述符
返回值:
指向终端路径名的指针,若出错则为NULL
每个文件系统有一个唯一的设备号(stat结构中的st_dev字段),文件系统每个目录项有唯一的i节点号(stat结构中的st_ino字段)。ttyname会读/dev目录,寻找具有相同设备号和i节点编号的表项。在此函数中设定当找到一个匹配的设备号和匹配的i节点号时,就找到了所希望的目录项。
#include
#include
#include
int main(void)
{
char *name;
if(isatty(0))
{
name=ttyname(0);
if(name==NULL)
name=”undefined”;
printf(“fd 0:%s\n”,name);
}
else
printf(“not a tty\n”);
if(isatty(1))
{
name=ttyname(1);
if(name==NULL)
name=”undefined”);
printf(“fd 1:%s\n”,name);
}
else
printf(“not a tty\n”);
if(isatty(2))
{
name=ttyname(2);
if(name==NULL)
name=”undefined”);
printf(“not a tty\n”);
}
else
printf(“not a tty\n”);
exit(0);
}
#./11_3
fd 0: /dev/tty1
fd 1: /dev/tty1
fd 2: /dev/tty1
#./11_3 /dev/null
fd 0: /dev/console
fd1: /dev/tty1
not a ttyselect()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET
#include
#include
int select(int maxfd,fd_set *rdset,fd_set *wrset,fd_set *exset,struct timeval *timeout);
fd_set(它比较重要所以先介绍一下)是一组文件描述字(fd)的集合,它用一位来表示一个fd(下面会仔细介绍),对于fd_set类型通过下面四个宏来操作:
FD_ZERO(fd_set *fdset);将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。
FD_SET(int fd, fd_set *fdset);用于在文件描述符集合中增加一个新的文件描述符。
FD_CLR(int fd, fd_set *fdset);用于在文件描述符集合中删除一个文件描述符。
FD_ISSET(int fd, fd_set *fdset);用于测试指定的文件描述符是否在该集合中。
过去,一个fd_set通常只能包含<32的fd(文件描述字),因为fd_set其实只用了一个32位矢量来表示fd;现在,UNIX系统通常会在头文件
fd_set set;
FD_ZERO(&set);
FD_SET(0, &set);
FD_CLR(4, &set);
FD_ISSET(5, &set);
―――――――――――――――――――――――――――――――――――――――
注意fd的最大值必须
―――――――――――――――――――――――――――――――――――――――
int select(int nfds, fd_set *readset, fd_set *writeset,fd_set* exceptset, struct tim *timeout);
测试指定的fd可读?可写?有异常条件待处理?
参数:
需要检查的文件描述字个数(即检查到fd_set的第几位),数值应该比三组fd_set中所含的最大fd值更大,一般设为三组fd_set中所含的最大fd值加1(如在readset,writeset,exceptset中所含最大的fd为5,则nfds=6,因为fd是从0开始的)。设这个值是为提高效率,使函数不必检查fd_set的所有1024位。
—— readset
用来检查可读性的一组文件描述字。
—— writeset
用来检查可写性的一组文件描述字。
—— exceptset
用来检查是否有异常条件出现的文件描述字。(注:错误不包括在异常条件之内)
—— timeout
用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。
有三种可能:
1.timeout=NULL(阻塞:select将一直被阻塞,直到某个文件描述符上发生了事件)
2.timeout所指向的结构设为非零时间(等待固定时间:如果在指定的时间段里有事件发生或者时间耗尽,函数均返回)
3.timeout所指向的结构,时间设为0(非阻塞:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生)
返回对应位仍然为1的fd的总数。
三组fd_set均将某些fd位置0,只有那些可读,可写以及有异常条件待处理的fd位仍然为1。
所以采用select来查看套节字是否可读(也就是是否有数据读了)
步骤如下——
socket s;
.....
fd_set set;
while(1)
{
FD_ZERO(&set);//将你的套节字集合清空
FD_SET(s, &set);//加入你感兴趣的套节字到集合,这里是一个读数据的套节字s
select(0,&set,NULL,NULL,NULL);//检查套节字是否可读,
//很多情况下就是是否有数据(注意,只是说很多情况)
//这里select是否出错没有写
if(FD_ISSET(s, &set) //检查s是否在这个集合里面,
{ //select将更新这个集合,把其中不可读的套节字去掉
//只保留符合条件的套节字在这个集合里面
recv(s,...);
}
//do something here
}
(1)执行fd_set set; FD_ZERO(&set);则set用位表示是0000,0000。
(2)若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位置为1)
(3)若再加入fd=2,fd=1,则set变为0001,0011
(4)执行select(6,&set,0,0,0)阻塞等待
(5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。注意:没有事件发生的fd=5被清空。
基于上面的讨论,可以轻松得出select模型的特点:
(1)可监控的文件描述符个数取决与sizeof(fd_set)的值。我这边服务 器上sizeof(fd_set)=512,每bit表示一个文件描述符,则我服务器上支持的最大文件描述符是512*8=4096。据说可调,另有说虽 然可调,但调整上限受于编译内核时的变量值。本人对调整fd_set的大小不太感兴趣,参考http://www.cppblog.com/CppExplore/archive/2008/03/21/45061.html中的模型2(1)可以有效突破select可监控的文件描述符上限。
(2)将fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd,一是用于再select 返回后,array作为源数据和fd_set进行FD_ISSET判断。二是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始 select前都要重新从array取得fd逐一加入(FD_ZERO最先),扫描array的同时取得fd最大值maxfd,用于select的第一个 参数。
(3)可见select模型必须在select前循环array(加fd,取maxfd),select返回后循环array(FD_ISSET判断是否有时间发生)。
下面给一个伪码说明基本select模型的服务器模型:
array[slect_len];
nSock=0;
array[nSock++]=listen_fd;(之前listen port已绑定并listen)
maxfd=listen_fd;
while{
FD_ZERO(&set);
foreach (fd in array)
{
fd大于maxfd,则maxfd=fd
FD_SET(fd,&set)
}
res=select(maxfd+1,&set,0,0,0);
if(FD_ISSET(listen_fd,&set))
{
newfd=accept(listen_fd);
array[nsock++]=newfd;
if(--res=0) continue
}
foreach 下标1开始 (fd in array)
{
if(FD_ISSET(fd,&set))
执行读等相关操作
如果错误或者关闭,则要删除该fd,将array中相应位置和最后一个元素互换就好,nsock减一
if(--res=0) continue
}
}
使用select函数的过程一般是:
先调用宏FD_ZERO将指定的fd_set清零,然后调用宏FD_SET将需要测试的fd加入fd_set,接着调用函数select测试fd_set中的所有fd,最后用宏FD_ISSET检查某个fd在函数select调用后,相应位是否仍然为1。
int isready(int fd)
{
int rc;
fd_set fds;
struct tim tv;
FD_ZERO(&fds);
FD_SET(fd,&fds);
tv.tv_sec = tv.tv_usec = 0;
rc = select(fd+1, &fds, NULL, NULL, &tv);
if (rc < 0) //error
return -1;
return FD_ISSET(fd,&fds) ? 1 : 0;
}
下面还有一个复杂一些的应用:
//这段代码将指定测试Socket的描述字的可读可写性,因为Socket使用的也是fd
uint32 SocketWait(TSocket *s,bool rd,bool wr,uint32 timems)
{
fd_set rfds,wfds;
#ifdef _WIN32
TIM tv;
#else
struct tim tv;
#endif
FD_ZERO(&rfds);
FD_ZERO(&wfds);
if (rd) //TRUE
FD_SET(*s,&rfds); //添加要测试的描述字
if (wr) //FALSE
FD_SET(*s,&wfds);
tv.tv_sec=timems/1000; //second
tv.tv_usec=timems%1000; //ms
for (;;) //如果errno==EINTR,反复测试缓冲区的可读性
switch(select((*s)+1,&rfds,&wfds,NULL,
(timems==TIME_INFINITE?NULL:&tv))) //测试在规定的时间内套接口接收缓冲区中是否有数据可读
{ //0--超时,-1--出错
case 0:
return 0;
case (-1):
if (SocketError()==EINTR)
break;
return 0; //有错但不是EINTR
default:
if (FD_ISSET(*s,&rfds)) //如果s是fds中的一员返回非0,否则返回0
return 1;
if (FD_ISSET(*s,&wfds))
return 2;
return 0;
};
}ioctl 函数
• 改变路由表 (例如 SIOCADDRT, SIOCDELRT),
• 读/更新ARP/RARP 缓存(如:SIOCDARP,SIOCSRARP),
• 一般的与网络接口有关的(例如 SIOCGIFNAME, SIOCSIFADDR 等等)
在Gooodies目录下有很多样例程序展示了如何使用ioctl。当你看这些程序时,注意参数argstruct是与参数command相关的。例如,与 路由表相关的ioctl使用rtentry这种结构,rtentry定义在/usr/include/linux/route.h(参见例子 adddefault.c)。与ARP有关的ioctl调用使用arpreq结构,arpreq定义在/usr/include/linux /if_arp.h(参见例子arpread.c)(C语言)共用体union的用法举例
1. 为了方便看懂代码。
比如说想写一个3 * 3的矩阵,可以这样写:
[ 注:下面用红色部分标记的地方是后来添加上去的,谢谢yrqing718的提醒!]
这两个东西共同使用相同的空间,所以没有空间浪费,在需要整体用矩阵的时候可以用
m._matrix.f (比如说传参,或者是整体赋值等);需要用其中的几个元素的时候可以用m._matrix._f11那样可以避免用m.f[0][0](这样不大直观,而且容易出错)。
2. 用在强制类型转换上(比强制类型转换更加容易看懂)
下面举几个例子:
(1). 判断系统用的是bigendian 还是 little endian(其定义大家可以到网上查相关资料,此略)
反之亦然
也许看起来不是很清晰,下面来看一下这个:
什么,不觉得清晰??那再看下面的例子:
(2). 将littleendian下的longlong类型的值换成 bigendian类型的值。已经知道系统提供了下面的api:longhtonl(long lg);作用是把所有的字节序换成大端字节序。因此得出下面做法:
(3).为了理解c++类的布局,再看下面一个例子。有如下类:
union在操作系统底层的代码中用的比较多,因为它在内存共赏布局上方便且直观。所以网络编程,协议分析,内核代码上有一些用到union都比较好懂,简化了设计。
获取或者设置与某个套接字关联的选项。选项可能存在于多层协议中,它们总会出现在最上面的套接字层。当操作套接字选项时,选项位于的层和选项的名称必须给出。为了操作套接字层的选项,应该将层的值指定为SOL_SOCKET。为了操作其它层的选项,控制选项的合适协议号必须给出。例如,为了表示一个选项由TCP协议解析,层应该设定为协议号TCP。Linux下getsockopt/setsockopt函数说明
用法:
#include
#include
sock:将要被设置或者获取选项的套接字。
level:选项所在的协议层。
optname:需要访问的选项名。
optval:对于getsockopt(),指向返回选项值的缓冲。对于setsockopt(),指向包含新选项值的缓冲。
optlen:对于getsockopt(),作为入口参数时,选项值的最大长度。作为出口参数时,选项值的实际长度。对于setsockopt(),现选项的长度。
返回说明:
成功执行时,返回0。失败返回-1,errno被设为以下的某个值
EBADF:sock不是有效的文件描述词
EFAULT:optval指向的内存并非有效的进程空间
EINVAL:在调用setsockopt()时,optlen无效
ENOPROTOOPT:指定的协议层不能识别选项
ENOTSOCK:sock描述的不是套接字
1)SOL_SOCKET:通用套接字选项.
2)IPPROTO_IP:IP选项.
3)IPPROTO_TCP:TCP选项.
optname指定控制的方式(选项的名称),我们下面详细解释
选项名称 说明 数据类型
========================================================================
SOL_SOCKET
------------------------------------------------------------------------
SO_BROADCAST 允许发送广播数据 int
SO_DEBUG 允许调试 int
SO_DONTROUTE 不查找路由 int
SO_ERROR 获得套接字错误 int
SO_KEEPALIVE 保持连接 int
SO_LINGER延迟关闭连接 structlinger
SO_OOBINLINE 带外数据放入正常数据流 int
SO_RCVBUF接收缓冲区大小 int
SO_SNDBUF发送缓冲区大小 int
SO_RCVLOWAT接收缓冲区下限 int
SO_SNDLOWAT发送缓冲区下限 int
SO_RCVTIMEO接收超时 structtimeval
SO_SNDTIMEO发送超时 structtimeval
SO_REUSERADDR允许重用本地地址和端口 int
SO_TYPE获得套接字类型 int
SO_BSDCOMPAT 与BSD系统兼容 int
========================================================================
IPPROTO_IP
------------------------------------------------------------------------
IP_HDRINCL 在数据包中包含IP首部 int
IP_OPTINOSIP首部选项 int
IP_TOS 服务类型
IP_TTL 生存时间 int
========================================================================
IPPRO_TCP
------------------------------------------------------------------------
TCP_MAXSEGTCP最大数据段的大小 int
TCP_NODELAY不使用Nagle算法 int
========================================================================
成功执行时,返回0。失败返回-1,errno被设为以下的某个值
EBADF:sock不是有效的文件描述词
EFAULT:optval指向的内存并非有效的进程空间
EINVAL:在调用setsockopt()时,optlen无效
ENOPROTOOPT:指定的协议层不能识别选项
ENOTSOCK:sock描述的不是套接字
int nRecvBuf=32*1024; //设置为32K
setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
//发送缓冲区
int nSendBuf=32*1024;//设置为32K
setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));SVN多版本库配置问题
windows 下本机配置svn以及多版本库的创建
服务器和客户端安装
建立版本库(Repository)
配置用户和权限
运行独立服务器
初始化导入
基本客户端操作
1、软件下载
下载Subversion服务器程序。
下载Subversion的Windows客户端TortoiseSVN及简体中文语言安装包。
http://tortoisesvn.net/downloads
svnservice下载
http://bbs.iusesvn.com/attachment.php?aid=12
2、服务器和客户端安装
服务器安装,直接运行安装程序,根据提示安装即可,这样我们就有了一套服务器可以运行的环境。
安装TortoiseSVN,同样直接运行安装程序,按照提示安装即可,不过最后完成后会提示是否重启,其实重启只是使svn工作拷贝在windows中的特殊样式生效,与所有的实际功能无关,这里为了立刻看到好的效果,还是重新启动机器。
重启完毕后安装简体中文语言包,然后在随便一个目录右击,就会发现多出了一些SVN相关菜单, 选择其中的TortoiseSVN,再选择子菜单"Settings",设置Language为"中文(简体)"。
3、建立版本库(Repository)
运行Subversion服务器需要首先要建立一个版本库(Repository),可以看作服务器上存放数据的数据库,在安装了Subversion服务器之后,可以直接运行,如:
svnadmin create F:\svn\repository
就会在目录F:\svn\repository下创建一个版本库。
我们也可以使用TortoiseSVN图形化的完成这一步:
在目录D:\svn\repository下"右键->TortoiseSVN->在此创建文件库",然后可以选择版本库模式,这里使用默认,fsfs方式即可,然后就创建了一系列目录和文件。
4、配置用户和权限
打开F:\svn\repository,你会发现已经多了一些目录和文件,打开conf子目录, 打开svnserve.conf文件, 这里行前凡是有#的都等于是被注释忽略了, 你可以把#去掉让那一行生效, 或者自己新添加行. 里面的英文注释已经详细说明了各种设置的含义,最后你设置[general]小节中行前没有#号的内容为:
anon-access = none
auth-access = write
password-db = passwd
一定要把空格给删了,否则会出错svnserve.conf:12:Option expected
含义是:
未验证用户无任何权限(如果把none修改为read就是给予读权限)
已验证用户给予写权限(当然也能读)
密码数据存放到passwd文件中
然后打开同目录的passwd文件来设置帐户:
同样, 设置[users]小节中行前没有#号的内容, 例如:
含义是:
用户admin的密码为ren
5、运行独立服务器
安装subversion的bin目录下不知为何没有svnservice.exe,将svnservice.exe放在subversion的bin目录下。
在dos控制台状态下cd 进入subversion的安装目录的bin目录,
6、初始化导入
打开"我的电脑",在你需要进行版本控制的目录上右击,选择TortoiseSVN,再选择子菜单"导入...",设置"文件库url"为svn://localhost点确定后就会提示文件正在导入.
需要注意的是,这里是svn文件库与svn服务是同一台计算机的情况, 所以可用localhost,其它机器如果要访问svn服务, 应该用svn://svn服务器的IP地址, 例如svn://192.168.1.125
7、基本客户端操作
创建一个准备用来存放版本控制工程的目录,例如d:\project,然后在"我的电脑"中右击这个目录, 选择"SVN取出...",设置"文件库url"为svn://svn服务器的IP地址, 接下来会问你用户名和帐号, 你就填写前面搭建服务器端所设置的用户admin密码zhang
点确定后就会提示文件正在取出到d:\project
至此, SVN客户端配置完成, 你会看到d:\project及其下面的文件都被标记了绿色对勾
简单日常使用:
要取得工程的当前的最新版本,右击d:\project,选择"SVN更新"
你更改工程后,要将你的修改更新到SVN,右击d:\project,选择"SVN提交" ,谨慎的话请先更新到SVN最新版本后再提交。【C/C++】Linux下使用system()函数一定要谨慎
1
#include
2
int
system(const
char
*command);
01
int
system(const
char
* cmdstring)
02
{
03
pid_t pid;
04
int
status;
05
06
if(cmdstring == NULL)
07
{
08
return
(1);
//
如果cmdstring为空,返回非零值,一般为1
09
}
10
11
if((pid = fork())<0)
12
{
13
status = -1;
//fork
失败,返回-1
14
}
15
else
if(pid == 0)
16
{
17
execl("/bin/sh",
"sh",
"-c", cmdstring, (char
*)0);
18
_exit(127);
// exec
执行失败返回127,注意exec只在失败时才返回现在的进程,成功的话现在的进程就不存在啦~~
19
}
20
else
//
父进程
21
{
22
while(waitpid(pid, &status, 0) < 0)
23
{
24
if(errno
!= EINTR)
25
{
26
status = -1;
//
如果waitpid被信号中断,则返回-1
27
break;
28
}
29
}
30
}
31
32
return
status;
//
如果waitpid成功,则返回子进程的返回状态
33
}
01
int
status;
02
if(NULL == cmdstring)
//
如果cmdstring为空趁早闪退吧,尽管system()函数也能处理空指针
03
{
04
return
XXX;
05
}
06
status =
system(cmdstring);
07
if(status < 0)
08
{
09
printf("cmd: %s\t error: %s", cmdstring,
strerror(errno));
//
这里务必要把errno信息输出或记入Log
10
return
XXX;
11
}
12
13
if(WIFEXITED(status))
14
{
15
printf("normal termination, exit status = %d\n", WEXITSTATUS(status));
//
取得cmdstring执行结果
16
}
17
else
if(WIFSIGNALED(status))
18
{
19
printf("abnormal termination,signal number =%d\n", WTERMSIG(status));
//
如果cmdstring被信号中断,取得信号值
20
}
21
else
if(WIFSTOPPED(status))
22
{
23
printf("process stopped, signal number =%d\n", WSTOPSIG(status));
//
如果cmdstring被信号暂停执行,取得信号值
24
}
成功返回子进程的status,使用WIFEXITED相关宏就可以取得command的返回结果;
失败返回-1,我们可以使用perro()函数或strerror()函数得到有用的错误信息。fork()函数 UNIX
头文件:
函数原型:
函数说明:
为什么fork会返回两次?
Linux下/proc目录简介
Linux 内核提供了一种通过 /proc 文件系统,在运行时访问内核内部数据结构、改变内核设置的机制。proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口。
/proc/buddyinfo 每个内存区中的每个order有多少块可用,和内存碎片问题有关
/proc/dma 已注册使用的ISA DMA频道列表
3.1 /proc/
yafang@QA:~$ ls/proc/
系统信息和内核参数
网卡设备信息
SCSI设备信息
所有加载到内核的模块列表
已经加载的设备并分类
Character devices:
root@BCNMB-A:~#
分区中的块分配信息
Linux内核版本和gcc版本
该文件指定了可以分配的文件句柄的最大数目。如果用户得到的错误消息声明由于打开文件数已经达到了最大值,从而他们不能打开更多文件,则可能需要增加该值。可将这个值设置成有任意多个文件,并且能通过将一个新数字值写入该文件来更改该值。默认设置时4096。
iflist1
then
list2
fi
其中list1其实就是一个命令列表,列表只有一项时就是单个命令。if的条件就是列表中最后一个命令执行的返回值。
所以要判断某个命令执行的返回值,只要直接把命令放在if后面就行了,千万不要画蛇添足地加上反引号!看例子:
`cmd`及其另一种形式$(cmd),叫做“命令替换”,就是把其中的命令执行后的“标准输出”(注意不是“返回值”!)代换到命令行,然后再执行代换后得到的新命令行。
所以:
if `ls foo`; then do sth; fi
这种写法是很奇怪的,也不能说它是错的,而且它确实能够执行,但起码可以认为该写法概念不清、逻辑混乱。
shell判断文件file存在:
其实关于判断文件存在与否,shell有专用的测试方法:
if [ -e file ]; then cmd; fi
判断file存在,且是普通文件:
if [ -f file ]; then cmd; fi
判断目录存在:
if [ -d path ]; then cmd; fi
判断文件存在且可执行:
if [ -x file ]; then cmd; fi在 /dev 中创建设备
6.8.1. 创建初始设备节点
mknod -m 600 /dev/console c 5 1
mknod -m 666 /dev/null c 1 3
6.8.2. 挂载 ramfs 并填充 /dev 目录
mount -nvt tmpfs none /dev
mknod -m 622 /dev/console c 5 1
mknod -m 666 /dev/null c 1 3
mknod -m 666 /dev/zero c 1 5
mknod -m 666 /dev/ptmx c 5 2
mknod -m 666 /dev/tty c 5 0
mknod -m 444 /dev/random c 1 8
mknod -m 444 /dev/urandom c 1 9
chown -v root:tty /dev/{console,ptmx,tty}
ln -sv /proc/self/fd /dev/fd
ln -sv /proc/self/fd/0 /dev/stdin
ln -sv /proc/self/fd/1 /dev/stdout
ln -sv /proc/self/fd/2 /dev/stderr
ln -sv /proc/kcore /dev/core
mkdir -v /dev/pts
mkdir -v /dev/shm
mount -vt devpts -o gid=4,mode=620 none /dev/pts
mount -vt tmpfs none /dev/shm
can't open /etc/fstab: No such file or directory.
比/tmp大或需要存较长时间的临时文件.
锁定文件.许多程序遵循在/var/lock 中产生一个锁定文件的约定,以支持他们正在使用某个特定的设备或文件.其他程序注意到这个锁定文件,将不试图使用这个设备或文件.
各种程序的log文件,特别是login (/var/log/wtmp log所有到系统的登录和注销) 和syslog (/var/log/messages 里存储所有核心和系统程序信息. /var/log 里的文件经常不确定地增长,应该定期清除
保存到下次引导前有效的关于系统的信息文件.例如,/var/run/utmp 包含当前登录的用户的信息.
静态编译,并指定交叉编译器的路径
注意:busybox-1.20.2目录和rootfs目录在同一个目录下!
在make ,make install之后,,rootfs中的bin,sbin 目录,就有了文件系统需要的命令!
Init进程
以上已经成功的创建了一个根文件系统目录树。虚拟内存盘
CramFS 文件系统的制作
【摘】编程质量--内存移动函数