linux_study_1

Linux 专栏收录该内容
10 篇文章 0 订阅

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压缩工具进行压缩!

我有一个文件abc.txt,我想用bunzip2压缩工具进行压缩

#bzip2 abc.txt

注释:压缩后会得到一个压缩文件abc.txt.bz2,同时原abc.txt会被删除。(这点很重要哦,linux考试会问到这一点)

如果有一个文件abc.txt.bz2,想解压缩:

#bunzip2 abc.txt.bz2

注释:解压后会得到abc.txt,而原abc.txt.bz2会被删除。

 insmod(install module)

功能说明:载入模块 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命令加载模块时,需要使用绝对路径方能加载,且加载时无法自动解决依赖关系。

mountNFS遇到的一个问题(-onolock)

前两天测试过程中,测试机始终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

关于C语言结构体赋值(LINUX内核风格)

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 <stdio.h>


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/

sprintf格式

                                      

 

sprintf格式

Rubysprintf格式与C语言的sprintf(3)基本相同。但还是有些差别: 它没有针对C特有类型的修饰符,shortlong; 它包含2进制数的指示符(%b); 它不支持sprintf的方言式的语法。

下面就对rubysprintf格式进行详细的说明。

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,truefalse的话,将尝试用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

总结了解决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

Fopen函数简介

函数功能:打开一个文件

函数原型:FILE* fopen(const char * path,const char * mode);

相关函数:openfclosefopen_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系统中在文本模式和二进制模式下并无区别。

strtok

目录

原型

功能

说明

返回值

使用

其他相关信息

展开

编辑本段原型

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用法

函数名:strncmp

功 能: 串比较

用 法: intstrncmp(char *str1, char *str2, int maxlen);

说明:此函数功能即比较字符串str1和str2的前maxlen个字符。如果前maxlen字节完全相等,返回值就=0;在前maxlen字节比较过程中,如果出现str1[n]与str2[n]不等,则返回(str1[n]-str2[n])。

fwrite

目录

函数名

功能

用法

程序示例

编辑本段函数名

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 <stdio.h>

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<stdio.h>

#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<SIZE;i++)

if(fwrite(&stu[i], sizeof(student), 1, fp) != 1)

printf("文件写入错误。!\n");

fclose(fp);

}

void main()

{

int i;

for(i=0;i<SIZE;i++)

scanf("%s%d%d%s",&stu[i].name,&stu[i].num,&stu[i].age,&stu[i].addr);

save();

}

示例三:

/* fwrite example : write buffer */

#include <stdio.h>

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));

#pragma pack

目录

对齐方式

对齐用法详解

编辑本段对齐方式

程序编译器对结构的存储的特殊处理确实提高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 错误搜集1

以下错误,都是在别的编译编译正确,而在GCC的错误。dev c++

错误提示:48 D:\××.c [Warning] malformed'#pragma pack(push[, id], <n>)' - ignored

#pragma pack(push)
#pragma pack(1)

struct tagPhysStruct
{
 *****

#pragma pack(pop)

修改开头两行合并为一行。#pragmapack(push1)

如何解决warning: no newline at end of file?

今天写了一段代码, 是在Windows下编辑的, 保存后放在linux系统下编译.

gcccc都产生以下的警告:
a.h:1:2: warning: no newline at end of file

后来发现解决这个问题产生的原因是源文件的最后一行没有回车符造成的; 解决的办法很简单, 在最后一行敲一个回车, 然后保存, 重新编译.

结构体初始化赋值={0},GCC打开-Wall选项编译会警告,大家探讨一下

比如结构体的第一个成员变量是数组或者嵌套的小结构体,linux下编译时gcc打开-Wall选项时会报告警类似如下:

警告:‘char [100]的初始值设定周围缺少花括号
警告:‘struct_a_s的初始值设定周围缺少花括号

因为本来数组或者结构体就得使用{}赋值,但是被嵌套了以后应该使用={{第一个成员变量的指定初始化值},第二个成员变量的指定初始化值}来初始化。故而报警了。
基于此,我分析有两种情况:
一、指定初始化只是第一个成员变量的第一个值,比如数组的第一个值,或者嵌套的小结构体的第一个成员变量的值被指定初始化为0,其他未指定的值根据gcc的编译器来初始化,但是在gccgdb走读代码发现并不符合“静态区初始化为0,动态区随机”规则,而是未指定的都初始化为0
二、指定初始化只是第一个成员变量的值。

windows 如何查看端口占用情况?

开始--运行--cmd进入命令提示符 输入netstat-ano 即可看到所有连接的PID 之后在任务管理器中找到这个PID所对应的程序如果任务管理器中没有PID这一项,可以在任务管理器中选"查看"-"选择列" 

        经常,我们在启动应用的时候发现系统需要的端口被别的程序占用,如何知道谁占有了我们需要的端口,很多人都比较头疼,下面就介绍一种非常简单的方法,希望对大家有用 

假如我们需要确定谁占用了我们的9050端口 

1、Windows平台 
在windows命令行窗口下执行: 
1.查看所有的端口占用情况

C:\>netstat -ano

  协议    本地地址                    外部地址              状态                  PID

  TCP   127.0.0.1:1434         0.0.0.0:0             LISTENING       3236
  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

 

2.查看指定端口的占用情况
C:\>netstat -aon|findstr "9050"

  协议    本地地址                    外部地址              状态                  PID

  TCP    127.0.0.1:9050        0.0.0.0:0             LISTENING       2016

P: 看到了吗,端口被进程号为2016的进程占用,继续执行下面命令: (也可以去任务管理器中查看pid对应的进程)

3.查看PID对应的进程
C:\>tasklist|findstr "2016"

 映像名称                      PID 会话名             会话#      内存使用
 ========================= ======== ================
 tor.exe                    2016Console                0     16,064 K 

P:很清楚吧,tor占用了你的端口。

 

4.结束该进程

C:\>taskkill /f /t /im tor.exe

memmove、memcpy和memccpy简介

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 <string.h>
定义函数: void *memcpy(void *dest, const void *src, size_t n)
函数说明: memcpy()用来拷贝src所指的内存内容前n个字节到dest所指的内存地址上。与strcpy()不同的是,memcpy()会完整的复制n个字节,不会因为遇到字符串结束'\0'而结束
返回值:   返回指向dest的指针

表头文件: #include <string.h>
定义函数: 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 <string.h>
定义函数: 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 |<count

如果在使用这些函数时,分配给足够的空间,然后再使用就不会出现覆盖问题。也就是说如果外部分配给的空间不足以存储要拷贝的数据时,就有可能出现源数据被覆盖更改的问题。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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));再观察输出结果。
可以看出在目的存储空间不足时,便会出现源数据被覆盖改变的问题。
如果目的存储空间分配足够的空间,则便不会出现覆盖问题。

流控制

串口的流控是指数据流。数据在两个串口之间传输时,常常会出现丢失数据的现象,或者两台计算机的处理速度不同,如台式机与单片机之间的通信,接收端数据缓冲区已满,则此时继续发送来的数据就会丢失。当接收端数据处理不过来时,就发出"不再接收"的信号,发送端就停止发送,直到收到“可以继续发送”的信号再发送数据。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()

将字符串转换成浮点型数

 

 

使用pthread_mutex_t锁的例子

linux下为了多线程同步,通常用到锁的概念。
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<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
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的用法 

 

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

函数名:ioctl

头文件:#include<sys/ioctl.h>

功 能: 控制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 <stdlib.h>

#include <stdio.h>

#include <sys/ioctl.h>

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控制都是通过这一部分的代码实现的。

Mkdir函数

原型:int mkdir (const char *filename, mode_t mode)

返回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

信号量的数据类型为结构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<semaphore.h>
int sem_init (sem_t *sem, int pshared, unsigned int value);

这个函数的作用是对由sem指定的信号量进行初始化,设置好它的共享选项,并指定一个整数类型的初始值。pshared参数控制着信号量的类型。如果pshared的值是0,就表示它是当前里程的局部信号量;否则,其它进程就能够共享这个信号量。我们现在只对不让进程共享的信号量感兴趣。 (这个参数受版本影响), pshared传递一个非零将会使函数调用失败。

  这两个函数控制着信号量的值,它们的定义如下所示:

#include<semaphore.h>
int sem_wait(sem_t * sem);
int sem_post(sem_t * sem);

这两个函数都要用一个由sem_init调用初始化的信号量对象的指针做参数。
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<semaphore.h>
int sem_destroy (sem_t *sem);
这个函数也使用一个信号量指针做参数,归还自己战胜的一切资源。在清理信号量的时候如果还有线程在等待它,用户就会收到一个错误。
与其它的函数一样,这些函数在成功时都返回“0”

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>

sem_t bin_sem;
void *thread_function1(void *arg)
{
printf("thread_function1--------------sem_wait\n");
sem_wait(&bin_sem);
printf("sem_wait\n");
while (1)
{
}
}

void*thread_function2(void *arg)
{
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_post

Linux下开启/关闭防火墙命令

1) 永久性生效,重启后不会复原

开启: 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

二.UBuntu关闭防火墙
iptables -A INPUT -i !   PPP0   -j ACCEPT

三.CentOS Linux防火墙配置及关闭

执行”setup”命令启动文字模式配置实用程序,在”选择一种工具”中选择”防火墙配置”,然后选择”运行工具”按钮,出现防火墙配置界面,将”安全级别”设为”禁用”,然后选择”确定”即可.

或者用命令:
#/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 off

Linux下配置ip、子网掩码、网关,并把他们保存在指定的文件中,每次启动后不用重新设置。

1、 可以把它放在/etc/rc.d/init.d/rc.local文件中
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.1

Linux的关机与重启命令

作者: Aillo, 发布于2009-05-10, 在系统分类下, 1条留言

重启命令:
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包或者安装源码包

在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一下吧,通常你会得到你想要的答案。

Linux rpm 命令参数使用详解[介绍和应用]

全屏阅读

2013-03-0823:03:47 作者:王睿宇 所属分类:Linux 阅读:322 评论:0

标签: Linux, rpm, YUM

·  ·  RPMRedHat Package ManagerRedHat软件包管理工具)类似Windows里面的添加/删除程序

rpm 执行安装包
二进制包(Binary)以及源代码包(Source)两种。二进制包可以直接安装在计算机中,而源代码包将会由RPM自动编译、安装。源代码包经常以src.rpm作为后缀名。

常用命令组合:

 

ivh:安装显示安装进度--install--verbose--hash
Uvh:升级软件包--Update
qpl:列出RPM软件包内的文件信息[Query Package list]
qpi:列出RPM软件包的描述信息[Query Package install package(s)]
qf:查找指定文件属于哪个RPM软件包[Query File]
Va:校验所有的RPM软件包,查找丢失的文件[View Lost]
e:删除包

 

 

 

rpm -q samba //查询程序是否安装rpm -ivh  /media/cdrom/RedHat/RPMS/samba-3.0.10-1.4E.i386.rpm //按路径安装并显示进度
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 file.rpm  #[安装新的rpm]--install--verbose--hash
rpm -ivh

rpm -Uvh file.rpm    [升级一个rpm]--upgrade
rpm -e file.rpm      
[删除一个rpm]--erase

 

常用参数:

Install/Upgrade/Erase options:

 

-i, --install                     install package(s)
-v, --verbose                     provide more detailed output
-h, --hash                        print hash marks as package installs (good with -v)
-e, --erase                       erase (uninstall) package
-U, --upgrade=<packagefile>+      upgrade package(s)
-replacepkge                    无论软件包是否已被安装,都强行安装软件包
--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

 

RPM源代码包装安装

.src.rpm结尾的文件,这些文件是由软件的源代码包装而成的,用户要安装这类RPM软件包,必须使用命令:

 

rpm--recompilevim-4.6-4.src.rpm   #这个命令会把源代码解包并编译、安装它,如果用户使用命令:rpm--rebuildvim-4.6-4.src.rpm  #在安装完成后,还会把编译生成的可执行文件重新包装成i386.rpmRPM软件包。

 

偶不喜欢写比较复杂的东东,麻烦的话`不过做为参考`偶还素转了一位哒人的`写的真很全面`

 

作者:北南南北
来自:LinuxSir.Org
提要:RPM Red Hat PackageManager 的缩写,原意是Red Hat 软件包管理;本文介绍RPM,并结合实例来解说RPM手工安装、查询等应用;


++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
正文:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

RPM Red Hat Package Manager 的缩写,本意是Red Hat 软件包管理,顾名思义是RedHat 贡献出来的软件包管理;在Fedora RedhatMandrivaSuSEYellowDog等主流发行版本,以及在这些版本基础上二次开发出来的发行版采用;

RPM包里面都包含什么?里面包含可执行的二进制程序,这个程序和Windows的软件包中的.exe文件类似是可执行的;RPM包中还包括程序运行时所需要的文件,这也和Windows的软件包类似,Windows的程序的运行,除了.exe文件以外,也有其它的文件;

一个RPM 包中的应用程序,有时除了自身所带的附加文件保证其正常以外,还需要其它特定版本文件,这就是软件包的依赖关系;依赖关系并不是Linux特有的, Windows操作系统中也是同样存在的;比如我们在Windows系统中运行3D游戏,在安装的时候,他可能会提示,要安装Direct 9 LinuxWindows原理是差不多的;

软件安装流程图:

 


本文使用范围:

1
、本文是对RPM管理的软件的说明,对通过file.tar.gz file.tar.bz2源码包用 make ;make install 安装的软件无效;
2
、安装软件时,最好用各自发行版所提供的系统软件包管理工具,对于Fedora/Redhat 您可以参考如下文章;

1Fedora 系统管理软件包工具 system-config-packages,方便的添加和移除系统安装盘提供的软件包,详情请看Fedora 软件包管理器system-config-packages

2Redhat 系统管理软件包工具,新一点的系统应该是 redhat-config-packages ,用法和Fedora 软件包管理器system-config-packages一样;

3apt + synaptic 软件包在线安装、移除、升级工具;用法:《用apt+synaptic 在线安装或升级Fedoracore 4.0 软件包》
4
yum 软件包在线安装、升级、移除工具;用法:《Fedora/Redhat 在线安装更新软件包,yum 篇》

5)所有的yumapt 教程apt and yum

目前 aptyum 已经极为成熟了,建议我们安装软件时,采用apt或者yum ;如果安装系统盘提供的软件包,可以用 system-config-packages redhat-config-packages


一、RPM包管理的用途;

1、可以安装、删除、升级和管理软件;当然也支持在线安装和升级软件;
2
、通过RPM包管理能知道软件包包含哪些文件,也能知道系统中的某个文件属于哪个软件包;
3
、可以在查询系统中的软件包是否安装以及其版本;
4
、作为开发者可以把自己的程序打包为RPM 包发布;
5
、软件包签名GPGMD5的导入、验证和签名发布
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软件包管理的查询功能:

命令格式

rpm {-q|--query}[select-options] [query-options]

RPM的查询功能是极为强大,是极为重要的功能之一;举几个常用的例子,更为详细的具体的,请参考#man rpm

1
、对系统中已安装软件的查询;

1)查询系统已安装的软件;

 

语法:rpm-q 软件名

举例:

 

[root@localhostbeinan]# rpm -q gaim
gaim-1.3.0-1.fc4

-q就是 --query ,中文意思是,此命令表示的是,是不是系统安装了gaim ;如果已安装会有信息输出;如果没有安装,会输出gaim 没有安装的信息;

查看系统中所有已经安装的包,要加 -a参数

[root@localhostRPMS]# rpm -qa

如果分页查看,再加一个管道 |more命令;

[root@localhostRPMS]# rpm -qa |more

在所有已经安装的软件包中查找某个软件,比如说gaim ;可以用 grep 抽取出来;

 

[root@localhostRPMS]# rpm -qa |grep gaim

上面这条的功能和rpm -q gaim 输出的结果是一样的;

2)查询一个已经安装的文件属于哪个软件包;

 

语法rpm -qf 文件名

注:文件名所在的绝对路径要指出 

举例:

[root@localhostRPMS]# rpm -qf /usr/lib/libacl.la
libacl-devel-2.2.23-8

3)查询已安装软件包都安装到何处;

 

语法:rpm-ql 软件名 rpm rpmquery -ql 软件名

举例:

 

[root@localhostRPMS]# rpm -ql lynx
[root@localhost RPMS]# rpmquery -ql lynx

4)查询一个已安装软件包的信息

 

语法格式:rpm -qi 软件名

举例:

[root@localhostRPMS]# rpm -qi lynx

5)查看一下已安装软件的配置文件;

 

语法格式:rpm-qc 软件名

举例:

[root@localhostRPMS]# rpm -qc lynx

6)查看一个已经安装软件的文档安装位置:

 

语法格式:rpm -qd 软件名

举例:

 

[root@localhostRPMS]# rpm -qd lynx

7)查看一下已安装软件所依赖的软件包及文件;

 

语法格式:rpm -qR 软件名

举例:

[root@localhostbeinan]# rpm -qR rpm-python

查询已安装软件的总结:对于一个软件包已经安装,我们可以把一系列的参数组合起来用;比如 rpm -qil ;比如:

[root@localhostRPMS]# rpm -qil lynx


2
、对于未安装的软件包的查看:

查看的前提是您有一个.rpm 的文件,也就是说对既有软件file.rpm的查看等;

1)查看一个软件包的用途、版本等信息;

 

语法:rpm -qpi file.rpm

举例:

 

[root@localhostRPMS]# rpm -qpi lynx-2.8.5-23.i386.rpm

2)查看一件软件包所包含的文件;

 

语法:rpm -qpl file.rpm

举例:

[root@localhostRPMS]# rpm -qpl lynx-2.8.5-23.i386.rpm

3)查看软件包的文档所在的位置;

 

语法:rpm -qpd file.rpm

举例:

[root@localhostRPMS]# rpm -qpd lynx-2.8.5-23.i386.rpm

5)查看一个软件包的配置文件;

 

语法:rpm -qpc file.rpm

举例:

[root@localhostRPMS]# rpm -qpc lynx-2.8.5-23.i386.rpm

4)查看一个软件包的依赖关系

 

语法: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

更多的参数,请查看man rpm

举例应用:

[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%]

注: --replacepkgs 参数是以已安装的软件再安装一次;有时没有太大的必要;

测试安装参数--test ,用来检查依赖关系;并不是真正的安装;

 

[root@localhostRPMS]# rpm -ivh --test gaim-1.3.0-1.fc4.i386.rpm
Preparing... ########################################### [100%]

由新版本降级为旧版本,要加--oldpackage 参数;

 

