Linux网络编程技术学习笔记
时间:2010-09-02 来源:xuequansongmo
1.文件系统
Ext2 linux自身的文件系统
Msdos DOS文件系统
Vfat Windows95/98 vfat长文件名文件系统
iso9660 CDROM文件系统
|
Nfs 网络文件系统
Auto 系统自动判断文件系统类型
mount -t <fs_type><device><mount_point> 直接装载文件系统
umount<device>
umount<mount_point>
umount -a 卸下所有的文件系统
umount<fs_type> 卸下指定的文件系统
<device><mount_point>指定文件系统
2.进程控制块PCB
进程的静态表示为三个部分,程序部分,数据空间和进程控制块,将这三个统一称为进程映像
程序部分表述了进程所要完成的功能,是组成进程的主要部分,一个程序段可以被多个程序所共享,被共享的程序段应当被编写成纯代码
数据空间包含了程序段被执行时所需要的数据区和工作单元,并且是对应进程专用的.当进程使用的程序段不是纯代码时,将其作为数据的一部分放入数据空间
进程控制块PCB:每个进程都设有一个进程控制块,PCB是用来跟踪并记录动态变化的进程的各种调度信息的数据结构,它集中体现了进程动态变化的特征,进程的当前状态,进程和
其他进程的关系等.
3.操作系统是通过进程来完成一个个任务的,可以把进程看作在运行的程序。linux中每个进程都是由一个叫task_struct的结构体表示的.而task数组则包含了系统中只想每个task_struct
结构的指针,因此,一个系统的进程数目不能超过task进程的大小.缺省状态下有512项,当一个新进程被创建时,一个新的task_stuct从系统内存中分配出来,并在task数组中登记.
这里特殊约定,当前正在运行的进程其task_stuct由current指针指着.
当系统启动的时候,总有一个真正的进程.init进程,它的pid号为1,做了一些初始化工作,即启动init核心线程.其他所有的进程都是由这个进程通过fork系统调用创建的.
4. fork()调用的作用是使调用fork()的进程变成新创建进程的父进程.
一旦进程已经被创建,则父进程和子进程都成fork()调用内部继续执行,这意味着两个进程的下一个动作是带有它的返回值从fork()返回.
如果fork()确实返回一个0值,则它必须给新的子进程.如果它返回一个非零值,则它必须是返回给父进程的子进程的进程识别号(PID)(或者在错误的时候,返回-1).
fork系统调用创建子进程的做法,是把自己复制给子进程,就是说,新创建的进程是父进程的一个副本.继而子进程通过exec系统调用,用一个新的程序来覆盖子进程的内存空间,
从而执行那个新程序.系统调用exit可以终止一个进程的执行.子进程也常常用exit系统调用来自我终止,子进程终止之后,进入僵死(zombie)状态,父进程可通过执行wait系统调用来实现
与子进程的终止同步,接受子进程的返回状态和返回参数.
parent----------------(wait)---------------Parent
|fork |
| |
child----(exec)---->child-----(exit)------>zomb
5.linux系统调用简介
linux系统提供的系统调用借口大部分是C语言函数接口.
在操作系统中的每一个app(应用程序)或用户程序都必须依靠系统调用来完成对硬件的操作和对一些基本操作的实现.在linux中使用中断机制来实现系统调用,专门给操作系统的系统调
用一个中断0*80,从而保证了系统的高效性
6.exec系统调用
用户使用shell运行一个条命令时,则在某一时刻,shell将执行fork()调用使一个新进程运行.做完后,shell使用exec()调用来使ls命令在新进程中运行以代替在fork()调用后立即运行shell的副
本.由若干不同形式的exec()调用,都执行同样的任务.在linux系统中使用exec()系统调用是使程序执行的唯一方法.具体做法是:在使用exec()系统调用时,将程序文件的名字作为参数传递给
exec().然后它将包含在程序文件中的数据段来代替执行exec()调用的数据段.
exec()调用:
当系统从shell执行一个程序时,需要在命令行中为该程序指定参数和开关.从C语言的知识,你也知道这些命令的值是通过mian函数的参数argc和argv日共给程序使用嗯.shell需要能
以某种方式取命令行的值,并且把他们作为argc和argv,传递给运行的程序.这一任务由exec()调用来完成.将命令行的值以适当的值以适当的形式传递给它,他将安排他们作为将要运
行的新程序的argc和argv.
argc是参数个数.是各个参数字符串指针数组,envp是新进程的环境变量字符串的指针数组.argc至少为1,argv[0]为程序文件名.所以int execve(path,argv,envp)中的exec系统调用族中
path为新进程文件的路径名,file为新进程文件明.若file不是全路径名,系统调用组按PATH环境变量自动找对应的可执行文件运行.若新进程文件不是一个可执行的目标文件(如批处理文件)
则execlp和execvp会将文件哦你容作为一个命令解释器的标准输.Argv[0]等指针只想'\0'结束的字符串,组成新进程的有效参数,且该参数列表以一个空指针结束.反过来,arg0至少必须存在
并指向新进程个文件名活路径名.同样,argv是字符串指针数组,以空指针结束.这些字符串组策划嗯新进程的环境.在调用这些系统调用前打开的文件指针对新进程来说也是打开的,除非它
已定义了close-on-exec标志.打开的文件指针在新进程中保持不变,所有相关的文件锁也被保留.调用进程设置并正被捕俘的信号在新进程中被恢复为缺省这只,其他的则保持不变.新进程
启动时按文件的SUID和SGID设置定义文件的UID和GID为有效UID和UID和GID.
调用成功后,系统调用修改新进程文件的最新访问时间
例子:
#include<stdio.h>
main()
{
execl("/bin/ls","ls","-l",0);
printf("Can onlu get here on error\n");
}
在这个例子中,execl()的第一个参数是ls命令的完整参数路径名,假如进程对该文件有执行权限,这一文件内容将被执行.新程序的argv数组元素所只想字符串将由excl()的其余参数提供.
argv[0]元素所指向的字符串ls和由它argv[1]元素所指向的字符串-l
通常exec()调用并不返回,因为它的功能是用某个其他程序的数据段代替调用他们的进程数据段.
但是如果对exec()调用由于任何原因而失效,则它们将返回,使用户能有机会对错误进程处理.
用户希望一个运行的进程组织另一个程序的执行作为它的操作的一部分时,步骤:
第一步:运行的进程取得或产生要执行的命令
第二步:运行的进程执行fork()系统调用.它启动一个新进程,使原来进程的拷贝,并且与原来的进程并发的执行.原来的进程称为父进程,新进程称为子进程
第三步:现在的子进程执行exec()调用.它去掉子进程的用户数据段并且将要运行的在命令文件中的用户数据段代替它们.
出现问题:首先,用父进程的完整复制品来创建子进程,仅仅是为了立即去掉它并用新程序代替它是否是一种浪费?其次,当子进程正在执行时,父进程又在干什么?
linux()以一种非常有效的方式实现的它的fork()系统调用.事实上,linux并没有复制进程的完整拷贝,它只用两组指针,每个进程对应一组,全部只想同一实际的数据段.虽然父进程和子进程是
一对独立的进程,但只要两个进程仅仅从数据段读数据而不写任何新值,就不会发生问题.当两个进程之一企图将数据写道数据段之一时,能发现它,并且按需要对数据段的这一区域进行复制
这一技术被称为在写时复制(copy on write).
从原则上讲,由任何一个进程写的任何的区域将得到复制,是的两个进程都有他们自己的这些区域的副本.如果子进程立即执行exec()调用,则在执行exec()调用之前只有少量的共享数据段
被复制,因而大量节省了时间和资源
7.system函数调用
函数system功能是产生一个新的进程,子进程执行指定的命令.
#include<stdio.h>
#include<stdib.h>
int system(string)
|
char *string;
说明:将参数string传递给一个命令解释器执行,即string被解释为一条命令,由shell执行该命令.若参数string为一个空指针则为检查命令解释器是否存在.
8.wait函数调用
其功能是等待子进程返回并修改状态,实际上提供一种父进程和子进程间同步的方式.返回值为该子进程退出的PID.
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(stat_loc)
int *stat_loc;
//父进程
if(fork()>0)
{
wait((int *)0);
//父进程等待子进程的返回
}
else{
//子进程处理过程
exit();
}
wait调用实际上是系统核心在循环检测task_struct表,如果发现当前进程有一个僵尸状态的子进程,系统核心将子进程在exit中没有释放的task_struct结构释放,并将子进程退出的状态码作
为wait调用的返回值
8.waitpid函数调用
等待指定进程号的子进程的返回并修改状态
#include<sys/types.h>
#include<sys/wait.h>
pid_t waitpid(pid,stat_loc,options)
pid_t pid;
int *stat_loc,options;
当pid等于-d,options等于0时,该系统调用等同于wait调用,否则系统调用的行为由参数pid和option决定.pid指定了一组父进程要求知道其状态的子进程
-1:要求知道任何一个子进程的返回状态;
>0:要求知道进程号为pid值的子进程的状态.
<-1:要求知道进程组号为pid的绝对值的子进程的状态.
9.setpgrp函数调用
设置进程组号和会话号.若调用进程不是会话首进程,该函数调用将进程组号和会话号都设置为与它的进程号相等,并释放调用进程的控制终端
10.signal函数调用
具有 信号管理功能
11.kill函数调用
向一个或一组进程发送一个信号
#include<sys/type.h>
#include<singal.h>
int kill(pidsig);
pid_t pid;
int sig;
这个调用向一个或一组进程发送一个信号,该信号由参数sig指定,为系统给出的信号表中的一个.
参数pid指定将要被发送信号的进程或进程组.pid若大于0,则信号将被发送到进程号等于pid的进程;若pid等于0,则信号将被发送到所有的与发送信号进程同在一个进程组的进程(系统的特
殊进程除外);若pid小于-1,则信号将发送到所有进程组号于pid绝对值相同的进程;若pid等于-1,则信号将被发送到所有的进程
kill调用成功则返回0,否则返回-1;
12.alarm系统调用
#include<unistd.h>
long alarm(long secs);
secs是在闹钟关闭之前所经过的秒数.如果传递一个0值给alarm函数,这将关闭任何当前正在运行的闹钟计时器.alarm函数的返回值是以前的闹钟计时器.如果当前没有设置任何闹钟计时器
,这将是0,或者是当作出该调用时,闹钟的剩余时间.
13.exit函数调用
#include<stdlib.h>
void exit(status)
int status;
在exit调用中,并没有释放task_struct结构,这是exit调用后进程唯一保留的资源,系统只是通过task_struct结构才能知道进程的存在,这个结构将保留给wait使用
14.linux系统中有三种进程,即核心进程,用户进程和守护进程,核心进程是进程swapd,这个进程只在核心才运行,并在整个系统活动期间生存,不会被终止.
用户进程一般是由用户创建的进程,init也是用户进程,但它有系统创建,在系统运行期间不能被用户终止
有几种启动守护进程的方法,
(1)引导系统时启动
这是运行的守护进程通常在系统启动scrip的执行其间被启动,这些scrip典型的被存放在目录/etc/rc.d中.
(2)手工从shell提示符启动
对任何具有相应的执行权限的用户可以用这种方法启动守护进程
(3)由crond守护进程启动
这个程序查询通常放在/var/spool/cron/crontabs目录的一组文件,他们规定了要执行的周期性任务
(4)由执行at命令启动
这将是一个程序在规定的日期和时间执行一次
15.域名
com 用于商业公司
org 用于组织,协会等
net 用于网络服务
edu 用于教育机构
gov 用于政府部门
mil 用于军事领域
名称服务器分三类:
主名称服务器
次名称服务器:定期由主名称服务器复制一份权限区文件
快速名称服务器:他不建立或维护特定权限区的名称数据.当用户向这类主机发出询问时,它首先搜索自己的缓冲区,如果未发现匹配的地址,则代替用户询问其他名称服务器.当得到
结果后,快速名称服务器在把结构返回给用户的同时,也将其保存在自己的缓冲区
16.通过域名获得IP地址
#include <netdb.h>
struct hostent *gethostbyname(const char *hostname) //指定主机的域名地址来获取主机的IP地址的信息
如果函数查询成功,则返回一个包含主机IP地址的struct hostent结构指针,否则返回空指针
17.通过IP地址获取域名信息
函数gethostbyaddr可以查询指定的IP地址对应的主机域名,它可以将一个32位的IP地址(C0A80001)转换为结构指针
#include<netdb.h>
struct hostent *gethostbyaddr(const *addr,size_t len,int type);
参数addr是只想in_addr结构的指针,它包含IP地址.len是IP地址的长度,对于Ipv4,长度为4字节,type是使用的协议族(AF_INET)
18.通过服务名获取端口号
函数getservbyname查找一个服务名对应的端口号
#include<netdb.h>
struct servent *getervbyname(const char *servname,const char *protoname);
参数servname指定要查找的服务名,参数protoname指定查找哪种协议的服务名,
调用函数getervbyname时,必须指定服务名
如果一种服务只有一种协议支持(如HTTP服务只有TCP协议支持),则参数protoname可以设置为空指针NULL,如果有多种协议支持同一种服务(如DNS服务,UDP和TCP协议都支持),那么我
们可以指定协议名,来查找特定协议的服务,可以将参数protoname设置为空指针NULL,因为不同协议的相同服务一般使用相同的端口号
19.通过端口号获取服务名
使用函数getservbyport函数通过端口号来反向的查询服务名称
#include<netdb.h>
struct ervent *getservbyport(int port,const char protoname);
因端口号514在TCP协议中提供的是Shell服务,而在UDP协议中提供的是Syslog服务,所以我们在编程时应该指定参数protoname的值