控制台切换:ctrl+alt+(f1-f7);
添加用户名:useradd
【选项】用户名;例:useradd smb添加名为smb的用户;
修改密码:passwd 【选项】 用户名;例:passwd smb修改名为smb用户的密码;
切换用户:su 【选项】 【用户名】;例:su – root切换至root用户,并将root用户的环境变量同时带入;
关机:shutdowm;例:shutdowm now;
拷贝: cp 【选项】源文件或目录目标文件或目录;
例:1.cp /home/test /tmp/ 将/home目录下的test文件拷贝到/tmp目录下;
2.cp –r /home/dir1 /tmp/ 将/home目录下的dir1目录拷贝到/tmp目录;
移动或更名:mv 【选项】源文件或目录 目标文件或目录;
例:1.mv /home/test /home/test1 将/home目录下得test文件更名为test1;2.mv /home/dir1 /tmp/将/home目录下的dir1目录移动到/tmp目录下;
删除:rm 【选项】 文件或目录;
例:1.rm /home/test删除/home目录下得test文件;
2.rm –r /home/dir 删除/home目录下的dir目录;
创建目录:mkdir 【选项】目录名;
例:1.mkdir /home/workdir 在/home目录下创建workdir目录;
2.mkdir –p /home/dir1/dir2 创建/home/dir1/dir2目录,如果dir1不存在,先创建dir1;
改变工作目录:cd 目录名;例:cd /home/ 进入/home目录;
查看当前路径:pwd ;例:pwd 显示当前工作目录的绝对路径;
查看目录:ls 【选项】【目录或文件】;
例:1.ls /home 显示/home目录下的文件和目录(不包含隐藏文件);
2.ls –a /home显示/home目录下的所有文件和目录(包含隐藏文件);
3.ls –l /home 显示/home目录下的文件和目录的详细信息;
4.ls –c /home 显示/home目录下的文件和目录,按修改时间排序;
打包与压缩:tar 【选项】 目录或文件;
例:1.tarcvftmp.tar
/home/tmp 将/home/tmp目录下得所有文件和目录打包成一个tmp.tar文件;
2.tar xvf tmp.tar 将打包tmp.tar文件在当前目录下解压;
3. tar cvzf tmp.tar.gz /home/tmp 将/home/tmp目录下得所有目录和文件打包并压缩成一个tmp.tar.gz文件;
4. tar xvzf tmp.tar.gz 将tmp.tar.gz压缩包在当前目录解压;
解压缩:unzip 【选项】 压缩文件名.zip;例:1.unzip tmp.zip 解压tmp.zip文件;
访问权限:当用 ls –l命令显示文件或目录时最左边一列为文件的访问权限,第一个字符指定了文件类型,如果第一个字符是横线,表示是一个非目录的普通文件,如果是d,表示是一个目录;
三组访问权限分别为:文件所有者,与文件所有者同组用户,其他用户;r代表只读,w代表写,x代表可执行;
例:$ ls –l sobsrc .tgz -rw-r--r--1 root root 483997 Ju1 l5 17:3lsobsrc.Tgz
改变访问权限:chmod 【who】【+|-|=】【mode】文件名;参数:who:u表示文件所有者,g表示与文件所有者同组的用户,o表示其他用户,a表示所有用户,它是系统默认值;mode:+添加某个权限,-取消某个权限,=赋予给定权限,
例:chmod g+w hello.c;mode表示的权限可使用下述字母(数字)的组合:r可读(4),w可写(2),x可执行(1);例chmod 761hello.c;
查看磁盘使用情况:df 【选项】;例:df –k以KB为单位显示磁盘使用情况;
查看目录大小:du 【选项】目录;例:du –b ipc 以字节为单位显示ipc这个目录的大小;
网络配置:ifconfig 【选项】 【网络接口】;
例:1.ifconfig eth0 192.168.0.1配置eth0这一网卡的ip地址为192.168.0.1 ;
2.ifconfig eth0 down 暂停eth0这一网卡的工作;
ifconfig eth0 up 恢复eth0这一网卡的工作;
查看网络状态:netstat 【选项】;例:netstat –a 查看系统中所有的网络监听端口;
软件安装:rpm 【选项】 【安装文件】;
例:1.rpm –ivh tftp.rpm 安装名为tftp的文件;
2.rpm –qa 列出所有已安装rpm包
3.rpm –e name 卸载名为name的rpm包;
挂载:mount 【选项】 设备源 目标目录;
例: mount /dev/cdrom /mnt
将光驱挂载到/mnt目录下;
取消挂载:umount 目标目录;
例:umount /mnt 取消光驱在/mnt下的挂载;
查找文件:find 路径 –name ‘文件名’;
例:1.find ./ -name ‘co*’
在当前目录及其子目录中寻找名为co开头的文件;
2.find ./ -name ‘test’ 在当前目录及其子目录中寻找名为test的文件;
查找字符串:grep 【选项】 字符串;
例:1.grep “file” ./ -rn 在当前目录及其子目录中,查找包含file字符串的文件;
2.netstat –a | grep tftp查看所有端口中用于tftp的端口;
动态查看cpu的使用:top ;
例:top 查看系统中的进程对cpu、内存等的占用情况;
查看进程:ps 【选项】;
例:ps aux 查看系统中的所有进程;
杀死进程:kill 【选项】 进程号;
例:1.kill –s SIGKILL 4096 杀死4096号进程;
帮助:man 命令名;
例:1.man grep 查看grep命令的使用方法;
2.man ping查看ping命令的使用方法;
文本编辑vi的三种工作模式:
命令行模式:最初进入的一般模式,该模式下可以移动光标进行浏览,整行删除,但无法编辑文字;
插入模式:只有在该模式下,用户才能进行文字的编辑输入,用户可以使用【esc】键回到命令行模式;
底行模式:该模式下,光标位于屏幕底行,用户可以进行文件保存或退出操作,也可以设置编辑环境,如寻找字符串、列出行列号;
文本编辑:
1.vi hello.c
2.键入i进入插入模式
3.编辑
4.键入【esc】退入到命令行模式
5.键入:wq保存退出
命令行模式功能键:
yy:复制当前光标所在行;
【n】yy:n为数字,复制当前光标开始的n行;
p:粘贴复制的内容到光标所在行;
dd:删除当前光标所在行;
【n】dd:删除当前光标所在行开始的n行;
/name:查找光标之后名为“name”的字符串;
G:光标移动到文件尾(大写);
u:取消前一个动作(小写);
底行模式功能键:
:w保存;
:q退出vi(系统会提示保存修改);
:q!强行退出(对修改不做保存);
:wq保存后退出;
:w【filename】另存为filename的文件;
:set nu显示行号;:set nonu取消行号;
Tftp服务器:工作于宿主机上的软件,主要提供对目标机的主要映像文件的下载工作;
使用rpm包安装Tftp-server:
1.如果利用如下命令能够看到服务器已启动,则不用安装#netstat–a | grep tftp 已安装结果: udp 0 0 *:tftp*:*
2.如果没有安装,执行如下命令安装# rpm –ivhtftp-server-0.42-3.1.i386.rpm(rhel安装光盘\Servers目录)
3.建立tftp的主工作目录# mkdir /tftpboot
4.修改配置文件# vi /etc/xinetd.d/tftp
Service tftp
{
……
Server_args=-s
/tftpboot
Disable=no
……
}
5.tftp服务器启动:#/etc/init.d/xinetdstart
#netstat –a | grep tftp
Udp 0 0 *:tftp*:*
网络文件系统NFS:一种将远程主机上的分区(目录)经网络挂载到本地的一种机制,通过对网络文件系统的支持,用户可以在本地系统上像操作本地分区一样来对远程主机的共享分区(目录)进行操作。(类似于windows共享目录);
NFS安装:# rpm –ivh nfs-utils-1.0.9-24.el5.i386.rpm
NFS配置:# vi /etc/exports 加入允许被其他计算机访问的目录和访问权限例:/home 192.168.1.*(rw,sync,no_root_squash)
1./home 允许被其他计算机访问的目录;
2.192.168.1.*:被允许访问该目录的客户端ip地址;
3.Rw:可读可写;
4.Sync:同步写磁盘(async:资料会先暂存于内存当中,而非直接写入硬盘);5.no_root_squash:表示客户端root用户对该目录具备写权限;
启动NFS:/etc/init.d/nfsstart;
重新启动NFS:/etc/init.d/nfsrestart;
NFS使用:使用mount命令来挂载NFS服务器上的共享目录。
# mount –tnfs servername:/shared_dir /localdir;
例:# mount –t nfs
10.168.1.100:/home /mnt/nfs 将IP地址为10.168.1.100的主机的/home目录加载到本机/mnt/nfs实现共享;
Samba:Linux与Linux之间通过NFS实现共享,Windows与Windows之间通过共享目录实现共享,Linux与Windows之间用samba实现共享;
重启网络:/etc/init.d/networkrestart;检测网络是否畅通:ping【windowsIP地址】
修改samba配置# vi /etc/samba/smb.conf,添加:
【root】
comment=Root Directories
browseable=yes
writeable=yes
path=/
valid users=smb(用户名)
添加名为smb的samba系统用户:useradd smb;
修改用户smb的密码:smbpasswd –a smb;
重启samba:# /etc/init.d/smb restart;
Shell脚本:一个包含一系列命令序列的文本文件;
Shell脚本语法:第一行: #!/bin/sh;
符号#!用来指定该脚本文件的解析程序,#!/bin/sh使用/bin/sh来解析该脚本;
注释:以符号#开始,直到该行结束;
变量:在shell编程中,所有变量都由字符串组成,变量是没有类型的,并且不需要预先对变量进行声明,变量在赋值时前不需要加$,在使用时前需要加符号$;
Shell编程中默认变量:$#:传入脚本的命令行参数个数;
$*:所有命令行参数值,在各个参数值之间留有空格;
$0:命令本身(shell文件名);
$1:第一个命令行参数;
$2:第二个命令行参数;
Shell编程中局部变量:在变量首次被赋值时加上local关键字可以声明一个局部变量;
变量(注意):
1.变量赋值时,“=”左右两边不能有空格;
2.BASH中的语句结尾不需要分号;
if语句:if 【expression】
then #codeblock
else #codeblock
fi
比较:
相同 –eq = ;
不同 –ne !=;
大于 –gt >;
小于 –lt <;
大于或等于 –ge ;
小于或等于 –le;
为空 –z;
不为空 –n;
for循环:for var in 【list】
do
#codeblock
Done
其中$var是循环控制变量,【list】是var需要遍历的一个集合,do/done对包含了循环体,相当于c语言中的一对大括号。
另外如果do和for被写在同一行,必须在do前面加上“;”。
如for $var in 【list】;do
注意:for循环中的var不加$,而循环体内则必须要加$;
wile循环:while 【condition】
do
#codeblock
done
Until循环: until 【condition】
do
#codeblock
done
while循环和until循环的区别在于while是在condition为真时执行,而until是在为假的时候执行;
case语句:case“$var”in
condition1)
;;
Condition2)
;;
*)
defaultstatements;;
esac
gcc
gcc编译器:最基本用法:gcc 【option】【filenames】
option:编译器所需要的编译选项;
filenames:要编译的文件名;
编译选项:
1.gcc filename –o output_filename(注:-o为小写)编译文件并确定可执行文件的名称为output_filename。如果不给出这个选项gcc就给出预设的可执行文件a.out。
例:
(1)gcchello.c –o hello,编译hello.c成名为hello的文件;
(2)gcchello.c,编译hello.c,给出预设的可执行文件a.out;
2.-c:只编译不连接成可执行文件,编译器只是由输入的.c等源代码文件生成.o为后缀的目标文件;
例:gcc –c hello.c编译hello.c不连接,产生hello.o的文件;
3.-g:产生调试工具(GNU的gdb)所必要的符号信息,要想对编译出的程序进行调试,就必须加入这个选项;
例:gcc –g hello.c –o hello 编译时对程序加入了调试信息;
4.-O(注:大写O):对整个程序进行优化编译、链接,采用这个选项,整个源代码会在编译、连接过程中进行优化处理,这样产生的可执行文件的执行效率可以提高,但是编译、连接的速度就要相对要慢一些。
5.-O2(注:大写O):比-O更好的优化编译、连接,当然整个编译、连接的速度会更慢。gcc –O2 hello.c–o hello;
6.-Idirname(注:首字母大写i):将dirname所指出的目录加入到程序头文件目录列表中(从dirname目录中加入可以编译的头文件);
例:gcc –I/home/include hello.c –o hello;
7.-Ldirname:将dirname所指出的目录加入到库文件的目录列表中。在默认状态下,连接程序ld在系统的预设路径中(如/usr/lib)寻找所需要的库文件,这个选项告诉连接程序,首先到-L指定的目录中去寻找,再到系统预设的目录中寻找。
8.-lname:在连接时,装载名字为“libname.a”的函数库,该函数库位于系统预设的目录或者由-L选项确定的目录下。
例如:-lm表示连接名为“libm.a”的数学函数库。
例:gcc foo.c –L/home/lib –lfoo–o foo;
9.-static:静态链接库文件,
例:gcc –static hello.c -0hello;
库有动态与静态两种,动态通常用.so为后缀,静态用.a。
例如:libhello.so libhello.a。
当使用静态库时,连接器找出程序所需的函数,然后将它们拷贝到可执行文件,一旦连接成功,静态程序库也就不再需要了。然而,对动态库而言,就不是这样,动态库会在执行程序内留下一个标记指明当程序执行时,首先必须载入这个库。由于动态库节省空间,linux下进行连接的缺省操作是首先连接动态库。
10.-Wall(首字母大写):生成所有警告信息;
例:gcc –Wall hello.c –ohello;
11.-w:不生成任何警告信息;
12.-DMACRO:定义MACRO宏,等效于在程序中使用#define MACRO;
例:gcc –DYES hello.c –ohello;编译过程中定义YES;
GDB
GDB程序调试:GDB是GNU发布的一款功能强大的程序调试工具。
GDB主要完成下面三个方面的功能:
1、启动被调试程序。
2、让被调试的程序在指定的位置停住。
3、当程序被停住时,可以检查程序状态(如变量值)。
例:
1.编译生成可执行文件: gcc –g tst.c -0tst;
2.启动GDB:gdb tst;
3.在main函数处设置断点:break main;
4.运行程序:run;
5.单步运行:next;
6.继续运行:contunue;
启动GDB的两种方法:
1. gdb 调试程序名;
例:gdb hello;
2. 首先使用gdb命令,然后file 调试程序名;
GDB命令:
1.list(缩写l),查看程序 ;
2.break(缩写b) 函数名,在某函数入口处添加断点;
3.break(b) 行号,在指定行添加断点;
4.break(b) 文件名:行号,在指定文件的指定行添加断点;
5.break(b) 行号 if条件,当条件为真时,指定行号处断点生效,例:b 5 if i=10,当i等于10时第五行断点生效;
6.info break,查看所有设置的断点;
7.delete 断点编号,删除断点;
8.run(r),开始运行程序;
9.next(n),单步运行程序(进入子函数);
10.step(s),单步运行程序(进入子函数);
11.continue(c),继续运行程序;
12.print(p) 变量名,查看指定变量值;
13.finish 运行程序,直到当前函数结束;
14.watch 变量名,对指定变量进行监控;
15.quit(q) 退出gdb;
GNU make
GNU的make能够使整个软件工程的编译、链接只需要一个命令就可以完成。Make在执行时,需要一个命名为Makefile的文件。Makefile文件描述了整个工程的编译,连接等规则。其中包括:工程中的哪些源文件需要编译以及如何编译;需要创建哪些库文件以及如何创建这些库文件、如何最后产生我们想要的可执行文件。
例:
hello:main.o func1.o func2.o
gcc main.o func1.o func2.o –o hello
main.o:main.c
gcc –c main.c
func1.o:func1.c
gcc –c func1.c
func2.o:func2.c
gcc –c func2.c
.PHONY:clean
clean:
rm –f hello main.o func1.o func2.o
规则:用于说明如何生成一个或多个目标文件,规则格式如下:
targets:prerequisites(目标:依赖)
command(命令)
例:main.o:main.c
Gcc–c main.c(命令需要以【TAP】键开始)
在Makefile中,规则的顺序是很重要的,因为,Makefile中只应该有一个终极目标,其它的目标都是被这个目标所连带出来的,所以一定要让make知道你的终极目标是什么。一般来说,定义在Makefile中的目标可能会有很多,但是第一条规则中的目标将被确立为最终的目标;
make命令默认在当前目录下寻找名字为makefile或者Makefile的工程文件,当名字不为这两者之一时,可以使用如下方法指定:make –f文件名;
伪目标:Makefile中把那些没有任何依赖只有执行动作的目标称为“伪目标”(phony targets)
.PHONY:clean
Clean:
Rm–f hello main.o fun1.o fun2.o
“.PHONY”将“clean”目标声明为伪目标
变量:如果要为前例中hello目标添加一个依赖,如:func3.0:
方法一:
hello:main.o func1.ofunc2.o func3.o
gccmain.o func1.o func2.o func3.o
方法二:
obj=main.o func1.o func2.o func3.o
hello:$(obj)
gcc$(obj) –o hello
在makefile中,存在系统默认的自动化变量:
1.$^:代表所有的依赖文件;
2.$@:代表目标;
3.$<:代表第一个依赖文件;例:
hello:main.o func1.ofunc2.o
gccmain.o func1.o func2..o –o hello
=>>
hello:main.o func1.o func2.o
gcc$^ -o $@
Makefile中“#”字符后的内容被视作注释。
hello:hello.c
@gcchello.c –o hello
@:取消回显
系统调用——文件访问
文件编程:Linux中文件编程可以使用两种方法:linux系统调用、c语言库函数;前者依赖于linux系统,后者与操作系统是独立的,在任何操作系统下,使用c语言库函数操作文件的方法都是相同的。
系统调用—创建:int creat(const char*filename,mode_t mode)
filename:要创建的文件名(包含路径,缺省为当前路径)
mode:创建模式,常见创建模式(S_IRUSR可读,S_IWUSR可写,S_IXUSR可执行,S_IRWXU可读、写、执行),除了可以使用上述宏以外,还可以直接使用数字来表示文件的访问权限:可执行->1;可写->2;可读->4;上述值的和,如可写可读->6;无任何权限->0;
文件描述:在Linux系统中,所有打开的文件都对应一个文件描述符。文件描述符的本质是一个非负整数。当打开一个文件时,该整数由系统来分配。文件描述符的范围是0 – OPEN_MAX。早期的UNIX版本OPEN_MAX=19,即允许每个进程同时打开20个文件,现在很多系统则将其增加至1024。
系统调用-打开:int open(const char *pathname,int flags)
int open(const char *pathname,intflags,mode_t mode)
pathname:要打开的文件名(包含路径,缺省为当前路径);flags:打开标志;常见的打开标志(O_RDONLY只读方式打开;O_WRONLY只读方式打开;O_RDWR读写方式打开;O_APPEND追加方式打开;0_CREAT创建一个文件;O_NOBLOCK非阻塞方式打开);如果使用了O_CREATE标志,则使用的函数是:int open(const char *pathname,int flags,mode_t
mode);这时需要指定mode来表示文件的访问权限。
系统调用—关闭:当我们操作完文件以后,需要关闭文件:intclose(int fd);fd:文件描述符;
系统调用—读:int read(int fd,constvoid *buf,size_t length)
功能:从文件描述符fd所指定的文件中读取length个字节到buf所指向的缓冲区中,返回值为实际读取的字节数。
系统调用—写:int write(int fd,constvoid *buf,size_t length)
功能:把length个字节从buf指向的缓冲区中写道文件描述符fd所指向的文件中,返回值为实际写入的字节数。
系统调用—定位:int lseek(int fd,offset_t offset,int whence)
功能:将文件读写指针相对whence移动offset个字节。操作成功时,返回文件指针相对于文件头的位置。
系统调用—定位:whence可使用下述值:SEEK_SET(相对文件开头);SEEK_CUR(相对文件读写指针的当前位置);SEEK_END(相对文件末尾);
offset可取负值,表示向前移动。例如下述调用可将文件指针相对当前位置向前移动5个字节:lseek(fd,-5,SEEK_CUR);由于lseek函数的返回值为文件指针相对于文件头的位置,因此下面调用的返回值就是文件的长度:lseek(fd,0,SEEK_END);
系统调用—访问判断:有时我们需要判断文件是否可以进行某种操作(读,写等),这时可以使用access函数:intaccess(const char *pathname,int mode);pathname:文件名称;mode:要判断的访问权限。可以取以下值或者是他们的组合。
R_OK:文件可读,
W_OK:文件可写,
X_0K:文件可执行,
F_OK:文件存在。
返回值:当我们测试成功时,函数返回0,否则如果一个条件不符时,返回-1;
例:
#include<unistd.h>
int main()
{
if(access(“/etc/passwd”,R_OK)==0)
printf(“/etc/passwdcan be read!\n”);
}
库函数—文件访问
C库函数的文件操作是独立于具体的操作系统平台的,不管是在DOS、Windows、Linux还是在VxWorks中都是这些函数。
库函数-创建和打开:FILE *fopen(constchar *filename, const char *mode)
Filename:打开的文件名(包含路径,缺省为当前路径);mode:打开模式;
常见打开模式:r, rb:只读方式打开;w, wb:只写方式打开,如果文件不存在,则创建该文件;a, ab:追加方式打开,如果文件不存 在,则创建该文件;r+, r+b, rb+:读写方式打开;w+, w+b, wh+:读写方式打开,如果文件不存在,则创建该文件;a+, a+b, ab+:读和追加方式打开。如果文件不存在,则创建该文件;
b用于区分二进制文件和文本文件,这一点在DOS、Windows系统中是有区分的,但Linux不区分二进制文件和文本文件。
库函数-读:size_t fread(void *ptr,size_t size, size_t n, FILE
*stream)功能:从文件stream中读取n个字段,每个字段为size字节,并将读取的数据放入ptr所指的字符数组中,返回实际已读取的字节数。
库函数-写:size_t fwrite (constvoid *ptr, size_t size, size_t n, FILE *stream)功能:从缓冲区ptr所指的数组中把n个字段写到文件stream中,每个字段长为size个字节,返回实际写入的字段数。
库函数-字符读:int fgetc(FILE*stream)从指定的文件中读一个字符;
#include<stdio.h>
main()
{
FILE *fp;
char ch;
if((fp=fopen("c1.txt","rt"))==NULL)
{
printf("\nCannot open filestrike any key exit!");
getch();
exit(1);
}
ch=fgetc(fp);
while(ch!=EOF)
{
putchar(ch);
ch=fgetc(fp);
}
fclose(fp);
}
库函数-字符写:int fputc(int c, FILE *stream)向指定的文件中写入一个字符;
#include<stdio.h>
main()
{
FILE *fp;
char ch;
if((fp=fopen("string","wt+"))==NULL) {
printf("Cannot open file,strike any key exit!");
getch();
exit(1);
}
printf("input a string:\n");
ch=getchar();
while (ch!='\n') {
fputc(ch,fp);
ch=getchar();
}
printf("\n");
fclose(fp);
}
库函数-格式化读:fscanf(FILE *stream, char *format [,argument...])从一个流中进行格式化输入;
#include<stdlib.h>
#include<stdio.h>
intmain(void)
{
int i;
printf("Input an integer: ");
if (fscanf(stdin, "%d", &i))
printf("The integer read was:%i\n", i);
return 0;
}
库函数-格式化写:int fprintf(FILE *stream, char *format [,argument,...]) 格式化输出到一个流中;
#include<stdio.h>
#include<process.h>
FILE *stream;
void main(void )
{
int i = 10;
double fp =1.5;
char s[] ="this is a string";
char c ='\n';
stream =fopen( "fprintf.out", "w" );
fprintf(stream, "%s%c", s, c );
fprintf(stream, "%d\n", i );
fprintf(stream, "%f\n", fp );
fclose(stream ); }
库函数-定位:int fseek(FILE *stream, long offset, int whence)
whence:SEEK_SET 从文件的开始处开始搜索;SEEK_CUR 从当前位置开始搜索;SEEK_END 从文件的结束处开始搜索;
路径获取:在编写程序的时候,有时候需要得到当前路径。C库函数提供了getcwd来解决这个问题。char *getcwd(char *buffer,size_t size)我们提供一个size大小的buffer,getcwd会把当前的路径名copy 到buffer中.如果buffer太小,函数会返回-1。
#include<unistd.h>
main()
{
char buf[80];
getcwd(buf,sizeof(buf));
printf(“current working directory : %sn”,buf);
}
创建目录:#include <sys/stat.h> int mkdir(char * dir, int mode)功能:创建一个新目录。返回值:0表示成功,-1表述出错。
时间编程
时间类型:Coordinated Universal Time(UTC):世界标准时间,也就是大家所熟知的格林威治标准时间(Greenwich Mean Time,GMT);Calendar Time:日历时间,是用“从一个标准时间点(如:1970年1月1日0点)到此时经过的秒数”来表示的时间。
时间获取:#include <time.h> time_t time(time_t*tloc)功能:获取日历时间,即从1970年1月1日0点到现在所经历的秒数。
/* typedeflong time_t */
时间保存
struct tm {
inttm_sec; //秒值
int tm_min; //分钟值
int tm_hour; //小时值
int tm_mday; //本月第几日
int tm_mon; //本年第几月
int tm_year; //tm_year + 1900 = 哪一年
int tm_wday; //本周第几日
int tm_yday; //本年第几日
int tm_isdst; //日光节约时间
};
时间获取:struct tm *gmtime(const time_t *timep)功能:将日历时间转化为格林威治标准时间,并保存至TM结构。
structtm *localtime(const time_t *timep)功能:将日历时间转化为本地时间,并保存至TM结构。
#include<time.h>
#include<stdio.h>
int main(void) {
struct tm *local;
time_t t;
t=time(NULL);
local=localtime(&t);
printf("Local hour is:%d\n",local->tm_hour);
local=gmtime(&t);
printf("UTC hour is:%d\n",local->tm_hour); return 0;
}
时间显示:char *asctime(const struct tm *tm)功能:将tm格式的时间转化为字符串,如: Sat Jul 3008:43:03 2005;
char*ctime(const time_t *timep)功能:将日历时间转化为本地时间的字符串形式。
#include<time.h>
#include<stdio.h>
intmain(void)
{
struct tm *ptr;
time_t lt;
lt=time(NULL);
ptr=gmtime(<);
printf(asctime(ptr));
printf(ctime(<));
return 0;
}
intgettimeofday(struct timeval *tv,struct timezone *tz)功能:获取从今日凌晨到现在时间。
structtimeval {
int tv_sec;//秒数
int tv_usec; //微妙数
};
voidfunction() {
unsigned int i,j;
double y;
for(i=0;i<1000;i++)
for(j=0;j<1000;j++)
y=sin((double)i);
}
main() {
struct timeval tpstart,tpend;
float timeuse;
gettimeofday(&tpstart,NULL);
function();
gettimeofday(&tpend,NULL);
timeuse=1000000*(tpend.tv_sec-tpstart.tv_sec)+
tpend.tv_usec-tpstart.tv_usec;
timeuse/=1000000;
printf("Used Time:%f\n",timeuse);
exit(0);
}
延时执行:unsigned int sleep(unsigned int seconds)功能:使程序睡眠seconds秒。
voidusleep(unsigned long usec)功能:使程序睡眠usec微秒。
进程控制原理
进程是一个具有一定独立功能的程序的一次运行活动。
特点:动态性,并发性,独立性,异步性;
进程ID(PID):标识进程的唯一数字;父进程的ID(PPID);启动进程的用户ID(UID);
进程互斥:当有若干进程都要使用某一共享资源时,任何时刻最多允许一个进程使用,其他药使用该资源的进程必须等待,直到占用该资源者释放了该资源为止。
临界资源:操作系统中将一次只允许一个进程访问的资源称为临界资源;
临界区:进程中访问临界资源的那段程序代码称为临界区。为实现对临界资源的互斥访问,应保证诸进程互斥地进入各自的临界区。
进程同步:一组并发进程按一定的顺序执行的过程称为进程间的同步。具有同步关系的一组并发进程称为合作进程,合作进程间互相发送的信号称为消息或事件。
进程调度:按一定算法,从一组待运行的进程中选出一个来占有cpu运行。调度方式:抢占式;非抢占式;
调度算法:先来先服务调度算法;短进程优先调度算法;高优先级优先调度算法;时间片轮转法;
死锁:多个进程因竞争资源而形成一种僵局,若无外力作用,这些进程都将永远不能再向前推进。
进程控制编程:
获取ID:
#include<sys/types.h>
#include<unistd.h>
pid_tgetpid(void)
获取本进程ID
pid_tgetppid(void)
获取父进程ID
例:#include<stdio.h>
#include<unistd.h>
#include<sidlib.h>
intmain(void)
{
printf(“PID=%d\n”,getpid());
printf(“PPID=%d\n”,getpid());
return 0;
}
进程创建-fork:
#include<unistd.h>
Pid_tfork(void)
功能:创建子进程。Fork的奇妙之处在于它被调用一次,却返回两次。它可能有三种不同的返回值:
1.在父进程中,fork返回新创建的子进程PID;
2.在子进程中,fork返回0;
3.如果出现错误,fork返回一个负值;
例:#include<sys/types.h>
#include<unistd.h>
Main()
{
pid_t pid; /*此时仅有一个进程*/
pid=fork();/*此时已经有两个进程在同时运行*/
if(pid<0)
printf(“error in fork”);
else if(pid==0)
printf(“Iam the child process,ID is %d\n”,getpid());
else
printf(“I am the parent process,ID is%d\n”,getpid());
}
进程创建-vfork
#include<sys/types.h>
#include<unistd.h>
pid_tvfork(void) 功能:创建子程序。
fork和vfork的区别:
1.fork:子进程拷贝父进程的数据段;vfork:子进程与父进程共享数据段;2.fork:父、子进程的执行次序不确定;vfork:子进程先运行,父进程后运行;
exec函数族:用被执行的程序替换调用它的程序。
与fork区别:fork创建一个新的进程,产生一个新的PID。exec启动一个新程序,替换与原有的进程,因此进程的PID不会改变。
#include<unistd.h>
Intexecl(const char* path,const char *arg1,……)
参数:path:被执行程序名(含完整路径);arg1-argn:被执行程序所需的命令行参数,含程序名。以空指针(NULL)结束。
例:
#include<unistd.h>
main()
{
execl(“/bin/ls”,”ls”,”-al”,”/etc/passwd”,(char*)0);
}
#include<unistd.h>
Intexeclp(const char* path,const char* arg1,……)
参数:path:被执行程序名(不含路径,将从path环境变量中查找该程序)。arg1-argn:被执行程序所需的命令行参数,含程序名。以空指针(NULL)结束。
例:#include<unistd.h>
main()
{
execlp(“ls”,”ls”,”-al”,”/etc/passwd”,(char*)0);
}
#include<unistd.h>
Intexecv (const char* path,char* const argv[])
参数:path:被执行程序名(含完整路径)。argv[]:被执行程序所需的命令行参数数组。
例:
#include<unistd.h>
main()
{
char*argv[]={“ls”,”-al”,”/etc/passwd”,(char*)0};
}
#include<stdlib.h>
intsystem(const char* string)
功能:调用fork产生子进程,由子进程来调用/bin/sh –c string来执行参数string所代表的命令。
例:
#include<stdlib.h>
Void main()
{
System(“ls –al /etc/passwd”);
}
进程等待:#include <sys/types.h>
#include<sys/wait.h>
pid_twait (int* status)
功能:阻塞该进程,直到其某个子进程退出。例:
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
Void main()
{
pid_t pc,pr;
pc=fork();
if(pc==0){/*如果是子进程*/
printf( “This is child process with pidof %d\n”,getpid());
sleep(10);/*睡眠10秒钟*/
}
else if(pc>0){/*如果是父进程*/
pr=wait(NULL);/*等待*/
printf(“I catched a child process with pid of %d\n”,pr);
}
exit(0);
}
进程间通信
目的:
1.数据传输:一个进程需要将它的数据发送给另一个进程。
2.资源共享:多个进程之间共享同样的资源。
3.通知事件:一个进程需要向另一个或一组进程发送消息,通知它发生了某种事件。
4.进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有操作,并能够及时知道它的状态改变。
Linux进程间通信(ipc)由以下几部分发展而来:
1.unix进程间通信
2.基于system v(unix纵多版本中的一个分支)进程间通信
3.posix进程间通信
POSIX表示可移植操作系统接口,IEEE最初开发posix标准,是为了提高unix环境下应用程序的可移植性。然而posix并不局限于unix,许多其它的操作系统,例如DEC OpenVMS和Microsoft Windows,都支持POSIX标准。
Linux使用的进程间通信方式包括:
1.管道(pipe)和有名管道(FIFO)
2.信号(signal)
3.消息队列
4.共享内存
5.信号量
6.套接字(socket)
管道通信:管道是单向的、先进先出的,它把一个进程的输出和另一个进程的输入连接在一起。
一个进程(写进程)在管道的尾部写入数据,另一个进程(读进程)从管道的头部读出数据。数据被一个进程读出后,将被从管道中删除,其它读进程将不能再读到这些数据。管道提供了简单的流控制机制,进程试图读空管道时,进程将阻塞。同样,管道已经满时,进程再试图向管道写入数据,进程将阻塞。
管道创建:管道包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者可用于运行同一系统中的任意两个进程间的通信。
无名管道由pipe()函数创建:int pipe(int filedis[2]);当一个管道建立时,它会创建两个文件描述符:filedis[0]用于读管道,filedis[1]用于写管道。
管道关闭:关闭管道只需将这两个文件描述符关闭即可,可以使用普通的close函数逐个关闭。
#include<unistd.h>
#include<errno.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
int pipe_fd[2];
if(pipe(pipe_fd<0))
{
printf(“pipe create error\n”)
return -1;
}
else
printf(“pipe create success\n”)
close(pipe_fd[0]);
close(pipe_fd[1]);
}
管道用于不同进程间通信。通常先创建一个管道,再通过fork函数创建一个子进程,该子进程会继承父进程所创建的管道。注意:必须在系统调用fork()前调用pipe(),否则子进程将不会继承文件描述符。
命名管道(FIFO)和无名管道基本相同,但也有不同点:无名管道只能由父子进程使用;但是通过命名管道,不相关的进程也能交换数据。
命名管道的创建:
#include<sys/types.h>
#include<sys/stat.h>
intmkfifo(const char* pathname,mode_t mode)
pathname:FIFO文件名;mode:一旦创建了一个FIFO,就可以用open打开它,一般的文件访问函数(close、read、write等)都可用于FIFO。
当打开命名管道FIFO时,非阻塞标志(O_NONBLOCK)将对以后的读写产生如下影响:
1.没有使用O_NONBLOCK:访问要求无法满足时进程将阻塞。如试图读取空的FIFO,将导致进程阻塞。
2.使用O_NONBLOCK:访问要求无法满足时不阻塞,立刻出错返回,errno是ENXIO;
信号通信
信号机制是unix系统中最为古老的进程间通信机制,很多条件可以产生一个信号:
1.当用户按下某些按键时产生信号。
2.硬件异常产生信号:除数为0、无效的存储访问等等。这些情况通常由硬件检测到,将其通知内核,然后内核产生适当的信号通知进程,例如,内核对正访问一个无效存储区的进程产生一个SIGSEGV信号。
3.进程用kill函数将信号发送给另一个进程。
4.用户可用kill命令将信号发送给其他进程。
几种常见的信号:SIGHUP:从终端上发出的结束信号;SIGINT:来自键盘的中断信号(ctrl-c);SIGKILL:该信号结束接收信号的进程;SIGTERM:kill命令发出的信号;SIGCHLD:标识子进程停止或结束的信号;SIGSTOP:来自键盘(ctrl-z)或调试程序的停止执行信号;
当信号出现时,将按照下列三种方式中的一种进行处理:
1. 忽略此信号:大多数信号都按照这种方式进行处理,但有两种信号却决不能被忽略。它们是:SIGKILL和SIGSTOP。
这两种信号不能被忽略的原因是:它们向超级用户提供了一种终止或停止进程的方法。
2.执行用户希望的动作:通知内核在某种信号发生时,调用一个用户函数。在用户函数中,执行用户希望的处理。
3.执行系统默认动作:对大多数信号的系统默认动作是终止该进程。
发送信号的主要函数有kill和raise;区别:kill既可以向自身发送信号,也可以向其他进程发送信号。与kill函数不同的是,raise函数是向进程自身发送信号。
#include<sys/rypes.h>
#include<signal.h>
intkill(pid_t pid,int signo)
intraise(int signo)
使用alarm函数可以设置一个时间值(闹钟时间),当所设置的时间到了时,产生SIGALRM信号。如果不捕捉此信号,则默认动作是终止该进程。
#include<unistd.h>
unsignedint alarm(unsigned int seconds)
seconds:经过了指定的seconds秒后会产生信号SIGALRM。
pause:pause函数使调用进程挂起直至捕捉到一个信号。
#include<unistd.h>
Intpause(void)
只有执行了一个信号处理函数后,挂起才结束。
信号的处理:当系统捕捉到某个信号时,可以忽略该信号或是使用指定的处理函数来处理该信号,或者使用系统默认的方式。
信号处理的主要方法有两种,一种是使用简单的signal函数,另一种是使用信号集函数组。
指定信号响应函数:
#include<signal.h>
Void(*signal(intsigno,void(*func)(int)))(int)
上一句可理解为:typedef void (*sighandler_t)(int)
Sighandler_t signal(int signum,sighandler_t handler)
Func可能的值是:1.SIG_IGN:忽略此信号2.SIG_DFL:按系统默认方式处理3.信号处理函数名:使用该函数处理
共享内存
/*你所浪费的今天,是昨天死去的人奢望的明天;你所厌恶的现在,是未来的你回不去的曾经!*/
共享内存是被多个进程共享的一部分物理内存。共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。
共享内存实现分为两个步骤:
一、创建共享内存,使用shmget函数。
二、映射共享内存,将这段创建的共享内存映射到具体的进程空间去,使用shmat函数。
创建共享内存:int shmget (key_t key,int size,int shmflg)
key标识共享内存的键值:0/IPC_PRIVATE。当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存;如果key的取值为0,而参数shmflg中又设置IPC_PRIVATE这个标识,则同样会创建一块新的共享内存。返回值:如果成功,返回共享内存标识符;如果失败,返回-1。
映射:int shmat(intshmid,char *shmaddr,int falg)
参数:shmid:shmget函数返回的共享存储标识符;flag:决定以什么方式来确定映射的地址(通常为0)
返回值:如果成功,则返回共享内存映射到进程中的地址;如果失败,则返回-1。
当一个进程不再需要共享内存时,需要把它从进程地址空间中脱离。
intshmdt(char *shmaddr)
消息队列
unix早期通信机制之一的信号能够传送的信息量有限,管道则只能传送无格式的字节流,这无疑会给应用程序开发带来不便。
消息队列(也叫做报文队列)则克服了这些缺点。
消息队列就是一个消息的链表。可以把消息看做一个记录,具有特定的格式。进程可以向中按照一定的规则添加新消息;另一些进程则可以从消息队列中读走消息。目前主要有两种类型的消息队列:POSIX(可移植的操作系统接口)消息队列以及系统V消息队列,系统V消息队列目前被大量使用。系统V消息队列是随内核持续的,只有在内核重起或者人工删除时,该消息队列才会被删除。消息队列的内核持续性要求每个消息队列都在系统范围内对应唯一的键值,所以,要获得一个消息队列的描述字,必须提供该消息队列的键值。
键值:
#include<sys/types.h>
#include<sys/ipc.h>
key_tftok (char *pathname,char proj)
功能:返回文件名对应的键值。pathname:文件名;proj:项目名(不为0即可);
获取消息队列描述字:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
intmsgget(key_t key,int msgflg)
key:键值,由ftok获得;msgflg:标志位;返回值:与键值key相对应的消息队列描述字。
标志位取值:IPC_CREAT创建新的消息队列;IPC_EXCL 与IPC_CREAT一同使用,表示如果要创建的消息队列已经存在,则返回错误;IPC_NOWAIT读写消息队列要求无法得到满足时,不阻塞。
在以下两种情况下,将创建一个新的消息队列:
1.如果没有与键值key相对应的消息队列,并且msgflg中包含了IPC_CREAT标志位。
2.key参数为IPC_PRIVATE。
发送消息:#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
intmsgsnd(int msqid,struct msgbuf *msgp,int msgsz,int msgflg)
功能:向消息队列中发送一条消息。msqid已打开的消息队列id;msgp存放消息的结构;msgsz消息数据长度;msgflg发送标志,有意义的msgflg标志为IPC_NOWAIT,指明在消息队列没有足够空间容纳要发送的消息时,msgsnd是否等待。
消息格式:struct msgbuf
{
long mtype; /*消息类型>0*/
char mtext[1];/*消息数据的首地址*/
};
接受消息:#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
intmsgrcv(int msqid,struct msgbuf *msgp,int msgsz,long msgtyp,int msgflg)
功能:从msqid代表的消息队列中读取一个msgtyp类型的消息,并把消息存储在msgp指向的msgbuf结构中。在成功地读取了一条消息以后,队列中的这条消息将被删除。
信号量
信号量(又名:信号灯)与其他进程间通信方式不大相同,主要用途是保护临界资源(只允许在一个时刻只有一个进程访问它)。进程可以根据它判定是否能够访问某些共享资源。除了用于访问控制外,还可用于进程同步。
二值信号灯:信号灯的值只能取0或1,类似于互斥锁。
但两者有不同:信号灯强调共享资源,只要共享资源可用,其他进程同样可以修改信号灯的值;
互斥锁更强调进程,占用资源的进程使用完资源后,必须由进程本身来解锁。
计数信号灯:信号灯的值可以取任意非负值。
创建/打开
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
intsemget(key_t key,int nsems,int semflg)
key:键值,由ftok获得;nsems:指定打开或者新创建的信号灯集中将包含信号灯的数目;semflg:标识,同消息队列;
intsemop(int semid,struct sembuf *sops,unsignednsops)
功能:对信号量进行控制。Semid:信号量集的ID;sops:是一个操作数组,表明要进行什么操作;nsops:sops所指向的数组的元素个数。
多线程程序设计
线程(thread)技术早在60年代就被提出,但真正应用多线程到操作系统中去,是在80年代中期,传统的unix也支持线程的概念,但是在一个进程中只允许有一个线程,这样多线程就意味着多进程。现在,多线程技术已经被许多操作系统所支持,包括windows/nt、linux。
为什么有了进程,还要引入线程呢?使用多线程到底有哪些好处?
1. 和进程相比,它是一种非常“节俭”的多任务操作方式。在linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种“昂贵”的多任务工作方式。运行于一个进程中的多个线程,它们之间使用相同的地址空间,而且线程间彼此切换所需的时间也远远小于进程切换所需要的时间。据统计,一个进程的开销大约是一个线程开销的30倍左右。
2. 线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过进程间通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。
Linux系统下的多线程遵循posix线程接口,称为pthread。编写linux下的多线程程序,需要使用投文件pthread.h,连接时需要使用库libpthread.a。
创建线程:#include<pthread.h>
intpthread_create(pthread_t *tidp,const pthread_attr_t *attr,void*(*start_rtn)(void),void *arg)
tidp:线程id;attr:线程属性(通常为空);start_rtn:线程要执行的函数;arg:start_rtn的参数;
编译:因为pthread的库不是linux系统的库,所以在进行编译的时候要加上-lpthread;
#gccfilename –lpthread
终止线程:如果进程中任何一个线程中调用exit或_exit,那么整个进程都会终止。
线程的正常退出方式有:
1.线程从启动例程中返回;
2.线程可以被另一个进程终止;
3.线程自己调用pthread_exit函数;
线程终止:#include<pthread.h>
Voidpthread_exit(void *rval_ptr)
功能:终止调用线程; rval_ptr:线程退出返回值的指针。
线程等待:#include<pthread.h>
intpthread_join(pthread_t tid,void **rval_ptr)
功能:阻塞调用线程,直到指定的线程终止。Tid:等待退出的线程id;rval_ptr:线程退出的返回值的指针。
线程标识:
#include<pthread.h>
pthread_tpthread_self(void)
功能:获取调用线程的thread identifier
清除:线程终止有两种情况:正常终止和非正常终止。
线程主动调用pthread_exit或者从线程函数中return都将使线程正常退出,这是可预见的退出方式;非正常终止是线程在其他线程的干预下,或者由于自身运行出错(比如访问非法地址)而退出,这种退出方式是不可预见的。不论是可预见的线程终止还是异常终止,都会存在资源释放的问题。
从pthread_cleanup_push的调用点到pthread_cleanup_pop之间的程序段中的终止动作(包括调用pthread_exit()和异常终止,不包括return)都将执行pthread_cleanup_push()所指定的清理函数。
#include<pthread.h>
Voidpthread_cleanup_push(void(*rtn)(void*),void *arg)
功能:将清除函数压入清除栈;rtn:清除函数;arg:清除函数的参数;
#include<pthread.h>
voidpthread_cleanup_pop(int execute)
功能:将清除函数弹出清除栈;参数:execute执行到pthread_cleanup_pop()时是否在弹出清理函数的同时执行该函数,非0:执行,0:不执行;
Linux网络概述
Linux为用户提供了完善的、强大的网络功能
1.完善的内置网络。其他操作系统不包含如此紧密地和内核结合在一起的网络部分。
2.linux免费提供了大量支持internet的软件,internet是在unix领域中建立并繁荣起来的,在这方面使用linux是相当方便的,用户能用linux与世界上其他人通过internet网络进行通信。
3.用户能通过一些linux命令完成内部信息或文件的传输。
4.远程访问。Linux不仅允许进行文件和程序的传输,它还为系统管理员和技术人员提供了访问其他系统的窗口。通过这种远程访问的功能,一位技术人员能够有效地为多个系统服务,即使那些系统位于相距很远的地方。
5.安全可靠。Linux采取了许多安全技术措施,包括对读、写进行权限控制、带保护的子系统、审计跟踪、核心授权等,这为网络多用户环境中的用户提供了必要的安全保障。
网络层协议
第一部分为网络层协议。主要包括internet协议(IP)、网际控制报文协议(ICMP)和地址解析协议(ARP):
Internet协议(IP):该协议被设计成互联分组交换通信网,以形成一个网际通信环境。它负责在源主机和目的地主机之间传输来自其较高层软件的称为数据报文的数据块,它在源和目的地之间提供非连接型传递服务。
网际控制报文协议(ICMP):它实际上不是IP层部分,但直接同IP层一起工作,报告网络上的某些出错情况。允许网际路由器传输差错信息或测试报文。
地址解析协议(ARP):ARP实际上不是网络层部分,它处于IP和数据链路层之间,它是在32位IP地址和48位物理地址之间执行翻译的协议。
传输层协议
第二部分是传输层协议,包括传输控制协议和用户数据报文协议。
传输控制协议(TCP):该协议对建立网络上用户进程之间的对话负责,它确保进程之间的可靠通信,所提供的功能如下:
1.监听输入对话建立请求
2.请求另一网络站点对话
3.可靠的发送和接收数据
4.适度的关闭对话
用户数据报文协议(UDP):UDP提供不可靠的非连接型传输层服务,它允许在源和目的地之间传送数据,而不必再传送数据之前建立对话。它主要用于那些非连接型的应用程序,如:视频点播。
应用层协议
这部分主要包括telnet,文件传送协议(FTP和TFTP),简单文件传送协议(SMTP)和域名服务(DNS)等协议。
IP协议
IP主要有以下四个主要功能:
数据传送、寻址、路由选择、数据报文的分段
IP的主要目的是为数据输入/输出网络提供基本算法,为高层协议提供无连接的传送服务。这意味着在IP将数据交给接收站点以前不在传输站点和接收站点之间建立对话。它只是封装和传递数据,但不向发送者或接收者报告包的状态,不处理所遇到的故障。IP包由IP协议头与协议数据两部分构成。
TCP协议
TCP是重要的传输层协议,目的是允许数据同网络上的其他节点进行可靠的交换。它能提供端口编号的译码,以识别主机的应用程序,而且完成数据的可靠传输。TCP协议具有严格的内装差错检验算法确保数据的完整性。TCP是面向字节的顺序协议,这意味着包内的每个字节被分配一个顺序编号,并分配给每包一个顺序编号。
UDP协议
UDP也是传输层协议,它是无连接的,不可靠的传输服务。当接收数据时它不向发送方提供确认信息,它不提供输入包的顺序,如果出现丢失包或重份包的情况,也不会向发送方发出差错报文。由于它执行功能时具有较低的开销,因而执行速度比TCP快。
Linux网络编程基础
socket:linux中的网络编程通过socket(套接字)实现,socket是一种文件描述符。
socket有三种类型:
1.流式套接字(SOCK_STREAM):流式套接字可以提供可靠的、面向连接的通讯流,它使用TCP协议。TCP保证了数据传输的正确性和顺序性。
2.数据报套接字(SOCK_DGRAM):数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠,无差错,它使用数据报协议UDP。
3.原始套接字(SOCK_RAW):原始套接字允许使用IP协议,主要用于新的网络协议的测试等。
在socket程序设计中,struct sockaddr用于记录网络地址:
struct sockaddr
{
u_short sa_family;
char sa_data[14];
}
sa_family:协议族,采用“AF_XXX”的形式,
如:AF_INET(IP协议族)。sa_data:14字节的特定协议地址。
在socket程序设计中,struct sockaddr_in 同样用于记录网络地址:
struct sockaddr_in
{
short int sin_family;/*协议族*/
unsigned short int sin_port;/*端口号*/
struct in_addr sin_addr;/*协议特定地址*/
unsigned char sin_zero[8];/*填0*/
}
编程中一般使用与sockaddr等价的sockaddr_in数据结构;
地址转换:IP地址通常由数字加点(192.168.0.1)的形式表示,而在struct in_addr中使用的IP地址是由32位的整数表示的,为了转换我们可以使用下面两个函数:
intinet_aton(const char *cp,struct in_addr *inp)
char*inet_ntoa(struct in_addr in)
函数里面a代表ascii,n代表network。inet_aton是将a.b.c.d形式的IP转换为32位的IP,存储在inp指针里面。inet_ntoa是将32位IP转换为a.b.c.d的格式。
字节序转换:不同类型的cpu对变量的字节存储顺序可能不同:有的系统是高位在前,低位在后,而又的系统是低位在前,高位在后,而网络传输的数据顺序是一定要统一的。所以当内部字节存储顺序和网络字节序(big endian)不同时,就一定要进行转换。
htons:把unsigned short类型从主机序转换到网络序;
htonl:把unsigned long类型从主机序转换到网络序;
ntohs:把unsigned short类型从网络序转换到主机序;
ntohl:把unsigned long类型从网络序转换到主机序;
IP与主机名:在网络中标识一台主机可以用IP地址,也可以使用主机名。
structhostent *gethostbyname(const char *hostname)
structhostent
{
char *h_name; /*主机的正式名称*/
char *h_aliases; /*主机的别名*/
int h_addrtype; /*主机的地址类型AF_INET*/
int h_length; /*主机的地址长度*/
char **h_addr_list;/*主机的第一个IP地址*/
}
#difineh_addr h_addr_list[0]/*主机的第一个IP地址*/
socket编程函数:
socket:创建一个socket;
bind:用于绑定IP地址和端口号到socket;
connect:该函数用于与服务器建立连接。
listen:设置服务器能处理的最大连接要求。
accept:用来等待来自客户端的socket连接请求。
send:发送数据;
recv:接收数据;