[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

为软件包指定安装目录:要加-relocate 参数;下面的举例是把gaim-1.3.0-1.fc4.i386.rpm指定安装在 /opt/gaim 目录中;

 

[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

为软件包指定安装目录:要加-relocate 参数;下面的举例是把lynx-2.8.5-23.i386.rpm 指定安装在 /opt/lynx 目录中;
[root@localhost RPMS]# rpm -ivh --relocate /=/opt/lynx --badreloclynx-2.8.5-23.i386.rpm
Preparing... ########################################### [100%]
1:lynx ########################################### [100%]

我们安装在指定目录中的程序如何调用呢?一般执行程序,都放在安装目录的bin或者sbin目录中;看下面的例子;如果有错误输出,就做相应的链接,用 ln -s

 

[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 包;

首先您要学会查询rpm;请看前面的说明;

[root@localhost beinan]#rpm -e 软件包名

举例:我想移除lynx包,完整的操作应该是:

[root@localhostRPMS]# rpm -e lynx

如果有依赖关系,您也可以用--nodeps忽略依赖的检查来删除。但尽可能不要这么做,最好用软件包管理器 systerm-config-packages 来删除或者添加软件;

 

[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的签名功能,详情请参见man rpm

五、RPM管理包管理器支持网络安装和查询;

比如我们想通过 Fedora Core 4.0 的一个镜像查询、安装软件包;

地址:

http://mirrors.kernel.org/fedora/core/4/i386/os/Fedora/RPMS/

举例:

命令格式:

 

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 软件名或文件名

通过updatedb,我们可以用locate 来查询一些软件安装到哪里了;系统初次安装时要执行updatedb ,每隔一段时间也要执行一次;以保持已安装软件库最新;updatedb slocate软件包所有;如果您没有这个命令,就得安装slocate

举例:

 

[root@localhostRPMS]# locate gaim


七、从rpm软件包抽取文件;

命令格式:rpm2cpio file.rpm |cpio -div

举例:

[root@localhostRPMS]# rpm2cpio gaim-1.3.0-1.fc4.i386.rpm |cpio -div

抽取出来的文件就在当用操作目录中的usr etc中;

其实这样抽到文件不如指定安装目录来安装软件来的方便;也一样可以抽出文件;

为软件包指定安装目录:要加-relocate 参数;下面的举例是把gaim-1.3.0-1.fc4.i386.rpm指定安装在 /opt/gaim 目录中;

 

[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

这样也能一目了然;gaim的所有文件都是安装在/opt/gaim 中,我们只是把gaim 目录备份一下,然后卸掉gaim;这样其实也算提取文件的一点用法;


八、RPM的配置文件;

RPM
包管理,的配置文件是 rpmrc ,我们可以在自己的系统中找到;比如Fedora Core 4.0中的rpmrc 文件位于;

[root@localhostRPMS]# locate rpmrc
/usr/lib/rpm/rpmrc
/usr/lib/rpm/redhat/rpmrc

我们可以通过rpm --showrc 查看;具体的还得我们自己来学习。呵。。。不要问我,我也不懂;只要您看了这篇文章,认为对您有用,您的水平就和我差不多;咱们水平是一样的,所以我不能帮助您了;请理解;

九、src.rpm的用法:

file.src.rpm 使用方法的简介》


后记:
Fedora/Redhat 入门教程中的软件包管理篇,我已经写了很多了;目前还缺少通过源码包安装软件我方法以及一篇总结性的文档;我想在最近两天补齐,这两篇我以前写过;重新整理一下贴出来就行了;

以我的水平来看,写Fedora入门教程是极为费力气的,只能一点一点的完善和补充;我所写的教程是面对的是对Linux一无所知新手;教程中实例应用占大部份;我发现没有实例的情况下,新手不如看man ;能看man了,当然也不是什么新手;

经常在论坛上看一些弟兄的提问,虽然一问话解说过去也能应付;但想让大家更方便一点,不如写系统入门教程;虽然所花的时间要长一点;

GCC警告选项例解

发布时间:2007-01-0117:26:00  来源: ChinaUnix博客    作者: ChinaUnix博客    点击:358


程序员是追求完美的一族,即使是一般的程序员大多也都不想看到自己的程序中有甚至那么一点点的瑕疵。遇到任意一条编译器警告都坚决不放过。有人会说:我们可以使用比编译器更加严格的静态代码检查工具,如
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 Collection

 

Csocket基本原理

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 函数源码: 

while(!Accept(...))
 
{
 
    if (GetLastError() == WSAEWOULDBLOCK) //  The socket ismarked as nonblocking and no connections are present to be accepted.
 
        PumpMessage(FD_ACCEPT);
 
    else
 
        return FALSE;
 
}

他不断调用 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: 

LRESULT CSocketWnd::OnSocketNotify(WPARAMwParam, LPARAM lParam)
 
{
 
 CSocket::AuxQueueAdd(WM_SOCKET_NOTIFY, wParam, lParam);
 
 CSocket::ProcessAuxQueue();
 
 return 0L ;
 
}

消息参数 wParam socket 的句柄, lParam socket 事件。这里稍作解释一下, CSocketWnd 类是作为 CSocket类的友元类,这意味着他能访问 CSocket类中的保护和私有成员函数和变量,AuxQueueAdd ProcessAuxQueue CSocket 类的静态成员函数,如果你对友元不熟悉,请迅速找本有关C++ 书看一下友元的使用方法吧! 
ProcessAuxQueue
是实质处理socket 事件的函数,在该函数中有这样一句代码:

CAsyncSocket* pSocket =CAsyncSocket::LookupHandle((SOCKET)wParam, TRUE);

其实也就是由 socket 句柄得到发送事件通知消息的 socket 指针 pSocket:从m_pmapSocketHandle 中查找(见1 )!

最后, WSAGETSELECTEVENT(lParam) 会取出事件类型,在一个简单的switch 语句中判断事件类型并调用事件处理函数。 
在这里,事件类型是FD_ACCEPT ,当然就调用pSocket->OnAccept

 

本文的结束:

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 ,他是不可靠的连接方式。

 

从问题看本质:socket到底是什么?

分类: 4.网络与云2011-09-2210:32 2151人阅读 评论(1) 收藏 举报

socket数据结构服务器网络serverunix

一、问题的引入——socket的引入是为了解决不同计算机间进程间通信的问题

1.socket与进程的关系

1).socket与进程间的关系:socket   用来让一个进程和其他的进程互通信息(IPC),而Socket接口是TCP/IP网络的API接口函数。

2).进程间通信(本机内)

进程间通信(不同计算机,要联网)

 

2socket与文件的关系——如何理解socket是种特殊的I/O?

1Socket最先应用于Unix操作系统,如果了解Unix系统的I/O的话,就很容易了解Socket了,因为Socket数据传输其实就是一种特殊的I/O 

2)可对其进行文件操作

3)有文件描述符。而文件描述符的本质是一个非负整数。只是用于区分。类似的还有进程ID

3.服务器端口与连接个数的关系
1
)服务端在8088上监听,然后生成一个新的socketclient通讯。(注意:服务器端监听端口是
不变的,但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下,最多可以有1024socket连接
4
)同时连接请求好像是5个(是连接请求,不是连接),可保持的链接理论上是655352字节的SOCKET端口号),

3.Socket是网络上运行的两个程序间双向通讯的一端,它既可以接受请求,也可以发送请求,利用它可以较为方便的编写网络上数据的传递。

5.问:现在serverclient想建立socket连接,server仅知道clientIP,端口号不知道,能建立连接吗?怎么建立呢?有没有代码看看?
答:CS是相对而言的,发起连接的一方就是C,而监听端口接受连接的一方就是SC如果不知道S监听的端口,怎么发起连接呢,
另外,对于S而言,端口是S上各个服务的区分标志,如果用错误的端口号去连接,是不能获得正确的服务的。
client
的端口是不需要指定的,Server绑定端口,然后监听,client使用serverIP和端口建立socket连接 

 

6.精彩问答

:看到的文章上说每个网络通信循环地进出主计算机的TCP   应用层。它被两个所连接的号码唯一地识别。这两个号码合起来叫做套接字.组成套接字的这两个号码就是机器的IP   地址和TCP   软件所使用的端口号。” 
又说通过socket()函数可以创建一个套接字,然后再把它绑定到端口号...” 
那么套接字socket的概念究竟到哪里为止呢?是仅限于socket()返回的文件描述符?还是是IP和端口号的组合?如果是,那么socket()调用之后产生的套接字描述符的作用是什么呢?   套接字描述符,IP地址,端口号三者间的关系是怎样的? 
谢谢各位前辈解答。
:一个socket句柄代表两个地址对   “本地ip:port”--“远程ip:port”
:那么socket的概念到底到那里为止呢?比如,利用socket()可以产生一个套接字句柄,可是在bind()   或者   connect   ()   之前它只是一个文件描述符,和linux中其他的文件描述符一样。 
如果说socket代表一个两个地址对,那么句柄的作用是不是仅仅是在bind()   或者   connect   ()   之后的用于区分和标记这样的地址对呢?因为这样他才能和网络的概念联系起来。这样的话,socket的意义应该是说用文件描述符描述的通信双方的IP地址和端口号地址对?(而文件描述符是区分这些地址对的标记?)
socket为内核对象,由操作系统内核来维护其缓冲区,引用计数,并且可以在多个进程中使用。 
至于称它为句柄”“文件描述符都是一样的,它只不过是内核开放给用户进程使用的整数而已
问:谢谢楼上,是我没描述清楚。对于句柄文件描述符我没有异议。 
我想我的问题是在于句柄和ipport的关系,不知道我这样说对否: 
1.  
每一个socket   本质上都是指一个ip地址和端口对 
2.  
为了操作这样的地址对,使用了文件描述符 
3.   socket
()函数只创建了一个普通的文件描述符,在进行bind()或者connect()之前并不能说创建了用于网络通讯的套接字 
4.  
只有在进行了bind()或者connect()之后socket才被创立起来
socket()创建了一个socket内核对象。 
accept
或者connect后,才可以对socket句柄读写。因为只有在   connect或者bind,listen,accept后才会设置好socket内核对象里边的ip和端口 

二、socket和端口理解

一个socket句柄代表两个地址对本地ip:port”--“远程ip:port” 
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使
用特定的IPport组合来进行通行的过程就是赋予这个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
函数来建立socketsocket地址之间的关系。其中的socket地址是它想要连接的
服务器端的socket地址。在connect建立socketsocket地址两者关系的同时,它也在
尝试着建立远端的连接。 
3
建立socket连接。 
对于准备建立一个连接,服务器端要两个步骤:bind, listen;客户端一个步骤:
connct
。如果服务器端accept一个connect,而客户端得到了这个accept的确认,那么
一个连接就建立了。
 

三、客户/服务器模式模式的理解

   客户/服务器模式采取的是主动请求方式:

   首先服务器方要先启动,并根据请求提供相应服务:

   1. 打开一通信通道并告知本地主机,它愿意在某一公认地址上(周知口,如FTP21)接收客户请求;

   2. 等待客户请求到达该端口;

   3. 接收到重复服务请求,处理该请求并发送应答信号。接收到并发服务请求,要激活一新进程来处理这个客户请求(如UNIX系统中用forkexec)。新进程处理此客户请求,并不需要对其它请求作出应答。服务完成后,关闭此新进程与客户的通信链路,并终止。

   4. 返回第二步,等待另一客户请求。

   5. 关闭服务器

   客户方:

   1. 打开一通信通道,并连接到服务器所在主机的特定端口;

   2. 向服务器发服务请求报文,等待并接收应答;继续提出请求......

   3. 请求结束后关闭通信通道并终止。

   从上面所描述过程可知:

   1. 客户与服务器进程的作用是非对称的,因此编码不同。

   2. 服务进程一般是先涌纪户请求而启动的。只要系统运行,该服务进程一直存在,直到正常或强迫终止。

半双工通信

即Half-duplexCommunication。这种通信方式可以实现双向的通信,但不能在两个方向上同时进行,必须轮流交替地进行。也就是说,通信信道的每一段都可以是发送端,也可以是接收端。但同一时刻里,信息只能有一个传输方向。如日常生活中的例子有步话机通信,对讲机等。

半双工传输的协议是称为线路规程的过程的一部分,它是OSI模型的第二层,数据链路层所包含的一项功能

MFC打开/保存文件对话框:CFileDialog

CFileDialog


文件选择对话框的使用:首先构造一个对象并提供相应的参数,构造函数原型如下: 
CFileDialog::CFileDialog( BOOL bOpenFileDialog,

LPCTSTRlpszDefExt = NULL,

LPCTSTRlpszFileName = NULL,

DWORDdwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,

 LPCTSTRlpszFilter = NULL,

CWnd*pParentWnd = NULL ,

 DWORDdwSize = 0
);



参数意义如下: 

bOpenFileDialog
TRUE则显示打开对话框,为FALSE则显示保存对话文件对话框。 
lpszDefExt
指定默认的文件扩展名。 
lpszFileName
指定默认的文件名。 
dwFlags
指明一些特定风格。 
lpszFilter
是最重要的一个参数,它指明可供选择的文件类型和相应的扩展名。参数格式如: 
"Chart Files (*.xlc)|*.xlc|Worksheet Files(*.xls)|*.xls|Data Files (*.xlc;*.xls)|*.xlc; *.xls|All Files (*.*)|*.*||";
文件类型说明和扩展名间用 | 分隔,同种类型文件的扩展名间可以用 ; 分割,每种文件类型间用 | 分隔,末尾用 || 指明。 
pParentWnd
为父窗口指针。

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.


创建文件对话框可以使用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);///TRUEOPEN对话框,FALSESAVE AS对话框

//dlg.m_ofn.lpstrInitialDir=_T("d:\\"); //这里就设置了对话框的默认目录d

if(dlg.DoModal()==IDOK)
FilePathName=dlg.GetPathName();

相关信息:CFileDialog用于取文件名的几个成员函数:
假如选择的文件是C:WINDOWSTEST.EXE
:
(1)GetPathName();
取文件名全称,包括完整路径。取回C:WINDOWSTEST.EXE
(2)GetFileTitle();
取文件全名:TEST.EXE
(3)GetFileName();
取回TEST
(4)GetFileExt();
取扩展名EXE

setsockopt的各种使用
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连接服务器过程中,如果处于非阻塞模式下的socketconnect()的过程中可
以设置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类对文件进行读写

2011-05-27 09:55 2028人阅读 评论(0) 收藏 举报

mfcfilewindowsc磁盘api

CFile类提供了对文件进行打开,关闭,读,写,删除,重命名以及获取文件信息等文件操作的基本功能,足以处理任意类型的文件操作


一个读写文件的例子:
文件I/O

  虽然使用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 file;
file.Open(“C://TestFile.txt”, CFile::modeReadWrite);
……
//
直接通过构造函数打开文件
CFile file(“C://TestFile.txt”, CFile::modeReadWrite);

  其中参数CFile::modeReadWrite是打开文件的模式标志,CFile类中与之类似的标志还有十几个,现集中列表如下:

文件模式标志说明 
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
设置文件为文本模式

  这些标志可以通过运算符而同时使用多个,并以此来满足多种需求。例如,需要以读写方式打开文件,如果文件不存在就创建一个新的,如果文件已经存在则不将其文件长度截断为0。为满足此条件,可用CFile::modeCreateCFile::modeReadWriteCFile::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 );
void Write( const void* lpBuf, UINT nCount );

  参数lpBuf为指向存放数据的缓存的指针,nCount为要读入或写入的字节数,Read()返回的为实际读取的字节数,该数值小于或等于nCount,如果小于nCount则说明已经读到文件末尾,可以结束文件读取,如继续读取,将返回0。因此通常可以将实际读取字节数是否小于指定读取的字节数或是否为0作为判断文件读取是否到达结尾的依据。下面这段代码演示了对文件进行一次性写入和循环多次读取的处理过程:

// 创建、写入方式打开文件
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();

Write()和Read()函数执行完后将自动移动文件指针,因此不必再显示调用Seek()函数去定位文件指针。包含有文件定位函数的完整代码如下所示:

// 创建、写入方式打开文件
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();

 

补充:

使用CFile类对文件进行按结构读取,如:

CFilefileRead,fileWrite;
 fileRead.Open(_T("E://a.dat"),CFile::modeRead);//
这里使用宏_T
 fileWrite.Open(_T("E://backup.txt"),CFile::modeCreate |CFile::modeWrite);

VIDEOHEADER *videoheader=new VIDEOHEADER();
fileRead.Read(videoheader,sizeof(VIDEOHEADER));

charbuf[sizeof(VIDEOHEADER)*8];
sprintf(buf,"videoheader.cCommandID:%s ,videoheader->cCommandID);
通过sprintf对我们需要写入文件中的数据进行格式化,这样在文件中存储的数据就是以这里定义的格式显示的。

MFC同步类

·        CCriticalSection 临界区:在用户模式工作 ( 遇到加锁等待时会进入内核模式 ) ,使用与保护线程间共享资源,一个线程可以多次 Lock 不会错。不支持在多进程之间工作。将一段代码置入临界区,只允许最多一个线程进入执行这段代码。一个临界区仅在创建它的进程中有效。

·        CMutex 互斥量:在内核模式工作,除支持临界区的功能外,还可以为互斥量命名,以便在多进程中工作。互斥量比临界区耗资源。一个时刻至多只允许一个线程访问某资源,未被占用时处于有信号状态,可以实现对共享资源的互斥访问。

·        CEvent 事件:在内核模式工作,适用于某一线程等待某事件发生才执行的场合。

·        CSemaphore 信号量 ( 信号灯 ) :在内核模式工作,适用于允许特定个数的线程执行某任务。允许一定数目的线程访问某个共享资源,常用来控制访问共享资源的线程数量。

同步对象的适用场合

·        1.如果某个线程必须等待某些事件发生后才能存取相应的资源,则用 CEvent

·        2.如果一个应用程序同时可以有多个线程存取相应资源,则用 CSemaphore

·        3.如果多个应用程序 ( 多个线程 ) 同时存取相应资源,则用 CMutex ,否则用CCriticalSection

等待类CSingleLock

一个 CSingleLock 类对象代表一种访问控制机制,这种机制用于控制在一个多线程程序中对一个资源的访问。为了使用同步类 CSemaphore  CMutex  CCriticalSection CEvent 所创建的同步对象,你必须创建一个 CSingleLock 或者 CMultiLock 对象来等待和释放这个同步对象。当你只需要每次等待一个对象时,则用 CsingleLock ,否则用CMultiLock 

要使用一个 CSingleLock 对象,在被控制资源的类中的一个成员函数内部调用CSingleLock 的构造函数。然后调用 IsLock 成员函数来确定这个资源是否可用。如果资源是可用的,则继续该成员函数的其余部分。

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的第二种形式,并指定要释放的访问数目。

 MFC 中,具有等待功能的类 CSingleLock  CMultiLock 封装了 Win32 中的等待函数WaitForSingleObject()  WaitForMultipleObjects()  

TTS

TTS的全称为Text To Speech,即“从文本到语音”。它是同时运用语言学和心理学的杰出之作,在内置芯片的支持之下,通过神经网络的设计,把文字智能地转化为自然语音流。 

    TTS技术对文本文件进行实时转换,转换时间之短可以秒计算。在其特有智能语音控制器作用下,文本输出的语音音律流畅,使得听者在听取信息时感觉自然,毫无机器语音输出的冷漠与生涩感。TTS语音合成技术即将覆盖国标一、二级汉字,具有英文接口,自动识别中、英文,支持中英文混读。所有声音采用真人普通话为标准发音,实现了120-150个汉字/秒的快速语音合成,朗读速度达3-4个汉字/秒,使用户可以听到清晰悦耳的音质和连贯流畅的语调。
 

-qws命令

运行嵌入式程序

在嵌入式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<font>         定义程序的字体,例如./myapplication -fn helvetica

 

-bg<color>                设置程序默认背景颜色例如./myapplication -bg blue,颜色名称必须能被QColor类构造函数识别

 

-btn<color>                设置默认的按钮颜色,例如./myapplication -btn green 同样颜色必须被认识

 

-fg<color>                设置foreground颜色,例如./myapplication -fg 'dark blue' 同上需被认识

 

-name <objectname>    设置应用程序名字例如./myapplication -nametexteditapplication

 

-title <title>        设置应用程序标题。./myapplication -title 'Text Edit'

 

-geometry<width>x<height>+<Xoffset>+<Yoffset>

设置窗口大小, ./myapplication-geometry 300x200+50+50

 

-keyboard                    启动键盘

 

-nokeyboard                关闭键盘

 

-mouse                        启动鼠标

 

-nomouse                    关闭鼠标

 

-qws                            设置为服务程序

 

-display                    设置显示器驱动

 

-decoration<style>

设置程序的风格,例如./myapplication-decoration windows,只支持windows default styled

ultraedit 自动缩进修改  

2011-04-29 18:31:41|  分类: 教程技巧|字号 订阅

经常在一行之后回车,或者在if else之后回车,光标不是和上一行对齐,而是向后移动了4个空格(如果是设置是4的话),这样看起来让人很不爽,

自动缩进的修改点,有三个地方,作用不一,自己去体会

1.高级-》配置-》编辑器显示-》格式化

2.找到wordfile.uew 文件进行修改,

一般wordfile.uew 文件的目录是C:\Documents andSettings\***\Application Data\IDMComp\UltraEdit\而不是你的安装目录


找到/IndentStrings = "{" "if" "else"":"  

把 if, else, :去掉就可以了 

然后重新启动ultraedit.

3.艺术样式

http://hi.baidu.com/begin_think/blog/item/a40f4a2eeb9af7341e308976.html 

 工作的时候,要经常拿到别人的代码来改,有些人不太注重编程风格,代码看上去很乱,所以不得不用一些自动排格式的软件先来“格式化”一下,以前我总用VC来排,可是用VC始终感觉太麻烦,而且它的排版功能太死板了,跟自己的风格有些差别,所以想到用UE来试试,没想到真的能改出和自己编程的风格类似的代码。下面就说说我是如何设置UE让它能按我的想法来排版的:


首先单击帮助工具栏(根据设置的不同可能在其他工具栏上)上的“艺术样式”按钮,

弹出如下对话框

然后就可以按照自己的习惯设置缩进的方式了,如果对排版没有特别的要求,默认的选择就可以了,在这种设置状态下,switch后的case是不会缩进的,如果要让case缩进,那么需要选中切换或大小写(就是switch和case,翻译的问题),最好不要两个都选,否则case会缩进两个单位,如果要case后的语句也缩进就选切换,如果只缩进case就选大小写,按照我的习惯是选择切换。其他的选择就看程序员自己的习惯了。
样式我选择的是ANSI,除了GUN,到没有感觉和其他样式有什么区别,有时间再来研究下吧。
UE默认情况下制表符是占两个空格,习惯四个空格的可以选菜单栏中的高级-配置-编辑器-自动换行/制表符设置,将缩进空格改为4就可以了。

typedef 函数指针的用法

在网上搜索函数指针,看到一个例子。开始没看懂,想放弃,可是转念一想,这个用法迟早要弄懂的,现在多花点时间看懂它,好过以后碰到了要再花一倍时间来弄懂它。其实很多时候都是这样,如果每次到难一点的内容,总想着下次我再来解决它,那就永远也学不到东西。

后面那个例子加了注释,是我对这种用法的理解,希望对新手有所帮助。

进入正文:

 代码简化, 促进跨平台开发的目的.

 typedef 行为有点像 #define 宏,用其实际类型替代同义字。

 不同点:typedef 在编译时被解释,因此让编译器来应付超越预处理器能力的文本替换

用法一:

typedef int (*MYFUN)(int, int)
这种用法一般用在给函数定义别名的时候
上面的例子定义MYFUN 是一个函数指针, 函数类型是带两个int 参数, 返回一个int 

分析这种形式的定义的时候可以用下面的方法: 
先去掉typedef 和别名剩下的就是原变量的类型. 
去掉typedefMYFUN以后就剩:
 
int (*)(int, int)

用法二:

typedef给变量类型定义一个别名.

typedef struct{ 
int a; 
int b; 
}MY_TYPE

这里把一个未命名结构直接取了一个叫MY_TYPE的别名, 这样如果你想定义结构的实例的时候就可以这样: 
MY_TYPE tmp;

第二种用法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<iostream.h>   

2.  #include<stdio.h>   

3.    

4.  typedef int (*FP_CALC)(intint);   

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)) (intint)   

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)(intint) = 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<iostream.h>

#include<stdio.h>

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

【转】(转)MFCTRACE的用法

个人总结:最近看网络编程是碰到了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);

同样还存在TRACE0TRACE1TRACE2。。。分别对应012。。个参数

TRACE信息输出到VC IDE环境的输出窗口(该窗口是你编译项目出错提示的哪个窗口),但仅限于你在VC中运行你的DEBUG版本的程序。

TRACE信息还可以使用DEBUGVIEW来捕获到。这种情况下,你不能在VCIDE环境中运行你的程序,而将BUILD好的DEBUG版本的程序单独运行,这个时候可以在DEBUGVIEW的窗口看到DEBUGVIE格式的输出了。

VCTRACE的用法有以下四种:

1:

TRACE   ,就是不带动态参数输出字符串,  类似Cprintf("输出字符串"); 
    
2:

TRACE   中的字符串可以带一个参数输出  , 类似Cprintf("...%d",变量);

3:

TRACE   可以带两个参数输出,类似Cprintf("...%d...%f",变量1,变量2);

4:

TRACE 可以带三个参数输出,类似Cprintf("...%d%d,%d",变量1,变量2,变量3);

TRACE 宏有点象我们以前在C语言中用的Printf函数,使程序在运行过程中输出一些调试信息,使我们能了解程序的一些状态。但有一点不同的是:


TRACE
宏只有在调试状态下才有所输出,而以前用的Printf函数在任何情况下都有输出。和Printf函数一样,TRACE函数可以接受多个参数如:

int x = 1;
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 );

要注意的是TRACE宏只对Debug 版本的工程产生作用,在Release版本的工程中,TRACE宏将被忽略。

linux c语言定时器

linux定时器的使用使用定时器的目的无非是为了周期性的执行某一任务,或者是到了一个指定时间去执行某一个任务。要达到这一目的,一般有两个常见的比较有效的方法。一个是用linux内部的三个定时器,另一个是用sleep,usleep函数让进程睡眠一段时间,其实,还有一个方法,那就是用gettimeofday,difftime等自己来计算时间间隔,然后时间到了就执行某一任务,但是这种方法效率低,所以不常用。

首先来看看linux操作系统为每一个进程提供的3个内部计时器。

ITIMER_REAL: 给一个指定的时间间隔,按照实际的时间来减少这个计数,当时间间隔为0的时候发出SIGALRM信号

ITIMER_VIRTUAL: 给定一个时间间隔,当进程执行的时候才减少计数,时间间隔为0的时候发出SIGVTALRM信号

ITIMER_PROF: 给定一个时间间隔,当进程执行或者是系统为进程调度的时候,减少计数,时间到了,发出SIGPROF信号,这个和ITIMER_VIRTUAL联合,常用来计算系统内核时间和用户时间。

用到的函数有:

#include<sys/time.h>
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;   /*
当前时间计数*/
};

it_interval用来指定每隔多长时间执行任务,it_value用来保存当前时间离执行任务还有多长时间。比如说,你指定it_interval2(微秒为0),开始的时候我们把it_value的时间也设定为2秒(微秒为0),当过了一秒,it_value就减少一个为1再过1秒,则it_value又减少1,变为0,这个时候发出信号(告诉用户时间到了,可以执行任务了),并且系统自动把it_value的时间重置为it_interval的值,即2秒,再重新计数。

为了帮助你理解这个问题,我们来看一个例子:

1.    #include <sys/time.h>

2.    #include <stdio.h>

3.    #include <unistd.h>

4.    #include <signal.h>

5.    #include <string.h>

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. /*设定执行任务的时间间隔为20微秒*/

35. value.it_value.tv_sec = 2;

36. value.it_value.tv_usec = 0;

37. /*设定初始时间计数也为20微秒*/

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_VIRTUALITIMER_PROF的使用方法类似,当你在setitimer里面设置的定时器为ITIMER_VIRTUAL的时候,你把sigaction里面的SIGALRM改为SIGVTALARM,同理,ITIMER_PROF对应SIGPROF

不过,你可能会注意到,当你用ITIMER_VIRTUALITIMER_PROF的时候,你拿一个秒表,你会发现程序输出字符串的时间间隔会不止2秒,甚至5-6秒才会输出一个,至于为什么,自己好好琢磨一下^_^

下面我们来看看用sleep以及usleep怎么实现定时执行任务。

1.    #include <signal.h>

2.    #include <unistd.h>

3.    #include <string.h>

4.    #include <stdio.h>

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 <signal.h>

2.    #include <unistd.h>

3.    #include <string.h>

4.    #include <stdio.h>

5.    #include <time.h>

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<stdio.h>
#include<signal.h>
#include<sys/time.h>//itimerval结构体的定义

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下查看文件和文件夹大小的dfdu命令

来源:芽雨快跑  时间: 2009-02-09 18:07:41  浏览: 102518   评论: 1853 

Tags : df du 文件大小  

   当磁盘大小超过标准时会有报警提示,这时如果掌握df和du命令是非常明智的选择。

   df可以查看一级文件夹大小、使用比例、档案系统及其挂入点,但对文件却无能为力。
    du可以查看文件及文件夹的大小。

   两者配合使用,非常有效。比如用df查看哪个一级目录过大,然后用df查看文件夹或文件的大小,如此便可迅速确定症结。

   下面分别简要介绍

    df命令可以显示目前所有文件系统的可用空间及使用情形,请看下列这个例子:

以下是代码片段:

[yayug@yayu ~]$ df -h
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

   参数 -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
27M     work/testing/logs
35M     work/testing

[root@bsso yayu]# du -h --max-depth=1 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

[root@bsso yayu]# du -h --max-depth=1 work/testing/logs/
27M     work/testing/logs/

[root@bsso yayu]# du -h --max-depth=1 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

   值得注意的是,看见一个针对du和df命令异同的文章:《du df 差异导致文件系统误报解决》。

   du 统计文件大小相加 
    df  统计数据块使用情况

   如果有一个进程在打开一个大文件的时候,这个大文件直接被rm 或者mv掉,则du会更新统计数值,df不会更新统计数值,还是认为空间没有释放。直到这个打开大文件的进程被Kill掉。

   如此一来在定期删除/var/spool/clientmqueue下面的文件时,如果没有杀掉其进程,那么空间一直没有释放。

   使用下面的命令杀掉进程之后,系统恢复。
    fuser -u /var/spool/clientmqueue

du查看目录大小,df查看磁盘使用情况。
我常使用的命令(必要时,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 查看文件属性命令

1,ls
    ls -a 查看所有文件
    ls -l 查看详细的属性
  
2,lsattr
    查看文件的扩展属性,
    如果文件被 chattr +i   添加了写保护,
    用lsattr可以看到添加的属性

3,file
查看文件的类型

4,stat
    查看文件的状态

pthread_attr_init线程属性

分类: linux2011-08-2610:41 4390人阅读 评论(8) 收藏 举报

threadpthreadsstructnulljoin

目录(?)[+]

 

 

 

1.线程属性

       线程具有属性,用pthread_attr_t表示,在对该结构进行处理之前必须进行初始化,在使用后需要对其去除初始化。我们用pthread_attr_init函数对其初始化,用pthread_attr_destroy对其去除初始化。

 

1.

名称::

pthread_attr_init/pthread_attr_destroy

功能:

对线程属性初始化/去除初始化

头文件:

#include<pthread.h>

函数原形:

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;

 

每个个属性都对应一些函数对其查看或修改。下面我们分别介绍。

 

2、线程的分离状态

       线程的分离状态决定一个线程以什么样的方式来终止自己。在默认情况下线程是非分离状态的,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。

而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。所以如果我们在创建线程时就知道不需要了解线程的终止状态,则可以pthread_attr_t结构中的detachstate线程属性,让线程以分离状态启动。

 

2.

名称::

pthread_attr_getdetachstate/pthread_attr_setdetachstate

功能:

获取/修改线程的分离状态属性

头文件:

#include<pthread.h>

函数原形:

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<pthread.h>

 

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);

}

 

3、线程的继承性

       函数pthread_attr_setinheritsched和pthread_attr_getinheritsched分别用来设置和得到线程的继承性,这两个函数的定义如下:

 

3.

名称::

pthread_attr_getinheritsched

pthread_attr_setinheritsched

功能:

获得/设置线程的继承性

头文件:

#include<pthread.h>

函数原形:

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.

       下面我来讲进程的调度策略和调度参数。我会结合下面的函数给出本函数的程序例子。

 

 

4、线程的调度策略

       函数pthread_attr_setschedpolicy和pthread_attr_getschedpolicy分别用来设置和得到线程的调度策略。

 

4.

名称::

pthread_attr_getschedpolicy

pthread_attr_setschedpolicy

功能:

获得/设置线程的调度策略

头文件:

#include<pthread.h>

函数原形:

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线程都在等待锁相同的互斥且,则当互斥量被解锁时,高优先级线程将总是被首先解除阻塞。

 

5、线程的调度参数

       函数pthread_attr_getschedparam 和pthread_attr_setschedparam分别用来设置和得到线程的调度参数。

 

5.

名称::

pthread_attr_getschedparam

pthread_attr_setschedparam

功能:

获得/设置线程的调度参数

头文件:

#include<pthread.h>

函数原形:

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

_vsnprintfC语言库函数之一,属于可变参数。用于向字符串中打印数据、数据格式用户自定义。

1函数简介

2用法实例

1函数简介

头文件:

#include <stdarg.h>

函数声明:

int vsnprintf(char *str, size_t size,  const  char  *format,  va_list ap);

参数说明:

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的限制,防止了内存溢出(sizestr所指的存储空间的大小)。

返回值:执行成功,返回写入到字符数组str中的字符个数(不包含终止符),最大不超过size;执行失败,返回负值,并置errno.[1]

备注:

linux环境下是:vsnprintf

VC6环境下是:_vsnprintf

2用法实例

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 <stdio.h>

#include <stdlib.h>

#include <stdarg.h>

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;

}

SOCKADDR_IN

1基本结构

2参数说明

3经典案例

1基本结构

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_data14字节协议地址

数据结构用做bindconnectrecvfromsendto等函数的参数,指明地址信息。但一般编程中并不直接针对此数据结构操作,而是使用另一个与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;

2参数说明

sin_family指代协议族,在socket编程中只能是AF_INET

sin_port存储端口号(使用网络字节顺序),在linux下,端口号的范围0~65535,同时0~1024范围的端口号已经被系统使用或保留。

sin_addr存储IP地址,使用in_addr这个数据结构

sin_zero是为了让sockaddrsockaddr_in两个数据结构保持大小相同而保留的空字节。

s_addr按照网络字节顺序存储IP地址

sockaddr_insockaddr是并列的结构,指向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()
{
//
创建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 main()
{
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_INETPF_INET的细微不同

在写网络程序的时候,建立TCP socket
    sock = socket(PF_INET, SOCK_STREAM, 0);
然后在绑定本地地址或连接远程地址时需要初始化sockaddr_in结构,其中指定addressfamily时一般设置为AF_INET,即使用IP

相关头文件中的定义:AF = Address Family
                 PF = Protocol Family
                AF_INET = PF_INET

所以在windowsAF_INETPF_INET完全一样. 而在Unix/Linux系统中,在不同的版本中这两者有微小差别.对于BSD,AF,对于POSIXPF.

理论上建立socket时是指定协议,应该用PF_xxxx,设置地址时应该用AF_xxxx。当然AF_INETPF_INET的值是相同的,混用也不会有太大的问题。

在函数socketpairsocketdomain参数中有AF_UNIX,AF_LOCAL,AF_INET,PF_UNIX,PF_LOCAL,PF_INET.
这几个参数有AF_UNIX=AF_LOCAL,PF_UNIX=PF_LOCAL, AF_LOCAL=PF_LOCAL,AF_INET=PF_INET.
**
建议:对于socketpairsocketdomain参数,使用PF_LOCAL系列,
而在初始化套接口地址结构时,则使用AF_LOCAL.
例如:
z = socket(PF_LOCAL, SOCK_STREAM, 0);
adr_unix.sin_family = AF_LOCAL;

popen

计算机科学中的进程I/O函数,与pclose函数一起使用。

目 录

1头文件

2函数定义

3函数说明

4返回值

5返回错误

6使用举例

7真实示例

1头文件

1

#include <stdio.h>

2函数定义

1

2

FILE * popen ( const char * command , const char * type );

int pclose ( FILE * stream );

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

if((fp=popen("/usr/bin/uptime","r"))==NULL);

{

    sprintf(buf,"error: %s\n", strerror(errno));

    ....//异常处理

}

else

{

    ....

    pclose(fp);

}

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

#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简介

原文: 

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;

 }

linux 下route命令

      为了让设备能访问另一个子网,需要在设备里增加路由到子网络,下面是一些资料。基本操作如下:

一般来说,都是为了能访问别的子网才设置路由的,比如说,你的主机处于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

 

摘自鸟哥的私房菜
路由修改 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 ,而上面的资讯有哪些你必须要知道的呢?
               

· Destination, Genmask:这两个玩意儿就是分别是 network netmask 啦!所以这两个咚咚就组合成为一个完整的网域囉!

· Gateway:该网域是通过那个 gateway 连接出去的? 如果显示 0.0.0.0 表示该路由是直接由本机传送,亦即可以透过区域网路的 MAC 直接传讯;如果有显示 IP 的话,表示该路由需要经过路由器 (通讯闸的帮忙才能够传送出去。

· Flags:总共有多个旗标,代表的意义如下:                        

U (route is up):该路由是启动的;                       

H (target is a host):目标是一部主机 (IP) 而非网域;                       

G (use gateway):需要透过外部的主机 (gateway) 来转递封包;                       

R (reinstate route for dynamic routing):使用动态路由时,恢复路由资讯的旗标;                       

D (dynamically installed by daemon or redirect):已经由服务或转 port 功能设定为动态路由                       

M (modified from routing daemon or redirect):路由已经被修改了;                       

!  (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
       
2)不是 169.254.0.0/16 结果到达 

3)0/0 时,OK!传出去了,透过 eth0 将封包传给 192.168.10.30那部 gateway 主机啊!所以说,路由是有顺序的。因此当你重复设定多个同样的路由时,例如在你的主机上的两张网路卡设定为相同网域的 IP 时,会出现什么情况?会出现如下的情况:
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 并不在你的网域内),所以,赶紧检查一下是否输入错误啊!加油吧!

 

# route 命令添加的路由,机器重启或者网卡重启后就没掉了,在linux下设置永久路由的方法:
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

 

===========================================================================================   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

UNIX环境高级编程读书笔记(十一)—终端IO (3)

来源: ChinaUnix博客  日期: 2007.04.09 15:09 (共有条评论) 我要评论

 

2.
名称::
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 tty

select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET

select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型,原型: 
         #include <sys/time.h> 
         #include <unistd.h> 
         int select(int maxfd,fd_set *rdset,fd_set *wrset,fd_set *exset,struct timeval *timeout); 
     
参数maxfd是需要监视的最大的文件描述符值+1rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集 合及异常文件描述符的集合。struct timeval结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0 
     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通常只能包含<32fd(文件描述字),因为fd_set其实只用了一个32位矢量来表示fd;现在,UNIX系统通常会在头文件<sys/select.h>中定义常量FD_SETSIZE,它是数据类型fd_set的描述字数量,其值通常是1024,这样就能表示<1024fd。根据fd_set的位矢量实现,我们可以重新理解操作fd_set的四个宏: 
     fd_set set;
     FD_ZERO(&set);      
     FD_SET(0, &set);    
     FD_CLR(4, &set);      
     FD_ISSET(5, &set);    
―――――――――――――――――――――――――――――――――――――――
注意fd的最大值必须<FD_SETSIZE
―――――――――――――――――――――――――――――――――――――――

     select函数的接口比较简单:
     int select(int nfds, fd_set *readset, fd_set *writeset,fd_set* exceptset, struct tim *timeout);

功能:
     
测试指定的fd可读?可写?有异常条件待处理?      
参数:

—— nfds     
     
需要检查的文件描述字个数(即检查到fd_set的第几位),数值应该比三组fd_set中所含的最大fd值更大,一般设为三组fd_set中所含的最大fd值加1(如在readset,writeset,exceptset中所含最大的fd5,则nfds=6,因为fd是从0开始的)。设这个值是为提高效率,使函数不必检查fd_set的所有1024位。
—— readset    
     
用来检查可读性的一组文件描述字。
—— writeset
     
用来检查可写性的一组文件描述字。
—— exceptset
     
用来检查是否有异常条件出现的文件描述字。(注:错误不包括在异常条件之内)
—— timeout
     
用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0 
     
有三种可能:
       1.timeout=NULL
(阻塞:select将一直被阻塞,直到某个文件描述符上发生了事件)
       2.timeout
所指向的结构设为非零时间(等待固定时间:如果在指定的时间段里有事件发生或者时间耗尽,函数均返回)
       3.timeout
所指向的结构,时间设为0(非阻塞:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生)

返回值:      
     
返回对应位仍然为1fd的总数。

Remarks
     
三组fd_set均将某些fd位置0,只有那些可读,可写以及有异常条件待处理的fd位仍然为1

举个例子,比如recv(),    在没有数据到来调用它的时候,你的线程将被阻塞,如果数据一直不来,你的线程就要阻塞很久.这样显然不好.    
所以采用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    

     理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8fd
     
1)执行fd_set set; FD_ZERO(&set);set用位表示是0000,0000
     
2)若fd5,执行FD_SET(fd,&set);set变为0001,0000(5位置为1)
     
3)若再加入fd2fd=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中的模型21)可以有效突破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返回后循环arrayFD_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)) //
如果sfds中的一员返回非0,否则返回0
                    return 1;
               if (FD_ISSET(*s,&wfds))
                    return 2;
               return 0;
          };
}

ioctl 函数

 

本函数影响由fd 参数引用的一个打开的文件。

 

#include<unistd.h>

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的由于功能的不同,可分为以下几个小类:
• 改变路由表 (例如 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)

与网络接口有关的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无线网络卡会保

 

C语言)共用体union的用法举例

分类: 程序语言2008-10-27 15:14 16926人阅读 评论(8) 收藏 举报

语言cstructmatrixfloat编程

以前在学校学习C语言的时候一直搞不懂那个共用体union有什么用的。工作之后才发现它的一些妙用,现举例如下:

1. 为了方便看懂代码。

比如说想写一个3 * 3的矩阵,可以这样写:
注:下面用红色部分标记的地方是后来添加上去的,谢谢yrqing718的提醒!]

  1. struct  Matrix
  2. {
  3.     union
  4.     {
  5.         struct
  6.         {
  7.             float  _f11, _f12, _f13, _f21, _f22, _f23, _f31, _f32, _f33;
  8.         };
  9.         float  f[3][3];
  10.     }_matrix;
  11. };
  12.  
  13. struct  Matrix m;
  14.  


这两个东西共同使用相同的空间,所以没有空间浪费,在需要整体用矩阵的时候可以用
m._matrix.f
(比如说传参,或者是整体赋值等);需要用其中的几个元素的时候可以用m._matrix._f11那样可以避免用m.f[0][0](这样不大直观,而且容易出错)。

2.
用在强制类型转换上(比强制类型转换更加容易看懂)
下面举几个例子:

1. 判断系统用的是bigendian 还是 little endian(其定义大家可以到网上查相关资料,此略)

  1. #define TRUE 1
  2. #define FALSE 0
  3. #define BOOL int


  4.  
  5. BOOL  isBigEndian()
  6. {
  7.     int  i = 1;   /* i = 0x00000001*/
  8.     char  c = *(char  *)&i; /* 注意不能写成 char c = (char)i; */
  9.     return  (int )c != i;
  10. }

如果是littleendian字节序的话,那个i= 1;的内存从小到大依次放的是:0x010x00 0x00 0x00,如是,按照i的起始地址变成按照char*方式(1字节)存取,即得c= 0x01
反之亦然

也许看起来不是很清晰,下面来看一下这个:

  1. BOOL  isBigEndian()
  2. {
  3.     union
  4.     {
  5.         int  i;
  6.         char  c;
  7.     }test;
  8.     
  9.     test.c = 2;
  10.  
  11.     return  test.i != 2;
  12. }

这里用的是union来控制这个共享布局,有个知识点就是union里面的成员ci都是从低地址开始对齐的。同样可以得到如此结果,而且不用转换,清晰一些。

什么,不觉得清晰??那再看下面的例子:

2. littleendian下的longlong类型的值换成 bigendian类型的值。已经知道系统提供了下面的apilonghtonl(long lg);作用是把所有的字节序换成大端字节序。因此得出下面做法:

  1. long  long  htonLL(long  long  lg)
  2. {
  3.     union  
  4.     {
  5.         struct  
  6.         { 
  7.             long  low;
  8.             long  high;
  9.         }val_1;
  10.         long  long  val_2;
  11.     }val_arg, val_ret;
  12.  
  13.  
  14.     if ( isBigEndian() )
  15.         return  lg;
  16.     val_arg.val_2 = lg;
  17.  
  18.  
  19.     val_ret.val_1.low = htonl( val_arg.val_1.high );
  20.     val_ret.val_1.high = htonl( val_arg.val_1.low );    
  21.  
  22.     return  val_ret.val_2;
  23. }

只要把内存结构的草图画出来就比较容易明白了。

(3).
为了理解c++类的布局,再看下面一个例子。有如下类:

  1. class  Test
  2. {
  3. public :
  4.     float  getFVal(){ return  f;}
  5. private :
  6.     int  i;
  7.     char  c;
  8.     float  f;
  9. };
  10. Test t;

 

不能在类Test中增加代码,给对象中的f赋值7.0f.

  1. class  Test_Cpy
  2. {
  3.  public :
  4.     float  getVal(){ return  f;}
  5.     float  setVal(float  f){ this ->f = f;}
  6. private :
  7.     int  i;
  8.     char  c;
  9.     float  f;
  10. };
  11.  
  12. ....
  13.  
  14. int  main()
  15. {
  16.     Test t;
  17.     union
  18.     {
  19.          Test t1, 
  20.          Test_Cpy t2;
  21.     }test;
  22.  
  23.     test.t2.setVal(7.0f);
  24.     t = test.t1;
  25.     assert( t.getVal() == 7.0f );   
  26.  
  27.     return  0;
  28. }

说明:因为在增加类的成员函数时候,那个类的对象的布局基本不变。因此可以写一个与Test类一样结构的类Test_Cpy,而多了一个成员函数setVal,再用uinon结构对齐,就可以给私有变量赋值了。(这种方法在有虚机类和虚函数机制时可能失灵,故不可移植)至于详细的讨论,网上有,这个例子在实际中没有用途,只是用来考察这个内存布局的使用而已.

union
在操作系统底层的代码中用的比较多,因为它在内存共赏布局上方便且直观。所以网络编程,协议分析,内核代码上有一些用到union都比较好懂,简化了设计。

功能描述: 
获取或者设置与某个套接字关联的选项。选项可能存在于多层协议中,它们总会出现在最上面的套接字层。当操作套接字选项时,选项位于的层和选项的名称必须给出。为了操作套接字层的选项,应该将层的值指定为SOL_SOCKET。为了操作其它层的选项,控制选项的合适协议号必须给出。例如,为了表示一个选项由TCP协议解析,层应该设定为协议TCP

Linux下getsockopt/setsockopt函数说明  


用法: 
#include <sys/types.h>
#include <sys/socket.h>

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);

参数:   
sock
:将要被设置或者获取选项的套接字。
level
:选项所在的协议层。
optname
:需要访问的选项名。
optval
:对于getsockopt(),指向返回选项值的缓冲。对于setsockopt(),指向包含新选项值的缓冲。
optlen
:对于getsockopt(),作为入口参数时,选项值的最大长度。作为出口参数时,选项值的实际长度。对于setsockopt(),现选项的长度。

    
返回说明:   
成功执行时,返回0。失败返回-1errno被设为以下的某个值   
EBADF
sock不是有效的文件描述词
EFAULT
optval指向的内存并非有效的进程空间
EINVAL
:在调用setsockopt()时,optlen无效
ENOPROTOOPT
:指定的协议层不能识别选项
ENOTSOCK
sock描述的不是套接字

level指定控制套接字的层次.可以取三种值: 
1)SOL_SOCKET:
通用套接字选项. 
2)IPPROTO_IP:IP
选项. 
3)IPPROTO_TCP:TCP
选项.
optname
指定控制的方式(选项的名称),我们下面详细解释 

optval获得或者是设置套接字选项.根据选项名称的数据类型进行转换 


选项名称        说明                  数据类型 
========================================================================
 
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_OPTINOS
IP首部选项               int 
IP_TOS
         服务类型 
IP_TTL
         生存时间                int 
========================================================================
IPPRO_TCP 
------------------------------------------------------------------------
TCP_MAXSEG
TCP最大数据段的大小          int 
TCP_NODELAY
不使用Nagle算法            int 
========================================================================

返回说明:   
成功执行时,返回0。失败返回-1errno被设为以下的某个值   
EBADF
sock不是有效的文件描述词
EFAULT
optval指向的内存并非有效的进程空间
EINVAL
:在调用setsockopt()时,optlen无效
ENOPROTOOPT
:指定的协议层不能识别选项
ENOTSOCK
sock描述的不是套接字

SO_RCVBUFSO_SNDBUF每个套接口都有一个发送缓冲区和一个接收缓冲区,使用这两个套接口选项可以改变缺省缓冲区大小。

// 接收缓冲区
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));

注意:

当设置TCP套接口接收缓冲区的大小时,函数调用顺序是很重要的,因为TCP的窗口规模选项是在建立连接时用SYN与对方互换得到的。对于客户,SO_RCVBUF选项必须在connect之前设置;对于服务器,SO_RCVBUF选项必须在listen前设置。

SVN多版本库配置问题

阅读: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"  

以上中使用的路径等,请自行转换成各自对应的路径。

windows 下本机配置svn以及多版本库的创建

软件下载

服务器和客户端安装

建立版本库(Repository

配置用户和权限

运行独立服务器

初始化导入

基本客户端操作

建立多版本库


1
、软件下载

下载Subversion服务器程序。


下载SubversionWindows客户端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

含义是:

用户admin的密码为ren

5
、运行独立服务器

安装subversionbin目录下不知为何没有svnservice.exe,将svnservice.exe放在subversionbin目录下。

dos控制台状态下cd 进入subversion的安装目录的bin目录,

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.

怀疑是win7vistaUAC问题,打开开始菜单,找到命令行的快捷方式,右键,以管理员身份运行,

svnserivce -install -d-r 。。。。。。


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最新版本后再提交。

8、建立多版本库

svn 在一个版本库下管理多个工程,不会为每个工程建一个版本库,这样会导致版本库跳跃,所以有时必须建立多个版本库

svn 在一个版本库下管理多个工程会导致版本库跳跃,所以有时必须建立多个版本库

(1)、把所有的版本库的放在一个统一的目录下(F:\svn),在此目录下我们要建立两个工程的版本库Respontory1Respontory2(必须先建好这两个文件夹)。

(2)、创建第一个项目project1版本库,命令:svnadmincreate F\svnroot\Respontory1

(3)、创建第二个项目project2版本库,命令:svnadmincreate F:\svnroot\Respontory2,当然这两步也可以用TortoiseSVN建立

(4)、为了便于管理,将所有版本库的密码和权限设置在同一个文件下面,操作步骤如下:

     A、取出Respontory1下面conf文件夹下的authzpasswd两个文件到svn根目录下面

      B、修改每个版本库目录conf文件夹下面的svnserve.conf文件,

           # anon-access = read#auth-access = write#password-db = passwd#authz-db = authz

           修改为:

           anon-access = noneauth-access= writepassword-db= ../../passwdauthz-db= ../../authz

          (password-db = ../../passwdauthz-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

到此为止就配置成功了你可以将两个工程导入到这两个版本库中,而且版本号不相互影响。

【C/C++】Linux下使用system()函数一定要谨慎

15人收藏此文章, 我要收藏发表于1年前(2012-04-15 00:35) , 已有16021次阅读,共3个评论

曾经的曾经,被system()函数折磨过,之所以这样,是因为对system()函数了解不够深入。只是简单的知道用这个函数执行一个系统命令,这远远不够,它的返回值、它所执行命令的返回值以及命令执行失败原因如何定位,这才是重点。当初因为这个函数风险较多,故抛弃不用,改用其他的方法。这里先不说我用了什么方法,这里必须要搞懂system()函数,因为还是有很多人用了system()函数,有时你不得不面对它。

 

先来看一下system()函数的简单介绍:

1

#include <stdlib.h>

2

int system(const char *command);

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执行期间,SIGINTSIGQUIT是被忽略的,意思是进程收到这两个信号后没有任何动作。

 

再来看一下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通过exitreturn返回的值。

(注意,command顺利执行不代表执行成功,比如command"rm debuglog.txt",不管文件存不存在该command都顺利执行了)

如果exec执行失败,也即command没有顺利执行,比如被信号中断,或者command命令根本不存在,system()函数返回127.

如果commandNULL,则system()函数返回非0值,一般为1.

 

看一下system()函数的源码

看完这些,我想肯定有人对system()函数返回值还是不清楚,看源码最清楚,下面给出一个system()函数的实现:

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

}

仔细看完这个system()函数的简单实现,那么该函数的返回值就清晰了吧,那么什么时候system()函数返回0呢?只在command命令返回0时。

 

看一下该怎么监控system()函数执行状态

这里给我出的做法:

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

}

到于取得子进程返回值的相关介绍可以参考另一篇文章:http://my.oschina.net/renhc/blog/35116

 

system()函数用起来很容易出错,返回值太多,而且返回值很容易跟command的返回值混淆。这里推荐使用popen()函数替代,关于popen()函数的简单使用也可以通过上面的链接查看。

popen()函数较于system()函数的优势在于使用简单,popen()函数只返回两个值:
成功返回子进程的status,使用WIFEXITED相关宏就可以取得command的返回结果;
失败返回-1,我们可以使用perro()函数或strerror()函数得到有用的错误信息。

这篇文章只涉及了system()函数的简单使用,还没有谈及SIGCHLDSIGINTSIGQUITsystem()函数的影响,事实上,之所以今天写这篇文章,是因为项目中因有人使用了system()函数而造成了很严重的事故。现像是system()函数执行时会产生一个错误:No child processes

关于这个错误的分析,感兴趣的朋友可以看一下:http://my.oschina.net/renhc/blog/54582

返回值:若成功调用一次则返回两个值,子进程返回0父进程返回子进程ID;否则,出错返

 

fork()函数 UNIX

头文件:

#include<unistd.h>

#include<sys/types.h>

函数原型:

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之后,数据、堆栈有两份,代码仍然为一份但是这个代码段成为两个进程的共享代码段都从fork函数中返回,箭头表示各自的执行处。当父子进程有一个想要修改数据或者堆栈时,两个进程真正分裂。

示例代码:

#include<sys/types.h> //对于此程序而言此头文件用不到

#include<unistd.h>

#include<stdio.h>

#include<stdlib.h>

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);

}

//可能需要时候waitwaitpid函数等待子进程的结束并获取结束状态

exit(0);

}

注意!样例代码仅供参考,样例代码存在着父进程在子进程结束前结束的可能性。必要的时候可以使用wait waitpid函数让父进程等待子进程的结束并获取子进程的返回状态。

fork()Linux系统中的返回值是没有NULL.

Error Codes

出错返回错误信息如下:

EAGAIN

达到进程数上限.

ENOMEM

没有足够空间给一个新进程分配.

fork函数的特点概括起来就是调用一次,返回两次,在父进程中调用一次,在父进程和子进程中各返回一次。

fork的另一个特性是所有由父进程打开的描述符都被复制到子进程中。父、子进程中相同编号的文件描述符内核中指向同一个file结构体,也就是说,file结构体的引用计数要增加。

Linux下/proc目录简介

分类: Java2012-07-1502:22 7896人阅读 评论(0) 收藏 举报

linuxlinux内核filesystemsprotocolscachetimer

1. /proc目录
Linux
内核提供了一种通过 /proc 文件系统,在运行时访问内核内部数据结构、改变内核设置的机制。proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口。

用户和应用程序可以通过proc得到系统的信息,并可以改变内核的某些参数。由于系统的信息,如进程,是动态改变的,所以用户或应用程序读取proc文件时,proc文件系统是动态从系统内核读出所需信息并提交的。下面列出的这些文件或子文件夹,并不是都是在你的系统中存在,这取决于你的内核配置和装载的模块。另外,在/proc下还有三个很重要的目录:netscsisys Sys目录是可写的,可以通过它来访问或修改内核的参数,而netscsi则依赖于内核配置。例如,如果系统不支持scsi,则scsi 目录不存在。

除了以上介绍的这些,还有的是一些以数字命名的目录,它们是进程目录。系统中当前运行的每一个进程都有对应的一个目录在/proc下,以进程的 PID号为目录名,它们是读取进程信息的接口。而self目录则是读取进程本身的信息接口,是一个link

2. 子文件或子文件夹
/proc/buddyinfo
每个内存区中的每个order有多少块可用,和内存碎片问题有关

/proc/cmdline 启动时传递给kernel的参数信息

/proc/cpuinfo cpu的信息

/proc/crypto 内核使用的所有已安装的加密密码及细节

/proc/devices 已经加载的设备并分类


/proc/dma
已注册使用的ISA DMA频道列表

/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 根据过去一段时间内CPUIO的状态得出的负载状态,与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 pidN的进程信息

/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. 例子
3.1 /proc/
yafang@QA:~$ ls/proc/

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
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


Character 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

 


root@BCNMB-A:~#

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
Linux
内核版本和gcc版本

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
该文件指定了可以分配的文件句柄的最大数目。如果用户得到的错误消息声明由于打开文件数已经达到了最大值,从而他们不能打开更多文件,则可能需要增加该值。可将这个值设置成有任意多个文件,并且能通过将一个新数字值写入该文件来更改该值。默认设置时4096

改变内核的参数,用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的条件分支语句:
iflist1
then 
list2
fi
其中list1其实就是一个命令列表,列表只有一项时就是单个命令。if的条件就是列表中最后一个命令执行的返回值。
所以要判断某个命令执行的返回值,只要直接把命令放在if后面就行了,千万不要画蛇添足地加上反引号!看例子:

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

复制代码



`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. 创建初始设备节点

内核在引导时要求某些设备节点必须存在(特别是 console  null ),这些设备节点必须创建在硬盘上才能使得内核在 udev 尚未启动之前就可以使用它们,特别是当系统以单用户模式启动(仅允许使用 console)的时候更是如此。使用下面的命令来创建这些节点:

mknod -m 600 /dev/console c 5 1
mknod -m 666 /dev/null c 1 3

6.8.2. 挂载 ramfs 并填充 /dev 目录

推荐的向 /dev 目录填充设备的方法是在 /dev 上挂载一个虚拟文件系统(比如 tmpfs),然后在设备被检测到或被访问到的时候(通常是在系统引导的过程中)动态创建设备节点。既然现在新的系统尚未被引导,那么就有必要通过挂载 /dev 来手工完成 LFS-Bootscripts 将来要做的事情:

mount -nvt tmpfs none /dev

Udev软件包是实际用于在 /dev 目录中添加设备的工具。但是由于它要在后面的步骤中才被安装,我们现在必须手动创建一个必需的设备文件的最小集合,以便继续构建我们的系统。[注意]前面创建的 console null 设备文件(保存在硬盘上)被新挂载的 tmpfs 文件系统隐藏了,所以这里还要再创建一次。

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}

有一些在系统启动的时候由 LFS-Bootscripts 创建的符号连接和目录是 LFS 系统所必须的。既然目前只是 chroot 后的环境而不是真实启动后的环境,那么就需要在这里先创建他们:

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

上面的 mount 命令可能会导致下面的警告信息:

can't open /etc/fstab: No such file or directory.

因为在挂载文件系统时需要 /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 
  比/tmp大或需要存较长时间的临时文件.

/var/lock 
  锁定文件.许多程序遵循在/var/lock 中产生一个锁定文件的约定,以支持他们正在使用某个特定的设备或文件.其他程序注意到这个锁定文件,将不试图使用这个设备或文件.

/var/log 
  各种程序的log文件,特别是login (/var/log/wtmp log所有到系统的登录和注销) 和syslog (/var/log/messages 里存储所有核心和系统程序信息. /var/log 里的文件经常不确定地增长,应该定期清除

/var/run 
  保存到下次引导前有效的关于系统的信息文件.例如,/var/run/utmp 包含当前登录的用户的信息.

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           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








静态编译,并指定交叉编译器的路径



指定命令的安装路径


注意:busybox-1.20.2目录和rootfs目录在同一个目录下!
在make ,make install之后,,rootfs中的bin,sbin 目录,就有了文件系统需要的命令!

Init进程


以上已经成功的创建了一个根文件系统目录树。

虚拟内存盘

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是内存盘序号)

CramFS 文件系统的制作  

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”语句,增强程序的健壮性,那么在调试通过后我们再改为“#undef DEBUG”语句,提高程序的性能。事实上在标准库里已经存在类似功能的宏:assert,而且更加好用,它还可以在定义DEBUG时指出代码在那一行检查失败,而在没有定义DEBUG时完全可以把它当作不存在。assert(_Expression)的使用非常简单,当_Expression为0时,调试器就可以出现一个调试错误,有了这个好东西代码就容易多了。

程序清单 7 V0.6版程序

void * MyMemMove(void *dst,const void*src,int count)

{

assert(dst);

assert(src);

void *ret=dst;

while (count--)

{

*(char *)dst = *(char *)src;

dst = (char *)dst + 1;

src = (char *)src + 1;

}

return ret;

}

一旦调用者的两个指针参数其中一个为零,就会出现如图1所示的错误,而且指示了哪一行非常容易查错。

图 1 assert(NULL)时,显示错误

到目前为止,在语言层面上,我们的程序基本上没有什么问题了,那么是否真的就没有问题了呢?这就要求程序员从逻辑上考虑了,这也是优秀程序员必须具备的素质,那就是思维的严谨性,否则程序就会有非常隐藏的bug,就这个例子来说,如果用户用下面的代码来调用你的程序。

程序清单 8 重叠的内存测试

void Test()

{

char p [256]= "hello,world!";

MyMemMove(p+1,p,strlen(p)+1);

printf("%s\n",p);

}

如果你身边有电脑,你可以试一下,你会发现输出并不是我们期待的“hhello,world!”(在“hello world!”前加个h),而是“hhhhhhhhhhhhhh”,这是什么原因呢?原因出在源地址区间和目的地址区间有重叠的地方,V0.6版的程序无意之中将源地址区间的内容修改了!有些反映快的同学马上会说我从高地址开始拷贝。粗略地看,似乎能解决这个问题,虽然区间是重叠了,但是在修改以前已经拷贝了,所以不影响结果。但是仔细一想,这其实是犯了和上面一样的思维不严谨的错误,因为用户这样调用还是会出错:

MyMemMove( p, p+1, strlen(p)+1);

所以最完美的解决方案还是判断源地址和目的地址的大小,才决定到底是从高地址开始拷贝还是低地址开始拷贝,所以V0.7顺利成章地出来了。

程序清单 9 V0.7版程序

void * MyMemMove(void *dst,const void*src,int count)

{

assert(dst);

assert(src);

void * ret = dst;

if (dst <= src || (char *)dst >=((char *)src + count)) {

while (count--) {

*(char *)dst = *(char *)src;

dst = (char *)dst + 1;

src = (char *)src + 1;

}

}

else {

dst = (char *)dst + count - 1;

src = (char *)src + count - 1;

while (count--) {

*(char *)dst = *(char *)src;

dst = (char *)dst - 1;

src = (char *)src - 1;

}

}

return(ret);

}

经过以上7个版本的修改,我们的程序终于可以算是“工业级”了。回头再来看看前面的测试用例,就会发现那根本就算不上是测试用例,因为它只调用了最正常的一种情况,根本达不到测试的目的。有了上面的经历,测试用例也就相应地出现了,我们不妨用字符数组来模拟内存。

程序清单 10 相对全面的测试用例

void Test()

{

char p1[256] = "hello,world!";

char p2[256] = {0};

MyMemMove(p2,p1,strlen(p1)+1);

printf("%s\n",p2);

MyMemMove(NULL,p1,strlen(p1)+1);

MyMemMove(p2,NULL,strlen(p1)+1);

MyMemMove(p1+1,p1,strlen(p1)+1);

printf("%s\n",p1);

MyMemMove(p1,p1+1,strlen(p1)+1);

printf("%s\n",p1);

}

初写代码的时候,往往考虑的是程序正常工作的情况该怎么处理。当你有了几年经验,写了几万行代码后就会发现,处理异常部分的分支代码有时比正常的主干线代码还要多,而这也正是高质量程序和一般程序拉开差距的地方。如果把软件产品当作一台机器,那么这样一个个细小的函数和类就是零部件,只有当这些零部件质量都很高时,整个软件产品的质量才会高,不然就会像前几年的国产轿车一样,今天这个零件罢工明天那个零件休息。而作为检验这些零部件的测试用例,一定要模拟各种恶劣的环境,将零部件隐藏的缺陷暴露出来,从这意义上说,编写测试用例的程序员要比软件设计的程序员思维要更严谨才行

 

vim与复制,删除,粘贴,块操作以及快速替换功能

分类: Linux学习2012-10-3110:48 2419人阅读 评论(0) 收藏 举报

目录(?)[+]

掌握如下命令有什么好办法,我可以告诉你唯手熟尔!!多看多练
对于VIM而言,复制,删除,粘贴的操作应该是非常多的。这次也做一个总结,关于处理VIM下的复制,删除,粘贴等操作。
学会使用帮助文件,命令的帮助入口,就是:help 命令名。例如,对于j命令,查看它的帮助,使用:help j

先谈一下基于块的复制,删除,粘贴操作


使用块选的好处
对于vim几乎提到的都是以行为单位的操作。那么如果我想要搞定的是一个区块范围呢(我特别喜欢像使用windows下的文本编辑器一样去使用块的选择

当我们按下v 戒者V 戒者[Ctrl]+v 时,这个时候光标移劢过癿地方就会开始反白。

区块选择的按键意义 【不使用鼠标,来选择块】


v                                    字符选择,会将光标经过癿地方反白选择!

V(大写)                         行选择,会将光标经过癿行反白选择!(常用,配合上下左右键,进行区域选择,很爽!!!)

[Ctrl]+v                          区块选择,可以用长方形癿方式选择资料

y                                    将反白癿地方复制起来d 将反白癿地方删除掉

批量替换列块

原始状态:

将光标指到第一行V处,然后ctrl-v选择,如下

选择完成后,接着按c键,输入你要替换后的文字,最后ESC,看看成果如下,

以行或者是以单词为单位的删除操作


对字符操作:
x,X                在一行字当中,x为向后删除一个字符(相当亍[del] 按键)X 为向前删除一个字符(相当亍[backspace] 亦即是退格键)(常用)

nx                  n 为数字,连续向后删除n 个字符。丼例来说,我要连续删除10 个字符,10x』。

d$                  删除游标所在处,到该行行尾的所有字符

d0                  删除游标所在处,到该行行首的所有字符
 对行操作:

dd                   删除游标所在的那一整列(常用)

ndd                 n 为数字。删除光标所在的行向下n行,例如 20dd 则是删除 20 (常用)

d1G                删除光标所在到第一行的所有数据

dG                  删除光标所在到最后一行的所有数据

与移动相关


移动到行首:gg

移动到行尾:字符$

       小注:命令G前可以加上数字,在这里,数字的含义并不是倍数,而是你打算跳转的行号。例如,你想跳转到文件的第1234行,只需输入1234G

字符0:第一个非空字符前

字符^:第一个非空字符上

上下左右:h,j, k, l

       小注:如同许多vim命令一样,可以在这些键前加一个数字,表示移动的倍数。例如,10j表示向下移动10行;10l表示向右移动10列。

翻页:

       小注:通常使CTRL-BCTRL-F来进行翻页,它们的功能等同于PageUpPageDownCTRL-BCTRL-F前也可以加上数字,来表示向上或向下翻多少页

      小注: 在文件中移动,你可能会迷失自己的位置,这时使用CTRL-G命令,查看一下自己位置。这个命令会显示出光标的位置及其它信息。为了避免迷失,你可以打开行号显示;使用:set number命令后,会在每一行前显示出行号,可以更方便的定位的跳转(:help‘number’

移动到指定字符: 使用f, t, F,T命令。    

        小注:f命令移动到光标右边的指定字符上,例如,fx,会把移动到光标右边的第一个’x'字符上。F命令则反方向查找,也就是移动到光标左边的指定字符上。

t命令和f命令的区别在于,它移动到光标右边的指定字符之前。例如,tx会移动到光标右边第一个’x'字符的前面。T命令是t命令的反向版本,它移动到光标右边的指定字符之后。

这四个命令只在当前行中移动光标,光标不会跨越回车换行符。

可以在命令前面使用数字,表示倍数。例如,3fx表示移动到光标右边的第3’x'字符上。

;命令重复前一次输入的f,t, F, T命令,而,命令会反方向重复前一次输入的f,t, F, T命令。这两个命令前也可以使用数字来表示倍数。

 

与复制相关


yy                复制游标所在癿那一行(常用)

y1G              复制光标所在列到第一列癿所有数据

yG               复制光标所在列到最后一列癿所有数据

y0               复制光标所在癿那个字符到该行行首癿所有数据

y$               复制光标所在癿那个字符到该行行尾癿所有数据

 

复原以及重做操作


u 复原前一个劢作。(常用)

[Ctrl]+r 重做上一个动作。(常用)

替换模式


 

关键是“R “ 取代模式,它可以快速的替换以及插入数据(类似于 word中的即点即输)的功能。

移动光标

命令

功能

h

光标左移

l(或空格键)

光标右移

k(或[Ctrl+p])

光标上移

j(或[Ctrl+n])

光标下移

[Ctrl+f]

向前翻页

[Ctrl+b]

向后翻页

[Ctrl+d]

向前翻半页

[Ctrl+u]

向后翻半页

1G

跳到文件首

40G

跳到第40行

G

跳到文件尾

[Ctrl+g]

显示当前行号及文件的百分比

:set number

显示所有带行号的行

在一行内移动光标
BEW命令的作用与bew的作用一样,只是后者忽略了标点符号。

命令

功能

b

向后移动到单词首

e

向前移动到单词尾

w

向前移动到单词首

0或|

移动到行首

30|

移动到第30列

^

移动到本行的第一个单词的首字符

$

移动到行尾

插入文本

命令

功能

i

在光标的左侧插入字符

20i-[Esc]

插入20个连字符

I

在行首插入字符

[Ctrl+v][Ctrl+b]

插入[Ctrl+b]字符

[Ctrl+v][Esc]

插入[Esc]字符

a

在光标的右侧添加文本

A

在行尾添加文本

o

在当前行下面插入一空行

O

在当前行上面插入一空行

:set showmode

当vi处在输入模式下时显示提示信息

:set sm

即时显示配对的)或}

:set ts=n

把制表符设置为n(默认为8)

:set ai

下一行从前一行的缩进位置开始

删除和移动文本

命令

功能

x

删除光标所在的字符

6x

删除光标位置的字符以及右侧的5个字符

X

删除前一个字符

dd

删除当前行

4dd

删除4行

64dd

删除64行

dw

删除一个单词

d0

删除到行首

d$

删除到行尾

d

删除一个区域(只用于vim)

p

把被剪切的文本粘贴到下面(整行)或右侧(部分行)

P

把被剪切的文本粘贴到上面(整行)或左侧(部分行)

"add

把当前行粘贴到缓冲区a里

"ap

把缓冲区a里的内容粘贴到当前位置

J

把当前行于下一行合并为一行

xp

调换两个字符的位置

修改文本

命令

功能

rch

把光标位置的字符替换为ch

R

替换光标右边的文本

s

把光标所在的字符替换为任意个字符

S

替换整行内容

cw

修改一个单词

c

修改区域里的文本(vim)

~

改变所扫描的或区域里的字母的大小写

!tr'[a-z]''[A-Z]'

把区域里的字母转换为大写(vim)

!tr'[A-Z]''[a-z]'

把区域里的字母转换为小写(vim)

复制文本

命令

功能

yy

复制当前行

6yy

复制六行

yw

复制单词

y

复制区域(vim)

p

把复制的文本粘贴到右边或下面

P

把复制的文本粘贴到左边或上面

 

还有一个博客,总结的比较详细,推荐一下

http://www.cnblogs.com/tzhangofseu/archive/2011/12/17/2290955.html

更多0

·  上一篇:Objective-C copy, tetain, assign ,readonly , readwrite, nonatomic区别

 

 

如何画程序流程图

2009-10-22 13:04 41469人阅读 评论(12) 收藏 举报

chartsresourcessystemnetwork图形算法

PST代码很多天了,里面的读写流程看懂了,又忘了,再看,再忘,归咎于笔记的不完善与可读性差,今天开始学习流程图.

下面介绍一下标准程序流程图的符号及使用约定

,引言

程序流程图(Progran flowchart)作为一种算法表达工具,早已为工国计算机工作者和广大计算机用户十分熟悉和普通使用.然而它的一个明显缺点在于缺乏统一的规范化符号表示和严格的使用规则.最近,国家标准局批准的国家标准(GB1525-89)<<信息处理--数据流程图,程序流程图,系统流程图,程序网络图和系统资源图的文件编制符号及约定>>为我们推荐了一套标准化符号和使用约定.由于该标准是与国际标准化组织公布的标准ISO5807--85Information processing--Documentation symbols and comventions for data,programand system flowcharts,program network charts and system resources charts是一致的,这里将其中程序流程图部分摘录出来,并做了一些解释,供读者参考.

根据这一标准画出的程序流程图我们称为标准流程图.

,符号

程序流程图表示了程序的操作顺序.它应包括:

(1)指明实际处理操作的处理符号,包括根据逻辑条件确定要执行的路径的符号.

(2)指明控制流的流线符号.

(3)便于读写程序流程图的特殊符号.


以下给出标准流程图所用的符号及其简要说明,请参看图1.

1 标准程序流程图符号

1.数据---- 平行四边形表示数据,其中可注明数据名,来源,用途或其它的文字说明.此符号并不限定数据的媒体.

2.处理---- 矩形表示各种处理功能.例如,执行一个或一组特定的操作,从而使信息的值,信息形世或所在位置发生变化,或是确定对某一流向的选择.矩形内可注明处理名或其简工功能.

3.特定处理---- 带有双纵边线的矩形表示已命名的特定处理.该处理为在另外地方已得到详细说明的一个操作或一组操作,便如子例行程序,模块.矩形内可注明特定处理名或其简要功能.

4.准备---- 六边形符号表示准备.它表示修改一条指令或一组指令以影响随后的活动.例如,设置开关,修改变址寄存器,初始化例行程序.

5.判断----- 菱形表示判断或开关.菱形内可注明判断的条件.它只有一个入口,但可以有若干个可供选择的出口,在对符号内定义折条件求值后,有一个且仅有一个出口被激活.求值结果可在表示出口路径的流线附近写出.


6.
循环界限---- 循环界限为去上角矩形表示年界限和去下角矩形的下界限构成,分别表示循环的开始和循环的结束. 2 两种循环表示

一对符号内应注明同一循环标识符.可根据检验终止循环条件在循环的开始还是在循环的末尾,将其条件分别在上界限符内注明(:A>B)或在下界限符内注明(:直到C<D).2给出了当终止条件成立时进入循环和直到终止条件成立退出循环的两种不同的表示.

7.连接符---- 圆表示连接符,用以表明转向流程图的它处,或从流程图它处转入.它是流线的断点.在图内注明某一标识符,表明该流线将在具有相同标识符的另一连接符处继续下去(参看以下关于连接符使用的约定).

8.端点符---- 扁圆形表示转向外部环境或从外部环境转入的端点符.例如,程序流程的起始或结束,数据的外部使用起点或终点.


9.
注解符---- 注解符由纵边线和虚线构成,用以标识注解的内容.虚线须连接到被注解的符号或符号组合上.注解的正文应靠近纵边线.请参看图3给出的注解符使用示例.

3 注解符的使用

10.流线 ----- 直线表示控制流的流线.关于流线上表示流向的箭头,其使用方法见后面的约定.

11.虚线 ---- 虚线用于表明被注解的范围或连接被注解部分与注解正文,也参看图3.


12.
省略符 ---- 若流程图中有些部分无需给出符号的具体形式和数量,可用三点构成的省略符.省略符应夹在流线符号之中或流线符号之间,参看图4.

4 省略符的使用


13.
并行方式 ---- 一对平行线表示同步进行两个或两个以上并行方式的操作.并行方式的示例如图5,图中在处理A完成后才能进行处理C,DE;同样,处理F要等处理B,C,D完成以后进行.但处理C可以处理D开始和()结束前开始和()结束.

5 并行方式示例

,使用约定

关于流程图符号的使用约定,简要地说明以下几点:

1.图的布局

流程图中所用的符号应该均心地分布,连线保持合理的长度,并尽量少使用长线.

2.符号的形状

流程图中多数符号内的空白供标注说明性文字.使用各种符号应注意符号的外形和各符号大小的统一,避免使符号变形或各符号大小比例不一.

3.符号内的说明文字

应使符号内的说明文字尽可能简明.通常按从左向右和从上向下方式书写,并与流向无关.如果说明文字较多,符号内写不完,可使用注解符.若注解符干扰或影响到图形的流程,应将正文写在另外一页上,并注明引用符号.

4.符号标识符


为符号规定标识符是为了便于其它文件引用该符号.便如,程序清单中引用到流程图中的特定符号.符号标识符一般写在符号的左上角,参看图6(a).

6 符号标识符和符号描述符

5.符号描述符

为便于进一步理解符号的功能,可标注符号描述符.通常描述符写在符号的右上角,如图6(b)所示.

6.详细表示


在处理符号或数据符号中画一横线,表明该符号在同一文件集中的其它地言有更为详细的表示.横线在符号内靠近项端,详细表示的标识符写在符号内横线之上,见图7(a).端点符用作详细表示的开始符号和结束符号,在此符号中应给出加横线符号中的标识符,见图7(b).

7 加横线符号及其详细表示

7.流线

(1)标准流向与箭头的使用

流线的标准流向是从左到右和从上到下.沿标准流向的流线可不用箭头指示流向,但沿非标准流向的流线应用箭头指示充向.

(2)流线的交叉

应当尽量避免流线的交叉.即使出现流线的交叉,交叉的流线之间也没有任何逻辑关系,并不对流向产生任何影响,如图8(a)所示.

(3)流线的汇集


两条或多条进入线可以汇集成一条输出线,此时各连接点应要互错工以提高清晰度,并用箭头表示流向,如图8(b)所示.

8 汉线的交叉与流线的汇集

(4)符号流线进出

一般情况下,流线应从符号的左边或项端进入,并从右边或底端离开.其进出点均应对准符号的中心.

(5)连接符

为避免出现流线交叉和使用长线,或某个流程图能在另一页上延续,可用连接符将流线截断.截断始端的连接符称为出口连接符,载断末端的连接符称为入口连接符.两连接符中用同一标识符.


换页截断可用与连接符相连的注解符表示,如图9所示.

9 出口连接符与入口连接符

8.多出口判断的两种表示方法

(1)直接从判断符号引出多条流线,如图10(a)所示.


(2)
从判断符号引聘条流线,再从它引出多条流线,如图10(b)所示.

10 多出口判断


多出口判断的每个出口都应标有相应的条件值,用以反映它所引出的逻辑路径,如图11所示.

11 多出口判断出口处标出条件值


, 示例

10个比viso好的流程图制作软件

1StarUML

  一款知名的免费开源的UML建模工具。

  

2ArgoUML

  另一个著名的开源UML建模软件,ArgoUML支持Windows、Mac、Linux等主流操作系统。

  

3OpenOffice Draw

  OpenOffice是一个可代替微软Office办公套件的免费产品,虽然没有微软Office那么庞大复杂,但可满足一般需求。其中的Draw就是用来设计diagrams的。

  

4Dia Diagram Editor

  Dia跟Visio高度相似,可制作流程图、建筑设计图、UML模型图等等,Dia同样免费开源,支持Windows、Linux和Mac OS X等OS。

  

5Pencil Project

  Pencil Project是一个用于创建流程图和界面原型的开源设计工具。

  

6yEd Graph Editor

  yEd图像编辑器是一个用于快速高效制作高质量流程图的桌面软件,可运行于Windows、Unix/Linux和Mac OS X平台。它支持从.xls和XML格式导入数据,可导出为多种位图或矢量图片格式:PNG, JPG, SVG, PDF, SWF。

  

7OmniGraffle

  OmniGraffle支持Mac电脑和iPad平板。

  

8CorelDRAW

  流行的矢量图形绘制和排版软件,同样可制作流程图,价格:499美元。

  

9Inkscape

  开源的矢量图形编辑器,提供类似于Illustrator、CorelDraw和Xara X的功能。

  

10cacoo

  一个在线流程图制作软件,提供免费、Plus和Team三个Plan,免费用户可保存最多25张图表

 Linuxpthread_mutex_init()函数

2011-09-19 10:32:15

标签:pthread_mutex_init() Linux 休闲 职场

    函数原型:Int pthread_mutex_init(pthread_mutex_t  *restrict_mutex,constpthread_mutextattr_t *restrict attr)

    该函数主要用于多线程中互斥锁的初始化。

   如果attr为空的话,则是默认属性,而默认属性的快速互斥锁。

   pthread_mutex_init完成成功后会返回0,其他值都是错误的。

   intpthread_mutextattr_destroy(pthread_mutextattr_t *restrict_mutext)

   该函数是销毁线程互斥锁

   设定互斥锁的作用域:

    Intpthread_mutextattr_setpshared(pthread_mutexattr_t *restrict mutext, intpshared)

    在多线程中是共享互斥锁的。

    如果想在多个进程中共享互斥锁,可以把pshared设置PTHREAD_PROCESS_SHARED

    如果只想在有同属于一个进程创建的线程中共享互斥锁,则可以把pshared设置为PTHREAD_PROCESS_PRIVATE

    获得互斥锁的作用域:

     intpthread_mutexattr_getpshared(pthread_mutexattr_t *restrict mutext,int*pshared);

     设定互斥锁类型的属性:

     intpthread_mutexattr_settype(pthread_mutexattr_t *restrict mutext,int type)

     其中type类型都有:

    PTHREAD_MUTEX_NOMRAL:此类型的互斥锁不会检测死锁

    而其中的缺省值值是PTHREAD_MUTEX_DEFAULT

    PTHREAD_MUTEX_ERRORCHECK:是提供错误检查

     intpthread_mutexattr_setprotocal(pthread_mutexattr_t *attr,int protocal)

     protocal可以设置互斥锁属性的协议

    PTHREAD_PRIO_NONE

    PTHREAD_PRIO_INHERIT

    PTHREAD_PRIO_PROTECT

 

条件变量、pthread_cond_init

2009-11-25 16:22 8687人阅读 评论(5) 收藏 举报

signal测试null

目录(?)[+]

 

1.初始化条件变量pthread_cond_init

#include <pthread.h>
int pthread_cond_init(pthread_cond_t *cv,
const pthread_condattr_t *cattr);
返回值:函数成功返回0;任何其他返回值都表示错误

初始化一个条件变量。当参数cattr为空指针时,函数创建的是一个缺省的条件变量。否则条件变量的属性将由cattr中的属性值来决定。调用 pthread_cond_init函数时,参数cattr为空指针等价于cattr中的属性为缺省属性,只是前者不需要cattr所占用的内存开销。这个函数返回时,条件变量被存放在参数cv指向的内存中。

可以用宏PTHREAD_COND_INITIALIZER来初始化静态定义的条件变量,使其具有缺省属性。这和用pthread_cond_init函数动态分配的效果是一样的。初始化时不进行错误检查。如:

pthread_cond_t cv = PTHREAD_COND_INITIALIZER;

不能由多个线程同时初始化一个条件变量。当需要重新初始化或释放一个条件变量时,应用程序必须保证这个条件变量未被使用。

 

2.阻塞在条件变量上pthread_cond_wait

#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *cv,
pthread_mutex_t *mutex);
返回值:函数成功返回0;任何其他返回值都表示错误

函数将解锁mutex参数指向的互斥锁,并使当前线程阻塞在cv参数指向的条件变量上。

被阻塞的线程可以被pthread_cond_signal函数,pthread_cond_broadcast函数唤醒,也可能在被信号中断后被唤醒。

pthread_cond_wait函数的返回并不意味着条件的值一定发生了变化,必须重新检查条件的值。

pthread_cond_wait函数返回时,相应的互斥锁将被当前线程锁定,即使是函数出错返回。

一般一个条件表达式都是在一个互斥锁的保护下被检查。当条件表达式未被满足时,线程将仍然阻塞在这个条件变量上。当另一个线程改变了条件的值并向条件变量发出信号时,等待在这个条件变量上的一个线程或所有线程被唤醒,接着都试图再次占有相应的互斥锁。

阻塞在条件变量上的线程被唤醒以后,直到pthread_cond_wait()函数返回之前条件的值都有可能发生变化。所以函数返回以后,在锁定相应的互斥锁之前,必须重新测试条件值。最好的测试方法是循环调用pthread_cond_wait函数,并把满足条件的表达式置为循环的终止条件。如:

pthread_mutex_lock();
while (condition_is_false)
 pthread_cond_wait();
pthread_mutex_unlock();

阻塞在同一个条件变量上的不同线程被释放的次序是不一定的。

注意:pthread_cond_wait()函数是退出点,如果在调用这个函数时,已有一个挂起的退出请求,且线程允许退出,这个线程将被终止并开始执行善后处理函数,而这时和条件变量相关的互斥锁仍将处在锁定状态。

 

3.解除在条件变量上的阻塞pthread_cond_signal

#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cv);
返回值:函数成功返回0;任何其他返回值都表示错误

函数被用来释放被阻塞在指定条件变量上的一个线程。

必须在互斥锁的保护下使用相应的条件变量。否则对条件变量的解锁有可能发生在锁定条件变量之前,从而造成死锁。

唤醒阻塞在条件变量上的所有线程的顺序由调度策略决定,如果线程的调度策略是SCHED_OTHER类型的,系统将根据线程的优先级唤醒线程。

如果没有线程被阻塞在条件变量上,那么调用pthread_cond_signal()将没有作用。

 

4.阻塞直到指定时间pthread_cond_timedwait

#include <pthread.h>
#include <time.h>
int pthread_cond_timedwait(pthread_cond_t *cv,
pthread_mutex_t *mp, const structtimespec * abstime);
返回值:函数成功返回0;任何其他返回值都表示错误

函数到了一定的时间,即使条件未发生也会解除阻塞。这个时间由参数abstime指定。函数返回时,相应的互斥锁往往是锁定的,即使是函数出错返回。

注意:pthread_cond_timedwait函数也是退出点。

超时时间参数是指一天中的某个时刻。使用举例:

pthread_timestruc_t to;
to.tv_sec = time(NULL) + TIMEOUT;
to.tv_nsec = 0;

超时返回的错误码是ETIMEDOUT。

 

5.释放阻塞的所有线程pthread_cond_broadcast

#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cv);
返回值:函数成功返回0;任何其他返回值都表示错误

函数唤醒所有被pthread_cond_wait函数阻塞在某个条件变量上的线程,参数cv被用来指定这个条件变量。当没有线程阻塞在这个条件变量上时,pthread_cond_broadcast函数无效。

由于pthread_cond_broadcast函数唤醒所有阻塞在某个条件变量上的线程,这些线程被唤醒后将再次竞争相应的互斥锁,所以必须小心使用pthread_cond_broadcast函数。

 

6.释放条件变量pthread_cond_destroy

#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cv);
返回值:函数成功返回0;任何其他返回值都表示错误

释放条件变量。

注意:条件变量占用的空间并未被释放。

 

7.唤醒丢失问题

在线程未获得相应的互斥锁时调用pthread_cond_signal或pthread_cond_broadcast函数可能会引起唤醒丢失问题。

唤醒丢失往往会在下面的情况下发生:

一个线程调用pthread_cond_signal或pthread_cond_broadcast函数;

另一个线程正处在测试条件变量和调用pthread_cond_wait函数之间;

没有线程正在处在阻塞等待的状态下

#ifdef __cplusplus 有什么作用

一般用于将C++代码以标准C形式输出(即以C的形式被调用),这是因为C++虽然常被认为是C的超集,但是C++的编译器还是与C的编译器不同的。C中调用C++中的代码这样定义会是安全的。

 

一般的考虑跨平台使用方法如下:

 

#ifdefined(__cplusplus)||defined(c_plusplus)//跨平台定义方法

extern "C"{

#endif

//... 正常的声明段

#ifdefined(__cplusplus)||defined(c_plusplus)

}

#endif

 

简单的用在windows下可以如下定义:

#ifdef   __cplusplus

extern "C"{

//... 正常的声明段

}

#endif

 

 

某一网文:

 

#ifdef__cplusplus 是什么意思?

时常在cpp的代码之中看到这样的代码:

#ifdef__cplusplus

extern"C"{

#endif

//一段代码

#ifdef__cplusplus

}

#endif

这样的代码到底是什么意思呢?

首先,__cplusplus是cpp中的自定义宏,那么定义了这个宏的话表示这是一段cpp的代码,也就是说,上面的代码的含义是:

    如果这是一段cpp的代码,那么加入extern"C"{和}处理其中的代码。

 

    要明白为何使用extern"C",还得从cpp中对函数的重载处理开始说起。在c++中,为了支持重载机制,在编译生成的汇编码中,要对函数的名字进行一些处理,加入比如函数的返回类型等等.而在C中,只是简单的函数名字而已,不会加入其他的信息.也就是说:C++和C对产生的函数名字的处理是不一样的.

比如下面的一段简单的函数,我们看看加入和不加入extern"C"产生的汇编代码都有哪

些变化:

 

intf(void)

{

return1;

}

在加入extern"C"的时候产生的汇编代码是:

.file"test.cxx"

.text

.align2

.globl_f

.def_f;.scl2;.type32;.endef

_f:

pushl%ebp

movl%esp,%ebp

movl$1,%eax

popl%ebp

ret

但是不加入了extern"C"之后

.file"test.cxx"

.text

.align2

.globl__Z1fv

.def__Z1fv;.scl2;.type 32;.endef

__Z1fv:

pushl%ebp

movl%esp,%ebp

movl$1,%eax

popl%ebp

ret

两段汇编代码同样都是使用gcc-S命令产生的,所有的地方都是一样的,唯独是产生的

函数名,一个是_f,一个是__Z1fv。

明白了加入与不加入extern"C"之后对函数名称产生的影响,我们继续我们的讨论:为什

么需要使用extern"C"呢?C++之父在设计C++之时,考虑到当时已经存在了大量的C代码,

为了支持原来的C代码和已经写好C库,需要在C++中尽可能的支持C,而extern"C"就是

其中的一个策略。

试想这样的情况:一个库文件已经用C写好了而且运行得很良好,这个时候我们需要使

用这个库文件,但是我们需要使用C++来写这个新的代码。如果这个代码使用的是C++的

方式链接这个C库文件的话,那么就会出现链接错误.我们来看一段代码:首先,我们使用C

的处理方式来写一个函数,也就是说假设这个函数当时是用C写成的:

//f1.c

extern"C"

{

voidf1()

{

return;

}

}

编译命令是:gcc-cf1.c-of1.o产生了一个叫f1.o的库文件。再写一段代码调用这个f1

函数:

//test.cxx

//这个extern表示f1函数在别的地方定义,这样可以通过

//编译,但是链接的时候还是需要

//链接上原来的库文件.

externvoidf1();

intmain()

{

f1();

return0;

}

通过gcc-ctest.cxx-otest.o产生一个叫test.o的文件。然后,我们使用gcctest.of1.o来

链接两个文件,可是出错了,错误的提示是:

test.o(.text+0x1f):test.cxx:undefinereferenceto'f1()'

也就是说,在编译test.cxx的时候编译器是使用C++的方式来处理f1()函数的,但是实

际上链接的库文件却是用C的方式来处理函数的,所以就会出现链接过不去的错误:因为链

接器找不到函数。

因此,为了在C++代码中调用用C写成的库文件,就需要用extern"C"来告诉编译器:

这是一个用C写成的库文件,请用C的方式来链接它们。

比如,现在我们有了一个C库文件,它的头文件是f.h,产生的lib文件是f.lib,那么我

们如果要在C++中使用这个库文件,我们需要这样写:

extern"C"

{

#include"f.h"

}

回到上面的问题,如果要改正链接错误,我们需要这样子改写test.cxx:

extern"C"

{

externvoidf1();

}

intmain()

{

f1();

return0;

}

重新编译并且链接就可以过去了.

总结

C和C++对函数的处理方式是不同的.extern"C"是使C++能够调用C写作的库文件的一

个手段,如果要对编译器提示使用C的方式来处理函数的话,那么就要使用extern"C"来说

明。 

另一篇文章:

 

#ifdef __cplusplus

#ifdef __cplusplus 倒底是什么意思?

时常在cpp的代码之中看到这样的代码:

#ifdef __cplusplus

extern "C" {

#endif

//一段代码

#ifdef __cplusplus

}

#endif

  这样的代码到底是什么意思呢?首先,__cplusplus是cpp中的自定义宏,那么定义了这个宏的话表示这是一段cpp的代码,也就是说,上面的代码的含义是:如果这是一段cpp的代码,那么加入extern "C"{和}处理其中的代码。

  要明白为何使用extern"C",还得从cpp中对函数的重载处理开始说起。在c++中,为了支持重载机制,在编译生成的汇编码中,要对函数的名字进行一些处理,加入比如函数的返回类型等等.而在C中,只是简单的函数名字而已,不会加入其他的信息.也就是说:C++和C对产生的函数名字的处理是不一样的.

  比如下面的一段简单的函数,我们看看加入和不加入extern "C"产生的汇编代码都有哪些变化:

int f(void)

{

return 1;

}

  在加入extern"C"的时候产生的汇编代码是:

.file "test.cxx"

.text

.align 2

.globl _f

.def _f; .scl 2; .type 32; .endef

_f:

pushl %ebp

movl %esp, %ebp

movl $1, %eax

popl %ebp

ret

  但是不加入了extern"C"之后

.file "test.cxx"

.text

.align 2

.globl __Z1fv

.def __Z1fv; .scl 2; .type 32; .endef

__Z1fv:

pushl %ebp

movl %esp, %ebp

movl $1, %eax

popl %ebp

ret

  两段汇编代码同样都是使用gcc-S命令产生的,所有的地方都是一样的,唯独是产生的函数名,一个是_f,一个是__Z1fv。

  明白了加入与不加入extern"C"之后对函数名称产生的影响,我们继续我们的讨论:为什么需要使用extern"C"呢?C++之父在设计C++之时,考虑到当时已经存在了大量的C代码,为了支持原来的C代码和已经写好C库,需要在C++中尽可能的支持C,而extern "C"就是其中的一个策略。

  试想这样的情况:一个库文件已经用C写好了而且运行得很良好,这个时候我们需要使用这个库文件,但是我们需要使用C++来写这个新的代码。如果这个代码使用的是C++的方式链接这个C库文件的话,那么就会出现链接错误.我们来看一段代码:首先,我们使用C的处理方式来写一个函数,也就是说假设这个函数当时是用C写成的:

//f1.c

extern "C"

{

void f1()

{

return;

}

}

  编译命令是:gcc -cf1.c -o f1.o 产生了一个叫f1.o的库文件。再写一段代码调用这个f1函数:

// test.cxx

//这个extern表示f1函数在别的地方定义,这样可以通过

//编译,但是链接的时候还是需要

//链接上原来的库文件.

extern void f1();

int main()

{

f1();

return 0;

}

  通过gcc -ctest.cxx -o test.o 产生一个叫test.o的文件。然后,我们使用gcctest.o f1.o来链接两个文件,可是出错了,错误的提示是:

test.o(.text + 0x1f):test.cxx: undefinereference to 'f1()'

  也就是说,在编译test.cxx的时候编译器是使用C++的方式来处理f1()函数的,但是实际上链接的库文件却是用C的方式来处理函数的,所以就会出现链接过不去的错误:因为链接器找不到函数。

  因此,为了在C++代码中调用用C写成的库文件,就需要用extern "C"来告诉编译器:这是一个用C写成的库文件,请用C的方式来链接它们。

  比如,现在我们有了一个C库文件,它的头文件是f.h,产生的lib文件是f.lib,那么我们如果要在C++中使用这个库文件,我们需要这样写:

extern "C"

{

#include "f.h"

}

  回到上面的问题,如果要改正链接错误,我们需要这样子改写test.cxx:

extern "C"

{

extern void f1();

}

int main()

{

f1();

return 0;

}

  重新编译并且链接就可以过去了.

  总结

C和C++对函数的处理方式是不同的.extern"C"是使C++能够调用C写作的库文件的一个手段,如果要对编译器提示使用C的方式来处理函数的话,那么就要使用extern "C"来说明。

1.引言

C++语言的创建初衷是“a better C”,但是这并不意味着C++中类似C语言的全局变量和函数所采用的编译和连接方式与C语言完全相同。作为一种欲与C兼容的语言,C++保留了一部分过程式语言的特点(被世人称为“不彻底地面向对象”),因而它可以定义不属于任何类的全局变量和函数。但是,C++毕竟是一种面向对象的程序设计语言,为了支持函数的重载,C++对全局函数的处理方式与C有明显的不同。

2.从标准头文件说起

某企业曾经给出如下的一道面试题:

面试题

为什么标准头文件都有类似以下的结构?

#ifndef __INCvxWorksh

#define __INCvxWorksh

#ifdef __cplusplus

extern "C" {

#endif

#ifdef __cplusplus

}

#endif

#endif

分析

显然,头文件中的编译宏“#ifndef__INCvxWorksh、#define__INCvxWorksh、#endif” 的作用是防止该头文件被重复引用。

那么

#ifdef __cplusplus

extern "C" {

#endif

#ifdef __cplusplus

}

#endif

的作用又是什么呢?我们将在下文一一道来。

3.深层揭密extern "C"

extern "C" 包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。让我们来详细解读这两重含义。

(1) 被extern "C"限定的函数或变量是extern类型的;

extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。记住,下列语句:

extern int a;

仅仅是一个变量的声明,其并不是在定义变量a,并未为a分配内存空间。变量a在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。

通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在连接阶段中从模块 A编译生成的目标代码中找到此函数。

与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,其不可能被extern “C”修饰。

(2) 被extern "C"修饰的变量和函数是按照C语言方式编译和连接的;

未加extern “C”声明时的编译方式

首先看看C++中对类似C的函数是怎样编译的。

作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为:

void foo( int x, int y );

该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangled name”)。_foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。例如,在C++中,函数void foo(int x, int y )与void foo( intx, float y )编译生成的符号是不相同的,后者为_foo_int_float。

同样地,C++中的变量除支持局部变量外,还支持类成员变量和全局变量。用户所编写程序的类成员变量可能与全局变量同名,我们以"."来区分。而本质上,编译器在进行编译时,与函数的处理相似,也为类中的变量取了一个独一无二的名字,这个名字与用户程序中同名的全局变量名字不同。

未加extern"C"声明时的连接方式

假设在C++中,模块A的头文件如下:

// 模块A头文件 moduleA.h

#ifndef MODULE_A_H

#define MODULE_A_H

int foo( int x, int y );

#endif

在模块B中引用该函数:

// 模块B实现文件 moduleB.cpp

#i nclude"moduleA.h"

foo(2,3);

实际上,在连接阶段,连接器会从模块A生成的目标文件moduleA.obj中寻找_foo_int_int这样的符号!

加extern"C"声明后的编译和连接方式

加extern"C"声明后,模块A的头文件变为:

// 模块A头文件 moduleA.h

#ifndef MODULE_A_H

#define MODULE_A_H

extern "C" int foo( int x, int y);

#endif

在模块B的实现文件中仍然调用foo( 2,3 ),其结果是:

(1)模块A编译生成foo的目标代码时,没有对其名字进行特殊处理,采用了C语言的方式;

(2)连接器在为模块B的目标代码寻找foo(2,3)调用时,寻找的是未经修改的符号名_foo。

如果在模块A中函数声明了foo为extern "C"类型,而模块B中包含的是extern int foo( int x, int y ) ,则模块B找不到模块A中的函数;反之亦然。

所以,可以用一句话概括extern“C”这个声明的真实目的(任何语言中的任何语法特性的诞生都不是随意而为的,来源于真实世界的需求驱动。我们在思考问题时,不能只停留在这个语言是怎么做的,还要问一问它为什么要这么做,动机是什么,这样我们可以更深入地理解许多问题):

实现C++与C及其它语言的混合编程。

明白了C++中extern "C"的设立动机,我们下面来具体分析extern "C"通常的使用技巧。

4.extern "C"的惯用法

(1)在C++中引用C语言中的函数和变量,在包含C语言头文件(假设为cExample.h)时,需进行下列处理:

extern "C"

{

#i nclude"cExample.h"

}

而在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern "C"声明,在.c文件中包含了extern"C"时会出现编译语法错误。

笔者编写的C++引用C函数例子工程中包含的三个文件的源代码如下:

#ifndef C_EXAMPLE_H

#define C_EXAMPLE_H

extern int add(int x,int y);

#endif

#i nclude"cExample.h"

int add( int x, int y )

{

return x + y;

}

// c++实现文件,调用add:cppFile.cpp

extern "C"

{

#i nclude"cExample.h"

}

int main(int argc, char* argv[])

{

add(2,3);

return 0;

}

如果C++调用一个C语言编写的.DLL时,当包括.DLL的头文件或声明接口函数时,应加extern "C"{ }。

(2)在C中引用C++语言中的函数和变量时,C++的头文件需添加extern "C",但是在C语言中不能直接引用声明了extern "C"的该头文件,应该仅将C文件中将C++中定义的extern "C"函数声明为extern类型。

笔者编写的C引用C++函数例子工程中包含的三个文件的源代码如下:

//C++头文件 cppExample.h

#ifndef CPP_EXAMPLE_H

#define CPP_EXAMPLE_H

extern "C" int add( int x, int y);

#endif

//C++实现文件 cppExample.cpp

#i nclude"cppExample.h"

int add( int x, int y )

{

return x + y;

}

extern int add( int x, int y );

int main( int argc, char* argv[] )

{

add( 2, 3 );

return 0;

}

Linux系统中的poll函数

20120717  Linux命令  暂无评论

poll()函数:这个函数是某些Unix系统提供的用于执行与select()函数同等功能的函数。

函数声明:

    #include <poll.h>

    int poll(struct pollfd *fds, nfds_t nfds, int timeout);

参数说明:

    fds 是一个struct pollfd结构类型的数组,用来存放需要检测其状态的socket描述符或者文件描述符。每次调用这个函数之后,系统不会清空这个数组,因此操作起来比较方便。这一点和select()函数不同,调用select()函数之后,select()函数会清空它所检测的socket描述符集合,导致每次调用select()之前都必须把socket重新加入集合中。所以,select()函数适合检测一个socket描述符的清空,而poll()函数适合于大量socket描述符的情况。

    nfds 是 nfds_t类型的参数,用来标记数组fds中的结构体元素的总数量。

    timeout 是poll函数调用阻塞的时间,单位是毫秒。

返回值:

    >0:数组fds中准备好读、写或者出错状态的描述符的总数量。

    ==0:没有任何文件描述符准备好读、写或者出错。这个时间是由第三个参数来指定的。(0,立刻返回/+n,n毫秒/-1,一直等待)

    <0:poll函数调用失败,同时会自动设置全局变量errno;

需要注意的是,如果待检测的文件描述符为负值,则对这个描述符的检测就会被忽略。

    所以,poll() 函数的功能和返回值的含义与 select() 函数的功能和返回值的含义是完全一样的,两者之间的差别就是内部实现方式不一样,select()函数基本上可以在所有支持文件描述符操作的系统平台上运行(如:Linux 、Unix 、Windows、MacOS等),可移植性好,而poll()函数则只有个别的的操作系统提供支持(如:SunOS、Solaris、AIX、HP提供支持,但是Linux不提供支持),可移植性差。

struct pollfd结构

typedef struct pollfd{

    int fd;

    short    events;    //感兴趣的事件

    short    revents;    //实际发生的事件

}    pollfd_t;

typedef    unsigned    longnfds_t;

经常检测的事件标记:POLLIN/POLLRDNORM(可读)、POLLOUT/POLLWRNORM(可写)、POLLERR(出错)或者是他们的与。如 POLLIN|POLLOUT|POLLERR。

检测可读:if(fds[index].revents& POLLIN) == POLLIN)    //接收数据

检测可写:if((fds[index].revents& POLLOUT) == POLLOUT)    //发送数据

检测出错:if((fds[index].revents& POLLERR) == pOLLERR)    //异常处理 

 

应用实例:

 

 

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/socket.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <netdb.h>

#include <string.h>

#include <errno.h>

#include <poll.h>

 

#define  LISTENQ  1024

#define  MAXLINE  1024

#define  OPEN_MAX  50000

#define  SERVER_PORT  3333

 

int  main(int argc,char **argv)

{

  int  i,max,listenfd,connfd,sockfd;

  int  nready;

  size_t  n;

  socklen_t  clilen;

  struct  sockaddr_in  servaddr,cliaddr;

  struct  hostent  *hp;

  char  buf[1024];

  struct  pollfd  client[OPEN_MAX];

 

  if(argc != 2){

    printf("usage:%s <hostname>\n",argv[0]);

    exit(1);

  }

 

  if((listenfd = socket(AF_INET,SOCK_STREAM,0)) < 0){

    printf("socket error!\n");

    exit(1);

  }

 

  bzero(&servaddr,sizeof(servaddr));

  servaddr.sin_family = AF_INET;

 

  if(!(hp = gethostbyname(argv[1]))){

    printf("gethostbyname error!\n");

    exit(1);

  }

  bcopy(hp->h_addr,(struct sockaddr*)&servaddr.sin_addr,hp->h_length);

 

  servaddr.sin_port = htons(SERVER_PORT);

 

  if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) < 0){

    printf("bind error!\n");

    exit(1);

  }

 

  listen(listenfd,LISTENQ);

 

  client[0].fd = listenfd;

  client[0].events = POLLIN;

 

  for(i=1;i<OPEN_MAX;i++){

    client[i].fd = -1;

  }

  max = 0;

 

  while(1){

    nready = poll(client,max+1,-1);

 

    if(client[0].revents & POLLIN){

      connfd = accept(listenfd,(struct sockaddr*)&servaddr,&clilen);

      for(i=1;i<OPEN_MAX;++i){

        if(client[i].fd < 0){

          client[i].fd = connfd;

          client[i].events = POLLIN;

          break;

        }

        if(i == OPEN_MAX){

          printf("too many clients\n");

          exit(1);

        }

        if(i>max)

          max = i;

        if(--nready <= 0)

          continue;

      }

 

      for(i=i;i<max;i++){

        if((sockfd = client[i].fd) < 0)

          continue;

        if(client[i].revents & (POLLIN|POLLERR)){

          if((n = read(sockfd,buf,MAXLINE)) < 0){

            if(errno == ECONNRESET){

              close(sockfd);

              client[i].fd = -1;

            }

            else

              printf("read error\n");

          }

          else if(n==0){

            close(sockfd);

            client[i].fd = -1;

          }

          else

            write(sockfd,buf,n);

 

          if(--nready <= 0)

            break;

        }

      }

    }

  }

}

ubuntu下配置vim

1、安装vim
       sudo apt-get install vim-full
2
、配置文件的位置
在目录/etc/vim下面,有个名为vimrc的文件,这是系统中公共的vim配置文件,对所有用户都有效。
       3
、设置语法高亮显示
1)
打开vimrc,添加以下语句来使得语法高亮显示:
syntax on
2)
如果此时语法还是没有高亮显示,那么在/etc目录下的profile文件中添加以下语句:
export TERM=xterm-color
     
 
       4
、设置Windows风格的C/C++自动缩进(添加以下set语句到vimrc中)
             1
)设置(软)制表符宽度为4
                           set tabstop=4
                           set softtabstop=4
             2
)设置缩进的空格数为4
                         set shiftwidth=4
             3
)设置自动缩进:即每行的缩进值与上一行相等;使用noautoindent 取消设置:
set autoindent
             4
)设置使用 C/C++ 语言的自动缩进方式:
                         set cindent
             5
)设置C/C++语言的具体缩进方式:
                           set cinoptions={0,1s,t0,n-2,p2s,(03s,=.5s,>1s,=1s,:1s
             6
)如果想在左侧显示文本的行号,可以用以下语句:
                         set nu
             7
)最后,如果没有下列语句,就加上吧:
if &term=="xterm"
set t_Co=8
             sett_Sb=^[[4%dm
set t_Sf=^[[3%dm
endif

VIM查找替换归纳总结zz

http://spaces.msn.com/dingy/blog/cns!2F24B9E66A542581!327.entry VIM中常用的替换模式总结。

1,简单替换表达式

替换命令可以在全文中用一个单词替换另一个单词:

:%s/four/4/g

 

”%” 范围前缀表示在所有行中执行替换。最后的 “g” 标记表示替换行中的所有匹配点。如果仅仅对当前行进行操作,那么只要去掉%即可

  如果你有一个象 “thirtyfour” 这样的单词,上面的命令会出错。这种情况下,这个单词会被替换成”thirty4”。要解决这个问题,用“\<” 来指定匹配单词开头:

      :%s/\<four/4/g

显然,这样在处理 “fourty” 的时候还是会出错。用“\>” 来解决这个问题:

      :%s/\<four\>/4/g

如果你在编码,你可能只想替换注释中的 “four”,而保留代码中的。由于这很难指定,可以在替换命令中加一个 “c” 标记,这样,Vim 会在每次替换前提示你:

      :%s/\<four\>/4/gc

2,删除多余的空格

要删除这些每行后面多余的空格,可以执行如下命令:

      :%s/\s\+$//

命令前面指明范围是 “%”,所以这会作用于整个文件。”substitute”命令的匹配模式是

“\s\+$”。这表示行末($)前的一个或者多个(\+)空格(\s)。替换命令的 “to” 部分是空的:”//”。这样就会删除那些匹配的空白字符。

3,匹配重复性模式

星号项 “*” 规定在它前面的项可以重复任意次。因此:

      /a*

匹配 “a”,”aa”,”aaa”,等等。但也匹配 “” (空字串),因为零次也包含在内。星号 “*” 仅仅应用于那个紧邻在它前面的项。因此 “ab*” 匹配 “a”,”ab”,”abb”,”abbb”,等等。如要多次重复整个字符串,那么该字符串必须被组成一个项。组成一项的方法就是在它前面加 “\(“,后面加 “\)”。因此这个命令:

      /\(ab\)*

匹配: “ab”,”abab”,”ababab”,等等。而且也匹配“”。

要避免匹配空字串,使用 “\+”。这表示前面一项可以被匹配一次或多次。

      /ab\+

匹配 “ab”,”abb”,”abbb”,等等。它不匹配 后面没有跟随 “b” 的 “a”。

要匹配一个可选项,用 “\=”。 例如:

      /folders\=

匹配 “folder” 和 “folders”。

4,指定重复次数

要匹配某一项的特定次数重复,使用 “\{n,m}” 这样的形式。其中“n” 和 “m” 都是数字。在它前面的那个项将被重复 “n” 到 “m” 次(|inclusive| 包含 “n” 和 “m”)。例如:

      /ab\{3,5}

匹配 “abbb”,”abbbb” 以及 “abbbbb”。

  当 “n” 省略时,被默认为零。当 “m” 省略时,被默认为无限大。当“,m” 省略时,就表示重复正好 “n” 次。例如:

      模式          匹配次数

      \{,4}           0,1,2,3 或 4

      \{3,}           3,4,5,等等

      \{0,1}          0 或 1,同 \=

      \{0,}           0 或更多,同 *

      \{1,}           1 或更多,同 \+

      \{3}            3

5,多选一匹配

在一个查找模式中,”或” 运算符是 “\|”。例如:

      /foo\|bar

这个命令匹配了 “foo” 或 “bar”。更多的抉择可以连在后面:

      /one\|two\|three

匹配 “one”,”two” 或 “three”。

  如要匹配其多次重复,那么整个抉择结构须置于 “\(” 和 “\)” 之间:

      /\(foo\|bar\)\+

这个命令匹配 “foo”,”foobar”,”foofoo”,”barfoobar”,等等。

  再举个例子:

      /end\(if\|while\|for\)

这个命令匹配 “endif”,”endwhile”和 “endfor”。

 

Linux下转换字符集(UTF8转换)(转)

分类: linux研究2009-03-0314:14 2616人阅读 评论(0) 收藏 举报

linux语言class编程filec

 LINUX上进行编码转换时,既可以利用iconv函数族编程实现,也可以利用iconv命令来实现,只不过后者是针对文件的,即将指定文件从一种编码转换为另一种编码。
一、利用iconv函数族进行编码转换
iconv
函数族的头文件是iconv.h,使用前需包含之。
#include <iconv.h>
iconv
函数族有三个函数,原型如下:
(1) iconv_t iconv_open(const char *tocode, const char *fromcode);
此函数说明将要进行哪两种编码的转换,tocode是目标编码,fromcode是原编码,该函数返回一个转换句柄,供以下两个函数使用。
(2) size_t iconv(iconv_t cd,char **inbuf,size_t *inbytesleft,char**outbuf,size_t *outbytesleft);
此函数从inbuf中读取字符,转换后输出到outbuf,inbytesleft用以记录还未转换的字符数,outbytesleft用以记录输出缓冲的剩余空间。

(3) int iconv_close(iconv_t cd);
此函数用于关闭转换句柄,释放资源。
例子1:C语言实现的转换示例程序

/* f.c :
 代码转换示例C程序 */具体讲,自己的验证实现是根据文中的f.c实现的
#include <iconv.h>
#define OUTLEN 255
main()
{
char *in_utf8 = "
姝e????";<=======此字符串似乎并不是正在安装四个字的UTF8的对照串,
char *in_gb2312 = "
正在安装";
char out[OUTLEN];

//unicode
码转为gb2312
rc = u2g(in_utf8,strlen(in_utf8),out,OUTLEN);
printf("unicode-->gb2312 out=%sn",out);
//gb2312
码转为unicode
rc = g2u(in_gb2312,strlen(in_gb2312),out,OUTLEN);
printf("gb2312-->unicode out=%sn",out);
}
//
代码转换:从一种编码转为另一种编码
int code_convert(char *from_charset,char *to_charset,char *inbuf,int inlen,char*outbuf,int outlen)
{
iconv_t cd;
int rc;
char **pin = &inbuf;
char **pout = &outbuf;

cd = iconv_open(to_charset,from_charset);
if (cd==0) return -1;
memset(outbuf,0,outlen);
if (iconv(cd,pin,&inlen,pout,&outlen)==-1) return -1;
iconv_close(cd);
return 0;
}
//UNICODE
码转为GB2312
int u2g(char *inbuf,int inlen,char *outbuf,int outlen)
{
return code_convert("utf-8","gb2312",inbuf,inlen,outbuf,outlen);
}
//GB2312
码转为UNICODE
int g2u(char *inbuf,size_t inlen,char *outbuf,size_t outlen)
{
returncode_convert("gb2312","utf-8",inbuf,inlen,outbuf,outlen);
}

例子2:C++语言实现的转换示例程序

/* f.cpp :
代码转换示例C++程序 */
#include <iconv.h>
#include <iostream>

#define OUTLEN 255

using namespace std;

//
代码转换操作类
class CodeConverter {
private:
iconv_t cd;
public:
//
构造
CodeConverter(const char *from_charset,const char *to_charset) {
cd = iconv_open(to_charset,from_charset);
}

//
析构
~CodeConverter() {
iconv_close(cd);
}

//
转换输出
int convert(char *inbuf,int inlen,char *outbuf,int outlen) {
char **pin = &inbuf;
char **pout = &outbuf;

memset(outbuf,0,outlen);
return iconv(cd,pin,(size_t *)&inlen,pout,(size_t *)&outlen);
}
};

int main(int argc, char **argv)
{
char *in_utf8 = "
姝e????";
char *in_gb2312 = "
正在安装";
char out[OUTLEN];

// utf-8-->gb2312
CodeConverter cc = CodeConverter("utf-8","gb2312");
cc.convert(in_utf8,strlen(in_utf8),out,OUTLEN);
cout << "utf-8-->gb2312 in=" << in_utf8 <<",out=" << out << endl;

// gb2312-->utf-8
CodeConverter cc2 = CodeConverter("gb2312","utf-8");
cc2.convert(in_gb2312,strlen(in_gb2312),out,OUTLEN);
cout << "gb2312-->utf-8 in=" << in_gb2312 <<",out=" << out << endl;
}


二、利用iconv命令进行编码转换

iconv
命令用于转换指定文件的编码,默认输出到标准输出设备,亦可指定输出文件。

用法:iconv [选项...][文件...]

有如下选项可用:

输入/输出格式规范:
-f, --from-code=
名称原始文本编码
-t, --to-code=
名称输出编码

信息:
-l, --list
列举所有已知的字符集

输出控制:
-c
从输出中忽略无效的字符
-o, --output=FILE
输出文件
-s, --silent
关闭警告
--verbose
打印进度信息

-?, --help
给出该系统求助列表
--usage
给出简要的用法信息
-V, --version
打印程序版本号

例子:
iconv -f utf-8 -t gb2312 aaa.txt >bbb.txt
这个命令读取aaa.txt文件,从utf-8编码转换为gb2312编码,其输出定向到bbb.txt文件。

 

pthread_attr_setdetachstate

分类: Linux 系列2012-12-1317:45 988人阅读 评论(0) 收藏 举报

在任何一个时间点上,线程是可结合的(joinable),或者是分离的(detached)。一个可结合的线程能够被其他线程收回其资源和杀死;在被其他线程回收之前,它的存储器资源(如栈)是不释放的。相反,一个分离的线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放。

        线程的分离状态决定一个线程以什么样的方式来终止自己。在默认情况下线程是非分离状态的,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。所以如果我们在创建线程时就知道不需要了解线程的终止状态,则可以pthread_attr_t结构中的detachstate线程属性,让线程以分离状态启动。

设置线程分离状态的函数为pthread_attr_setdetachstatepthread_attr_t*attr, int detachstate)。第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD_CREATE_JOINABLE(非分离线程)。这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timewait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。

另外一个可能常用的属性是线程的优先级,它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数pthread_attr_setschedparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。

线程等待——正确处理线程终止

#include <pthread.h>

void pthread_exit(void *retval);

void pthread_join(pthread_t th,void *thread_return);//挂起等待th结束,*thread_return=retval;

int pthread_detach(pthread_t th);

如果线程处于joinable状态,则只能只能被创建他的线程等待终止。

Linux平台默认情况下,虽然各个线程之间是相互独立的,一个线程的终止不会去通知或影响其他的线程。但是已经终止的线程的资源并不会随着线程的终止而得到释放,我们需要调用pthread_join() 来获得另一个线程的终止状态并且释放该线程所占的资源。(说明:线程处于joinable状态下)

调用该函数的线程将挂起,等待th 所表示的线程的结束。thread_return 是指向线程th 返回值的指针。需要注意的是th 所表示的线程必须是joinable 的,即处于非detached(游离)状态;并且只可以有唯一的一个线程对th 调用pthread_join() 。如果th 处于detached 状态,那么对th pthread_join() 调用将返回错误。

如果不关心一个线程的结束状态,那么也可以将一个线程设置为detached 状态,从而让操作系统在该线程结束时来回收它所占的资源。将一个线程设置为detached状态可以通过两种方式来实现。一种是调用pthread_detach() 函数,可以将线程th 设置为detached 状态。另一种方法是在创建线程时就将它设置为detached 状态,首先初始化一个线程属性变量,然后将其设置为detached 状态,最后将它作为参数传入线程创建函数pthread_create(),这样所创建出来的线程就直接处于detached 状态。

创建detach 线程:

pthread_t tid;

pthread_attr_t attr;

pthread_attr_init(&attr);

pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

pthread_create(&tid, &attr, THREAD_FUNCTION, arg);

总之为了在使用pthread 时避免线程的资源在线程结束时不能得到正确释放,从而避免产生潜在的内存泄漏问题,在对待线程结束时,要确保该线程处于detached 状态,否着就需要调用pthread_join() 函数来对其进行资源回收。

 

pthread_attr_init线程属性

1.线程属性

       线程具有属性,用pthread_attr_t表示,在对该结构进行处理之前必须进行初始化,在使用后需要对其去除初始化。我们用pthread_attr_init函数对其初始化,用pthread_attr_destroy对其去除初始化。

 

1.

名称::

pthread_attr_init/pthread_attr_destroy

功能:

对线程属性初始化/去除初始化

头文件:

#include<pthread.h>

函数原形:

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<pthread.h>

函数原形:

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<pthread.h>

 

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<pthread.h>

函数原形:

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<pthread.h>

函数原形:

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<pthread.h>

函数原形:

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中定义如下:

      

structsched_param

{

       intsched_priority;

};

 

结构sched_param的子成员sched_priority控制一个优先权值,大的优先权值对应高的优先权。系统支持的最大和最小优先权值可以用sched_get_priority_max函数和sched_get_priority_min函数分别得到。

 

注意:如果不是编写实时程序,不建议修改线程的优先级。因为,调度策略是一件非常复杂的事情,如果不正确使用会导致程序错误,从而导致死锁等问题。如:在多线程应用程序中为线程设置不同的优先级别,有可能因为共享资源而导致优先级倒置。

 

6.

http://linux.die.net/man/3/sched_get_priority_min 

Synopsis

#include <sched.h>

int sched_get_priority_max(int policy);
int sched_get_priority_min(int policy); 

Description

The sched_get_priority_max()and sched_get_priority_min()functions shall return the appropriate maximum or minimum, respectively, forthe scheduling policy specified bypolicy.

The value of policy shallbe one of the scheduling policy values defined in <sched.h>.

Return Value

If successful, the sched_get_priority_max()and sched_get_priority_min()functions shall return the appropriate maximum or minimum values, respectively.If unsuccessful, they shall return a value of -1 and set errno to indicate theerror.

Errors

The sched_get_priority_max()and sched_get_priority_min()functions shall fail if:

EINVAL

The value of the policy parameterdoes not represent a defined scheduling policy.

 

   下面是上面几个函数的程序例子:

 

#include <string.h>
#include<pthread.h>
#include<sched.h>
void *child_thread(void *arg)
{
int policy = 0;
int max_priority = 0,min_priority = 0;
struct sched_param param;
pthread_attr_t attr;
 
pthread_attr_init(&attr);
pthread_attr_setinheritsched(&attr,PTHREAD_EXPLICIT_SCHED);
pthread_attr_getinheritsched(&attr,&policy);
if(policy == PTHREAD_EXPLICIT_SCHED){
printf("Inheritsched:PTHREAD_EXPLICIT_SCHED\n");
}
if(policy == PTHREAD_INHERIT_SCHED){
printf("Inheritsched:PTHREAD_INHERIT_SCHED\n");
}
 
pthread_attr_setschedpolicy(&attr,SCHED_RR);
pthread_attr_getschedpolicy(&attr,&policy);
if(policy == SCHED_FIFO){
printf("Schedpolicy:SCHED_FIFO\n");
}
if(policy == SCHED_RR){
printf("Schedpolicy:SCHED_RR\n");
}
if(policy == SCHED_OTHER){
printf("Schedpolicy:SCHED_OTHER\n");
}
max_priority = sched_get_priority_max(policy);
min_priority = sched_get_priority_min(policy);
printf("Maxpriority:%u\n",max_priority);
printf("Minpriority:%u\n",min_priority);
 
param.sched_priority = max_priority;
pthread_attr_setschedparam(&attr,&param);
printf("sched_priority:%u\n",param.sched_priority);
pthread_attr_destroy(&attr);
}
 
int main(int argc,char *argv[ ])
{
pthread_t child_thread_id; 
pthread_create(&child_thread_id,NULL,child_thread,NULL);
pthread_join(child_thread_id,NULL);
}

==[23]==gaoke@dev64_23:~/code$g++ -o test gaoke.cpp -lpthread
==[23]==gaoke@dev64_23:~/code$./test
Inheritsched:PTHREAD_EXPLICIT_SCHED
Schedpolicy:SCHED_RR
Maxpriority:99
Minpriority:1
sched_priority:99

 

Linux信号量线程控制

分类: 程序相关2009-11-11 16:31 1237人阅读 评论(0) 收藏 举报

linuxnulljoinstruct

 线程中互斥锁的使用,达到对共享资源互斥使用。除了使用互斥锁,信号量,也就是操作系统中所提到的PV原语,能达到互斥和同步的效果,这就是今天我们所要讲述的信号量线程控制。

PV原语是对整数计数器信号量sem的操作,一次P操作可使sem减一,而一次V操作可是sem加一。进程(或线程)根据信号量的值来判断是否对公共资源具有访问权限。当信号量的值大于零或等于零的时候,该进程(或线程)具有对公共资源访问的权限,否则,当信号量的值小于时,该进程(或线程)就会被阻塞,直到信号量的值大于或等于一。

1、在LINUX中,实现了POSIX的无名信号量,主要用于线程间的互斥同步,下面将简单介绍一些函数接口:

1)、sem_init

功能:        用于创建一个信号量,并初始化信号量的值。

头文件:      <semaphore.h>

函数原型:    int sem_init (sem_t* sem, int pshared, unsigned int value);

函数传入值:  sem:信号量。

                   pshared:决定信号量能否在几个进程间共享。由于目前LINUX还没有实现进

                              程间共享信息量,所以这个值只能取0

函数返回值:  0:成功。

                  -1:失败。

2)其他函数。

int sem_wait       (sem_t* sem);

int sem_trywait   (sem_t* sem);

int sem_post       (sem_t* sem);

int sem_getvalue (sem_t* sem);

int sem_destroy   (sem_t* sem);

功能:sem_waitsem_trywait相当于P操作,它们都能将信号量的值减一,两者的区别在

        于若信号量的值小于零时,sem_wait将会阻塞进程,而sem_trywait则会立即返回。

        sem_post相当于V操作,它将信号量的值加一,同时发出唤醒的信号给等待的进程

       (或线程)。

        sem_getvalue 得到信号量的值。

        sem_destroy 摧毁信号量。

函数传入值:sem:信号量。

函数返回值:同上。

2、函数实现。

[cpp] view plaincopy

#include <stdio.h>    

#include <stdlib.h>    

#include <unistd.h>    

#include <pthread.h>    

#include <semaphore.h>    

#include <errno.h>    

   

#define return_if_fail(p)  /     

        if(!p){printf ("[%s]: func error!", __func__);return NULL;}     

    

typedef struct _PrivInfo     

{     

  sem_t    sem;     

  int      lock_var;     

  time_t   end_time;     

}PrivInfo;     

    

static void info_init (PrivInfo* thiz);     

static void *pthread_func_1 (void* thiz);     

static void *pthread_func_2 (void* thiz);     

    

int main (int argc, char** argv)     

{     

  pthread_t pt_1 = 0;     

  pthread_t pt_2 = 0;     

  int ret = 0;     

  PrivInfo* thiz = NULL;     

    

  thiz = (PrivInfo* )malloc (sizeof (PrivInfo));     

  if (thiz == NULL)     

  {     

    printf ("[%s]:Failed to malloc PrivInfo./n");     

    return -1;     

  }     

    

  info_init (thiz);     

       

  ret = pthread_create (&pt_1, NULL, pthread_func_1, thiz);     

  if (ret != 0)     

  {     

    perror ("pthread_1_create:");     

  }     

    

  ret = pthread_create (&pt_1, NULL, pthread_func_2, thiz);     

  if (ret != 0)     

  {     

    perror ("pthread_2_create:");     

  }     

    

  pthread_join (pt_1, NULL);     

  pthread_join (pt_2, NULL);     

    

  sem_destroy (&thiz->sem);     

  free (thiz);     

  thiz = NULL;     

    

  return 0;     

}     

    

static void info_init (PrivInfo* thiz)     

{     

    

   thiz->lock_var = 0;     

   thiz->end_time = time(NULL) + 10;     

    

   sem_init (&thiz->sem, 0,  1);     

    

   return;     

}     

    

static void *pthread_func_1 (void* th)     

{   

  return_if_fail(th);  

    

  int i = 0;     

  PrivInfo* thiz = (PrivInfo*)th;  

  while (time(NULL) < thiz->end_time)     

  {     

     sem_wait (&thiz->sem);     

     printf ("pthread: pthread1 get lock./n");     

     printf ("the lock_var = %d/n", thiz->lock_var);   

     for (i = 0; i < 2; i ++)     

     {     

         thiz->lock_var ++;     

         sleep (1);     

     }     

    

     sem_post (&thiz->sem);     

       

     sleep (1);    

  }     

  

  

   return NULL;     

}     

    

static void *pthread_func_2 (void* th)     

{    

   return_if_fail(th);  

    

   PrivInfo* thiz = (PrivInfo*)th;  

   while (time (NULL) < thiz->end_time)     

   {     

      sem_wait (&thiz->sem);     

      printf ("pthread2: pthread2 get lock!/n");     

      printf ("the lock_var = %d/n", thiz->lock_var);         

      sem_post (&thiz->sem);     

      sleep (3);     

   }     

   return NULL;     

}   

C语言中printf格式化输出函数

分类:C语言 2009-10-09 11:18 24081人阅读 评论(1) 收藏举报

语言c扩展

目录(?)[+]

用 法:

  intprintf(const char *format,[argument]);

  format 参数输出的格式,定义格式为:

  %[flags][width][.perc][F|N|h|l]type

  规定数据输出方式,具体如下:

  1.type 含义如下:

  d 有符号10进制整数

  i 有符号10进制整数

  o 有符号8进制整数

  u 无符号10进制整数

  x 无符号的16进制数字,并以小写abcdef表示

  X 无符号的16进制数字,并以大写ABCDEF表示

  F/f 浮点数

  E/e 用科学表示格式的浮点数

  g 使用%f和%e表示中的总的位数表示最短的来表示浮点数 G 同g格式,但表示为指数

  c 单个字符

  s 字符串

  % 显示百分号本身

  p 显示一个指针,near指针表示为:XXXX

  far 指针表示为:XXXX:YYYY

  n 相连参量应是一个指针,其中存放已写字符的个数

  2.flags 规定输出格式,取值和含义如下:

  无 右对齐,左边填充0和空格

  - 左对齐,右边填充空格

  + 在数字前增加符号 + 或 -

  一个空格 只对负数显示符号

  # 当type=c,s,d,i,u时没有影响

  type=o,x,X时,分别在数值前增加'0',"0x","0X"

  type=e,E,f时,总是使用小数点

  type=g,G时,除了数值为0外总是显示小数点 3.width 用于控制显示数值的宽度,取值和含义如下n(n=1,2,3...) 宽度至少为n位,不够以空格填充

  0n(n=1,2,3...)宽度至少为n位,不够左边以0填充 * 格

  式列表中,下一个参数还是width4.prec 用于控制小数点后面的位数,取值和含义如下:

  无 按缺省精度显示

  0 当type=d,i,o,u,x时,没有影响

  type=e,E,f时,不显示小数点

  n(n=1,2,3...)当type=e,E,f时表示的最大小数位数

  type=其他,表示显示的最大宽度 .*

  格式列表中,下一个参数还是width

  5.F|N|h|l 表示指针是否是远指针或整数是否是长整数

  F 远指针

  n 近指针

  h短整数或单精度浮点数

  l 长整数或双精度浮点数

  1.一般格式

  printf(格式控制,输出表列)

  例如:printf("i=%d,ch=%c/n",i,ch);

  说明:

  (1)“格式控制”是用双撇号括起来的字符串,也称“转换控制字符串”,它包括两种信息:

  ①格式说明:由“%”和格式字符组成,它的作用是将输出的数据转换为指定的格式输出。

  ②普通字符,即需要原样输出的字符。

  (2)“输出表列”是需要输出的一些数据,可以是表达式

  (3)printf函数的一般形式可以表示为

  printf(参数1,参数2,……,参数n)

  功能是将参数2~参数n按参数1给定的格式输出

  2.格式字符(9种)

  (1)d(或i)格式符。用来输出十进制整数,有以下几种用法:

  ①%d,按整型数据的实际长度输出。

  ②%md,m为指定的输出字段的宽度。如果数据的位数小于m,则左端补以空格,若大于m,则按实际位数输出。

  ③%ld(%mld 也可),输出长整型数据。

  例如:longa=123456;

  printf("%ld",a);

  (2)o格式符,以八进制数形式输出整数。格式:%o,%mo,%lo,%mlo都可。

  (3)x(或X)格式符,以十六进制数形式输出整数。格式:%x,%mx,%lx,%mlx都可。

  (4)u格式符,用来输出unsigned型数据,即无符号数,以十进制数形式输出。格式:%u,%mu,%lu都可。

  参见:li4-3.c/*无符号数据的输出*/

  (5)c格式符,用来输出一个字符。格式:%c,%mc都可。

  (6)s格式符,用来输出一个字符串。格式:%s,%ms,%-ms,%m.ns,%-m.ns都可。

  参见:li4-5.c/*字符串的输出*/

  (7)f格式符,用来输出实数(包括单、双精度),以小数形式输出。格式:%f,%m.nf,%-m.nf都可。

  注意:单精度实数的有效位数一般为7位,双精度为16位。

  参见:li4-6.c/*输出单精度实数时的有效位数*/

  li4-7.c/*输出双精度实数时的有效位数*/

  li4-8.c/*输出实数时指定小数位数*/

  (8)e(或E)格式符,以指数形式输出实数。格式:%e,%m.ne,%-m.ne都可。

  (9)g(或G)格式符,用来输出实数,它根据数值的大小,自动选f格式或e格式(选择输出时占宽度较小的一种)。

  3.说明

  (1)除了X、E、G(用大写字母表示)外,其他格式字符必须用小写字母;

  (2)“格式控制”字符串内可以包含转义字符;

  (3)如果想输出字符“%”,则应该在“格式控制”字符串中用连续两个%表示,如:

  printf("%f%%",1.0/3);

  (4)格式字符表参见下表

  表4.1 printf格式字符

  格式字符 说明

  d,i 以带符号的十进制形式输出整数(正数不输出符号)

  o 以八进制无符号形式输出整数(不输出前导符0)

  x,X 以十六进制无符号形式输出整数(不输出前导符0x),用x则输出十六进制数的a~f时以小写形式输出,用X时,则以大写字母输出

  u 以无符号十进制形式输出整数

  c 以字符形式输出,只输出一个字符

  s 输出字符串

  f 以小数形式输出单、双精度数,隐含输出6位小数

  e,E 以指数形式输出实数

  g,G 选用%f或%e格式中输出宽度较短的一种格式,不输出无意义的0

  表4.2 printf的附加格式说明字符

  字符

  说明

  字母l

  用于长整型整数,可加在格式符d、o、x、u前面

  m(代表一个正整数)

  数据最小宽度

  n(代表一个正整数)

  对实数,表示输出n位小数;对字符串,表示截取的字符个数

  -

  输出的数字或字符在域内向左靠

程序例:

  #include <stdio.h>

  int main()

  {

  printf("Hello,world/n");

  while(1);

  }

  #include<stdio.h>

  int main()

  {

  int i = 1, j=2;

  printf("%d%d/n",i,j);

  while(1);

  }

printf 命令

  用途

  写格式化输出。

  语法

  printfFormat [ Argument ... ]

  描述

  printf 命令转换、格式化并写 Argument 参数到标准输出。Argument 参数是由 Format 参数控制格式化的。格式化输出行不能超出 LINE_MAX 字节长度。

  下列环境变量影响printf 命令的执行:

  LANG 在 LC_ALL 和相应的环境变量(以 LC_ 开头)没有指定语言环境时,确定语言环境编目使用的语言环境。

  LC_ALL 确定用于覆盖由 LANG 或其它任何 LC_ 环境变量设置的任何语言环境编目值的语言环境。

  LC_CTYPE 确定把文本字节数据顺序解释为字符的语言环境;例如,单一字节对应多字节字符的参数。

  LC_MESSAGES 确定写消息使用的语言。

  LC_NUMERIC 确定数字格式编排的语言环境。此环境变量影响使用 e、E、f、g 和 G 转换字符编写的数字的格式。

  Format 参数是包含三种对象类型的一个字符串:

  * 无格式字符复制到输出流。

  * 转换规范,每个规范导致在值参数列表中检索 0 个或更多个项。

  * 以下转义序列。在复制到输出流时,这些序列导致它们的相关操作在有此功能的设备上显示:

  // 反斜杠

  /a 警告

  /b 退格

  /f 换页

  /n 换行

  /r 回车

  /t 跳格

  /v 垂直跳格

  /ddd ddd 是 1、2 或 3 位八进制数字。这些转义序列作为由八进制数指定的具有数字值的字节显示。

  Argument 参数是一个或多个字符串的列表,它在 Format 参数的控制下被写到标准输出。

  Format 参数在必要的情况