2019-01-29 16:14:06 Swallow_he 阅读数 557
  • 数组&字符串&结构体&共用&枚举-C语言专题第5部分

    本课程综合讲解了数组、字符串、字符数组、结构体定义及使用、结构体对齐、复杂结构体结合指针、共用体定义及使用、大小端模式、枚举常量及其与宏定义的关联。通过本部分共15节课的理论讲解加代码实战,希望大家能够对以上知识点有更深入的理解。

    13811 人正在学习 去看看 朱有鹏

1.当我们想发送一个结构体给服务端时,如果该结构体是字节对齐,那么无需考虑大小端的转换

比如:

typedef struct OpenMessage
{
    int32_t  SessionType;
    int32_t  SessionId;
    int64_t  TimeStamp;

};

OpenMessage  kmessage;给结构体赋值后,char * sendbuffer = (char *)&kmessage;

2、UDP客户端/服务器端

当我们UDP定时(2s)给服务端发送数据,服务端收到数据后将此数据再发送给客户端,客户端接收,因此客户端和服务器端都会使用sendto和recvfrom,但是客户端程序当我们在一个主线程中使用sendto和recvfrom时,会出现recvfrom被阻塞,客户端没有周到数据也无法再发送数据。

UDPServer.cpp

#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <strings.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

int UDPServer()
{
	int sock;
	struct sockaddr_in toAddr;
	struct sockaddr_in fromAddr;

	sock = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);

	if(sock < 0)
	{
		perror("socket!");
		return -1;
	}

	memset(&fromAddr,0,sizeof(fromAddr));
	fromAddr.sin_family=AF_INET;
	fromAddr.sin_addr.s_addr=inet_addr("0.0.0.0");
	fromAddr.sin_port = htons(7861);

	if(bind(sock,(struct sockaddr*)&fromAddr,sizeof(fromAddr))<0)
	{
		perror("bind!");
		close(sock);
		return -1;
	}
       int count =0;
       int recvLen;
       unsigned int addrLen;
       char recvBuffer[1024]={0};
       while(1)
      {
	    count ++;
	    addrLen = sizeof(toAddr);   //client
	    recvLen = recvfrom(sock,recvBuffer,1024,0,(struct sockaddr*)&toAddr,&addrLen);
	   if(recvLen>0)
	  {
	    recvBuffer[recvLen]= 0;
	    printf("recv size:%2d,count = %2d\n",recvLen,count);
	  
	    int ret =0;
	   ret = sendto(sock,recvBuffer,recvLen,0,(struct sockaddr*)&toAddr,sizeof(toAddr));
	   if(ret!=recvLen)
	   {
		 perror("sendto");		
	   } 
	
	 }
	 else 
         {
           sleep(1);
	 }
     }
     close(sock);
     return 0;
}

int main(int argc, char *argv[])
{
	UDPServer();
	return 0;
}

UDPClient.cpp

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

typedef struct message
{
	int32_t  SessionType;
	int32_t  SessionId;
	int64_t  TimeStamp;

};

int main()
{
	
    int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(socket_fd == -1)
    {
        perror("udp_socket");
        return -1;
    }

   struct sockaddr_in addr;
   addr.sin_family = AF_INET;
   addr.sin_addr.s_addr = inet_addr("192.168.154.201");
   addr.sin_port = htons(4000);  
   if (addr.sin_addr.s_addr == INADDR_NONE)
       {
           printf("Incorrect ip address!");
           close(socket_fd);
           return -1;
       }
   char buff[1024];
   int sessionId =0;
   socklen_t len = sizeof(addr);
   message sendmessages;
   while (1)
       {
  
	   sleep(2);
           sessionId ++ ;
	   sendmessages.SessionType = 1;
	   sendmessages.SessionId = sessionId;
	   sendmessages.TimeStamp = 9999;
        
	   printf("client send:%d ,%d ,%lld \n", sendmessages.SessionType, sendmessages.SessionId, sendmessages.TimeStamp);
	   char * sendbuffer = (char *)&sendmessages;
           int n;
           n = sendto(socket_fd, sendbuffer, sizeof(sendmessages), 0, (struct sockaddr *)&addr, sizeof(addr));
           if (n < 0)
           {
               perror("sendto !");
           }
           memset(buff,0,sizeof (buff));
           n = recvfrom(socket_fd, buff, 1024, 0, (struct sockaddr *)&addr, &len);
           if (n>0)
           {
                message recv;
	        memcpy(&recv, buff, sizeof(message)+1);	   
                buff[n] = 0;
                printf("client recv:%d ,%d ,%lld \n", recv.SessionType, recv.SessionId, recv.TimeStamp);
            
           }
           else 
           {
               printf("recvfrom error!\n");
              
           }
    close(socket_fd);
    return 0;
}

如果想长时间客户端和服务器端交互,那么客户端出现被阻塞情况,一直收不到数据,也没有动力再去发送数据!!!

因为网络的问题,经常丢包,也就是发了之后没有响应。这样的话,recvfrom会一直停在那里,死机了一样。

 

2016-08-25 17:54:08 u012177034 阅读数 2074
  • 数组&字符串&结构体&共用&枚举-C语言专题第5部分

    本课程综合讲解了数组、字符串、字符数组、结构体定义及使用、结构体对齐、复杂结构体结合指针、共用体定义及使用、大小端模式、枚举常量及其与宏定义的关联。通过本部分共15节课的理论讲解加代码实战,希望大家能够对以上知识点有更深入的理解。

    13811 人正在学习 去看看 朱有鹏

数据类型占用字节数

首先强调,不同数据类型的内存占用大小不固定,与编译器有关,与CPU的位数和操作系统的位数无关。但编译器仍然受CPU的字长影响。具体常用的标准如下:

type 32字长 64字长
char 8 8
short 16 16
int 32 32
long 64 64
pointer 32 64

对于16位或者8位的单片机而言有的可能采用16位作为int

大小端模式

定义

小端模式:数据的高字节保存在内存的高地址中,低字节保存在内存的低地址中
大端模式:数据的高字节保存在低地址中,低字节保存在高地址中
例如对于占4个字节的int数据0x12345678来说,其大小端的存储模式如下图

记忆:“小端低低”~
intel的CPU为小端模式,大部分的arm,DSP也为小端模式,有的ARM支持硬件选择大小端模式

大小端存储示意

如何测试编译器是大/小端

考虑如下代码

#include <stdio.h>
int main()
{
  short int x;
  char x0,x1;
  x = 0x1122;
  x0 = ((char *)&x)[0];//低地址单元
  x1 = ((char *)&x)[1];//高地址单元
  if(x0 = 0x22)
    printf("Little Endian\n");
  else
    printf("Big Endian\n");
  return 0;
}

需要考虑大小端问题的场合

1.所写程序需要向不同的硬件平台迁移,迁移平台可能是大端也可能是小端。在编程之前,为了保证可移植性,需要事先考虑。

2.不同类型机器之间通过网络传送二进制数据时。字符串通信时不需这种情况。

数据类型自动转换

自动转换主要发生在不同类型的数据进行的混合运算中。遵循以下规则:
(1)若参与运算的类型不一致,先全部转为同一类型,然后进行运算
(2)转换按照数据长度增加的方向进行,以保证不降低精度
(3)short与char在参与运算时全部都要先自动转换成int类型或者更高的类型(不论是否由变量的混合)
(4)赋值运算时默认将右边类型转为左边类型,精度可能降低,float转int遵循去掉小数点,不是四舍五入

char,short -> int -> unsigned -> long -> double
float -> double

结构体内存占用

考虑以下结构体在64位编译器上占用的内存大小

#pragma pack(4)
//PPB = 4
struct{
int ld;
//4个字节,align = min(4,PPB)=4,offset = 0,occupy 0~3 bytes
char color[5];
//1个字节,align = min(1,PPB)=1,offset = 4,occupy 4~8 bytes
unsigned short age;
//2个字节,align = min(2,PPB)=2,offset = 10,occupy 10~11 bytes
char *name;
//8个字节,align = min(8,PPB)=4,offset = 8,occupy 12~19 bytes
void (*jump)(void);
//8个字节,align = min(8,PPB)=4,occupy 20~27 bytes
}Garfield;

#pragma pack()

由上面的分析可得该结构体内容占用空间为28字节。由于28字节正好是min(PPB,sizeof(char *))的整数倍,因此最终的结构体占用空间也为28字节。

综上,结构体内存对齐原则:
(1)结构体中的每个元素都认为内存是以它自己的大小来划分的,因此元素的偏移位置一定是在自己宽度的整数倍上开始。(首变量偏移为0)
(2)在按照(1)中放置后,结构体的整体大小需要为结构体中宽度最大的变量的宽度的整数倍。不满足则需补齐。
(3)若使用#pragma pack(PPB)指令对齐,则(1)中的个元素的偏移位置为PPB与本身宽度的较小值。原则(2)中的宽度最大变量的宽度也要换为min(PPB,宽度最大变量的宽度)。

2018-06-20 16:15:32 hhhanpan 阅读数 72
  • 数组&字符串&结构体&共用&枚举-C语言专题第5部分

    本课程综合讲解了数组、字符串、字符数组、结构体定义及使用、结构体对齐、复杂结构体结合指针、共用体定义及使用、大小端模式、枚举常量及其与宏定义的关联。通过本部分共15节课的理论讲解加代码实战,希望大家能够对以上知识点有更深入的理解。

    13811 人正在学习 去看看 朱有鹏

1.页

    Linux的内核把物理页作为内存管理的基本单位。而视窗操作系统中的基本单位是进程和线程,与Linux的不同。

    内存管理单元(MMU):管理内存并把虚拟地址转换为物理地址的硬件。

MMU通常以页为单位来管理系统中的页表。当然,页的大小在不同的体系结构下是不同的0.32位一般支持4KB的页,而64位支持8KB的页。

    Linux内核中用struct page结构体类型表示系统中的物理页,该结构位于<linux \ include \ Mm.h>中,

struct page {
	/**
	 * 一组标志,也对页框所在的管理区进行编号
	 * 在不支持NUMA的机器上,flags中字段中管理索引占两位,节点索引占一位。
	 * 在支持NUMA的32位机器上,flags中管理索引占用两位。节点数目占6位。
	 * 在支持NUMA的64位机器上,64位的flags字段中,管理区索引占用两位,节点数目占用10位。
	 */
	page_flags_t flags;		/* Atomic flags, some possibly
					 * updated asynchronously */
	/**
	 * 页框的引用计数。当小于0表示没有人使用。
	 * Page_count返回_count+1表示正在使用的人数。
	 */
	atomic_t _count;		/* Usage count, see below. */
	/**
	 * 页框中的页表项数目(没有则为-1)
	 *		-1:		表示没有页表项引用该页框。
	 *		0:		表明页是非共享的。
	 *		>0:		表示而是共享共享的。
	 */
	atomic_t _mapcount;		/* Count of ptes mapped in mms,
					 * to show when page is mapped
					 * & limit reverse map searches.
					 */
	/**
	 * 可用于正在使用页的内核成分(如在缓冲页的情况下,它是一个缓冲器头指针。)
	 * 如果页是空闲的,则该字段由伙伴系统使用。
	 * 当用于伙伴系统时,如果该页是一个2^k的空闲页块的第一个页,那么它的值就是k.
	 * 这样,伙伴系统可以查找相邻的伙伴,以确定是否可以将空闲块合并成2^(k+1)大小的空闲块。
	 */
	unsigned long private;		/* Mapping-private opaque data:
					 * usually used for buffer_heads
					 * if PagePrivate set; used for
					 * swp_entry_t if PageSwapCache
					 * When page is free, this indicates
					 * order in the buddy system.
					 */
	/**
	 * 当页被插入页高速缓存时使用或者当页属于匿名页时使用)。
	 * 		如果mapping字段为空,则该页属于交换高速缓存。
	 *		如果mapping字段不为空,且最低位为1,表示该页为匿名页。同时该字段中存放的是指向anon_vma描述符的指针。
	 *		如果mapping字段不为空,且最低位为0,表示该页为映射页。同时该字段指向对应文件的address_space对象。
	 */
	struct address_space *mapping;	/* If low bit clear, points to
					 * inode address_space, or NULL.
					 * If page mapped as anonymous
					 * memory, low bit is set, and
					 * it points to anon_vma object:
					 * see PAGE_MAPPING_ANON below.
					 */
	/**
	 * 作为不同的含义被几种内核成分使用。
	 * 在页磁盘映象或匿名区中表示存放在页框中的数据的位置。
	 * 或者它存放在一个换出页标志符。
	 */
	pgoff_t index;			/* Our offset within mapping. */
	/**
	 * 包含页的最近最少使用的双向链表的指针。
	 */
	struct list_head lru;		/* Pageout list, eg. active_list
					 * protected by zone->lru_lock !
					 */
	/*
	 * On machines where all RAM is mapped into kernel address space,
	 * we can simply calculate the virtual address. On machines with
	 * highmem some memory is mapped into kernel virtual memory
	 * dynamically, so we need a place to store that address.
	 * Note that this field could be 16 bits on x86 ... ;)
	 *
	 * Architectures with slow multiplication can define
	 * WANT_PAGE_VIRTUAL in asm/page.h
	 */
#if defined(WANT_PAGE_VIRTUAL)
	/**
	 * 如果进行了内存映射,就是虚拟地址。对存在高端内存的系统来说有意义。
	 */
	void *virtual;			/* Kernel virtual address (NULL if
					   not kmapped, ie. highmem) */
#endif /* WANT_PAGE_VIRTUAL */
};

· flag域:用于存放页的状态,包括页是不是脏的,是不被被锁定在内存中等。每一位单独表示一种状态,可表示32种不同状态。

· _count域存放页面的引用计数。当_count为-1​​时,说明当前内核并没有引用这一页。通过调用page_count()函数检查该域名。

·虚拟域是页的虚拟地址,也就是页在虚拟内存中的地址。·高端内存并不永久地映射到内核地址空间上,这时虚拟域的值为NULL。


2.区

    一些硬件由于存在缺陷而引起的内存寻址问题:

·一些硬件只能用某些特定的内存地址来执行DMA(直接内存访问)。

·一些体系结构的内存的物理寻址范围比虚拟寻址范围大,就会有一些内存不能永久地映射到内核空间上

    Linux的的为解决这些问题,使用了如下四种区:

·ZONE_DMA - 这个区包含的页用来执行DMA操作(进行直接内存访问)。物理内存范围为<16MB

·ZONE_DMA32 - 和ZONE_DMA类似,该区包含的页面可执行DMA操作;不同之处在于这些页面只能被32位设备访问,比ZONE_DMA更大。

·ZONE_NORMAL - 能正常映射的页,即正常柯林斯寻址的页物理内存范围的英文。16〜896MB

·ZONE_HIGHMEM -这个区包含“ 高端内存 “,动态映射的页。    linux32镜像镜像位下映射高于1G的内存,在64位上没有该区。物理内存范围大于896MB


    Linux的的把系统的页划分为区,形成不同的内存池,根据用途进行分配。但是,分配不能跨区界限的。比如,尽管用于DMA的内存必须从DMA中进行分配,但是一般用途的内存既能从ZONE_DMA中分配,也能从ZONE_NORMAL分配。

    Inter x86-64体系结构可以映射和处理64位的内存空间,所以X86-64没有ZONE-HIGHMEM区,所有物理内存都处于ZONE_NORMAL和ZONE_DMA中。


/**
 * 包含低16MB的内存页框。
 */
#define ZONE_DMA		0
/**
 * 包含高于16MB且低于896MB的内存页框。
 */
#define ZONE_NORMAL		1
/**
 * 包含从896MB开始高于896MB的内存页框。
 */
#define ZONE_HIGHMEM		2

管理内存区描述符结构区

struct zone {
	/* Fields commonly accessed by the page allocator */
	/**
	 * 管理区中空闲页的数目
	 */
	unsigned long		free_pages;
	/**
	 * Pages_min-管理区中保留页的数目
	 * Page_low-回收页框使用的下界。同时也被管理区分配器为作为阈值使用。
	 * pages_high-回收页框使用的上界,同时也被管理区分配器作为阈值使用。
	 */
	unsigned long		pages_min, pages_low, pages_high;
	/**
	 * 为内存不足保留的页框
	 */
	unsigned long		lowmem_reserve[MAX_NR_ZONES];
	/**
	 * 用于实现单一页框的特殊高速缓存。
	 * 每CPU、每内存管理区都有一个。包含热高速缓存和冷高速缓存。
	 */
	struct per_cpu_pageset	pageset[NR_CPUS];

	/*
	 * free areas of different sizes
	 */
	/**
	 * 保护该描述符的自旋锁
	 */
	spinlock_t		lock;
	struct free_area	free_area[MAX_ORDER];
	ZONE_PADDING(_pad1_)

	/* Fields commonly accessed by the page reclaim scanner */
	/**
	 * 活动以及非活动链表使用的自旋锁。
	 */
	spinlock_t		lru_lock;	
	/**
	 * 管理区中的活动页链表
	 */
	struct list_head	active_list;
	/**
	 * 管理区中的非活动页链表。
	 */
	struct list_head	inactive_list;
	/**
	 * 回收内存时需要扫描的活动页数。
	 */
	unsigned long		nr_scan_active;
	/**
	 * 回收内存时需要扫描的非活动页数目
	 */
	unsigned long		nr_scan_inactive;
	/**
	 * 管理区的活动链表上的页数目。
	 */
	unsigned long		nr_active;
	/**
	 * 管理区的非活动链表上的页数目。
	 */
	unsigned long		nr_inactive;
	/**
	 * 管理区内回收页框时使用的计数器。
	 */
	unsigned long		pages_scanned;	   /* since last reclaim */
	/**
	 * 在管理区中填满不可回收页时此标志被置位
	 */
	int			all_unreclaimable; /* All pages pinned */
	/**
	 * 临时管理区的优先级。
	 */
	int temp_priority;
	/**
	 * 管理区优先级,范围在12和0之间。
	 */
	int prev_priority;


	ZONE_PADDING(_pad2_)
	/**
	 * 进程等待队列的散列表。这些进程正在等待管理区中的某页。
	 */
	wait_queue_head_t	* wait_table;
	/**
	 * 等待队列散列表的大小。
	 */
	unsigned long		wait_table_size;
	/**
	 * 等待队列散列表数组的大小。值为2^order
	 */
	unsigned long		wait_table_bits;

	/*
	 * Discontig memory support fields.
	 */
	/**
	 * 内存节点。
	 */
	struct pglist_data	*zone_pgdat;
	/** 
	 * 指向管理区的第一个页描述符的指针。这个指针是数组mem_map的一个元素。
	 */
	struct page		*zone_mem_map;
	/* zone_start_pfn == zone_start_paddr >> PAGE_SHIFT */
	/**
	 * 管理区的第一个页框的下标。
	 */
	unsigned long		zone_start_pfn;

	/**
	 * 以页为单位的管理区的总大小,包含空洞。
	 */
	unsigned long		spanned_pages;	/* total size, including holes */
	/**
	 * 以页为单位的管理区的总大小,不包含空洞。
	 */
	unsigned long		present_pages;	/* amount of memory (excluding holes) */

	/*
	 * rarely used fields:
	 */
	/**
	 * 指针指向管理区的传统名称:DMA、NORMAL、HighMem
	 */
	char			*name;
} ____cacheline_maxaligned_in_smp;

    其中,锁定域是一个自旋锁,防止该结构被并发访问。(该域只保护结构,不保护驻留在这个区的所有页)。而且没有特定的锁保护单个页。


3.获得页

alloc_pages()

    请求分配一组连续页框,这是管理区分配器的核心。

    该函数分配2 ^ order个连续的物理页,并返回一个指针,该指针指向第一个页面的结构体;如果出错返回NULL。

而函数alloc_page()只分配一页,返回指向页结构的指针

· gfp_mask:在内存分配请求中指定的标志。

· order:连续分配的页框数量的对数(2 ^ order个)

· zonelist:zonelist数据结构的指针。该结构按优先次序描述了适用于内存分配的内存管理区


/**
 * 分配2^order个连续的页框。它返回第一个所分配页框描述符的地址或者返回NULL
 */
static inline struct page *
alloc_pages(unsigned int gfp_mask, unsigned int order)
{
	if (unlikely(order >= MAX_ORDER))
		return NULL;

	return alloc_pages_current(gfp_mask, order);
}

在alloc_pages_current()中调用__alloc_pages(),

struct page * fastcall
__alloc_pages(unsigned int gfp_mask, unsigned int order,
		struct zonelist *zonelist)
{
	const int wait = gfp_mask & __GFP_WAIT;
	struct zone **zones, *z;
	struct page *page;
	struct reclaim_state reclaim_state;
	struct task_struct *p = current;
	int i;
	int classzone_idx;
	int do_retry;
	int can_try_harder;
	int did_some_progress;

	might_sleep_if(wait);


	can_try_harder = (unlikely(rt_task(p)) && !in_interrupt()) || !wait;

	zones = zonelist->zones;  /* the list of zones suitable for gfp_mask */

	if (unlikely(zones[0] == NULL)) {
		/* Should this ever happen?? */
		return NULL;
	}

	classzone_idx = zone_idx(zones[0]);

 restart:
	/* Go through the zonelist once, looking for a zone with enough free */
	/**
 	 * 扫描包含在zonelist数据结构中的每个内存管理区
	 */
	for (i = 0; (z = zones[i]) != NULL; i++) {

    执行对内存管理区的第二次扫描,将值Z-> pages_min作为阀值传入。

	for (i = 0; (z = zones[i]) != NULL; i++)
		wakeup_kswapd(z, order);

	/*
	 * Go through the zonelist again. Let __GFP_HIGH and allocations
	 * coming from realtime tasks to go deeper into reserves
	 */
	/**
	 * 执行对内存管理区的第二次扫描,将值z->pages_min作为阀值传入。这个值已经在上一步的基础上降低了。
	 * 当然,实际的min值还是要由can_try_harder和gfp_high确定。z->pages_min仅仅是一个参考值而已。
	 */
	for (i = 0; (z = zones[i]) != NULL; i++) {
		if (!zone_watermark_ok(z, order, z->pages_min,
				       classzone_idx, can_try_harder,
				       gfp_mask & __GFP_HIGH))
			continue;

		page = buffered_rmqueue(z, order, gfp_mask);
		if (page)
			goto got_pg;
	}

     如果产生内存分配的内核控制路径不是一个中断处理程序或者可延迟函数,并且它试图回收页框(PF_MEMALLOC,TIF_MEMDIE标志被置位),那么才对内存管理区进行第三次扫描。

	 if (((p->flags & PF_MEMALLOC) || unlikely(test_thread_flag(TIF_MEMDIE))) && !in_interrupt()) {
		/* go through the zonelist yet again, ignoring mins */
		for (i = 0; (z = zones[i]) != NULL; i++) {
			/**
			 * 本次扫描就不调用zone_watermark_ok,它忽略阀值,这样才能从预留的页中分配页。
			 * 允许这样做,因为是这个进程想要归还页框,那就暂借一点给它吧(呵呵,舍不得孩子套不到狼)。
			 */
			page = buffered_rmqueue(z, order, gfp_mask);
			if (page)
				goto got_pg;
		}

		/**
		 * 不论是高端内存区还是普通内存区、还是DMA内存区,甚至这些管理区中保留的内存都没有了。
		 */
		goto nopage;
	}

后面代码省略。


page_address()

    把给定的页转换成对应的逻辑地址,也就是线性地址。(/Mm.h)

/**
 * page_address返回页框对应的线性地址。
 */
void *page_address(struct page *page)
{
	unsigned long flags;
	void *ret;
	struct page_address_slot *pas;

	/**
	 * 如果页框不在高端内存中(PG_highmem标志为0),则线性地址总是存在的。
	 * 并且通过计算页框下标,然后将其转换成物理地址,最后根据物理地址得到线性地址。
	 */
	if (!PageHighMem(page))
		/**
		 * 本句等价于__va((unsigned long)(page - mem_map) << 12)
		 */
		return lowmem_page_address(page);

	/**
	 * 否则页框在高端内存中(PG_highmem标志为1),则到page_address_htable散列表中查找。
	 */
	pas = page_slot(page);
	ret = NULL;
	spin_lock_irqsave(&pas->lock, flags);
	if (!list_empty(&pas->lh)) {
		struct page_address_map *pam;

		list_for_each_entry(pam, &pas->lh, list) {
			/**
			 * 在page_address_htable中找到,返回对应的物理地址。
			 */
			if (pam->page == page) {
				ret = pam->virtual;
				goto done;
			}
		}
	}
	/**
	 * 没有在page_address_htable中找到,返回默认值NULL。
	 */
	 
done:
	spin_unlock_irqrestore(&pas->lock, flags);
	return ret;
}

page_address()函数返回一个void *指针,指向给定物理页当前所在的逻辑地址。如果无应用到结构页,可以调用:

usigned long __get_free_pages(gfp_t gfp_mask,unsigned int order);

    这个函数与alloc_pages()作用相同,不过_get_free_pages直接返回所请求的第一页的逻辑地址,分配2 ^顺序页。因为页是连续的,所以其他页会在其之后。

fastcall unsigned long __get_free_pages(unsigned int gfp_mask, unsigned int order)
{
	struct page * page;
	page = alloc_pages(gfp_mask, order);
	if (!page)
		return 0;
	return (unsigned long) page_address(page);
}

而函数__get_free_page 只分配一页,返回指向其逻辑地址的指针


4.获得填充为0的页

get_zeroed_pa​​ge()

/**
 * 用于获取填满0的页框。
 */
fastcall unsigned long get_zeroed_page(unsigned int gfp_mask)
{
	struct page * page;

	/*
	 * get_zeroed_page() returns a 32-bit address, which cannot represent
	 * a highmem page
	 */
	BUG_ON(gfp_mask & __GFP_HIGHMEM);

	page = alloc_pages(gfp_mask | __GFP_ZERO, 0);
	if (page)
		return (unsigned long) page_address(page);
	return 0;
}

    这个函数与__get_free_pages()工作方式相同,只不过把分配好的页都填充成了0

get_zeored_pa​​ ge()函数只分配一页,返回指向其逻辑地址的指针。

在用户空间的页面返回之前,所有数据都必须填充为0,或做其他清理工作。


5.释放页

    首先检查页面指向的页描述符。
    如果该页框未被保留,就把描述符的计数字段减1; 
    如果计数变为0时,就假定从与页对应的页框开始的2 ^顺序个连续页框不再被使用。这种情况下,该函数释放页框。

fastcall void __free_pages(struct page *page, unsigned int order)
{
	if (!PageReserved(page) && put_page_testzero(page)) {
		if (order == 0)
			free_hot_page(page);
		else
			__free_pages_ok(page, order);
	}
}

     free_pages()函数类似于__free_pages,但是它的接收参数为要释放的第一个页框的线性地址

fastcall void free_pages(unsigned long addr, unsigned int order)
{
	if (addr != 0) {
		BUG_ON(!virt_addr_valid((void *)addr));
		__free_pages(virt_to_page((void *)addr), order);
	}
}



-------------------------------------------------------------------------------------------

补充:

高端内存的映射

永久映射

    要映射一个给定的页面结构体到内核地址空间,使用函数的KMAP()。

void * kmap(struct page * page)l

  该函数在高端内存或低端内存上都可用可睡眠,只能用于进程上下文中

如果页面结构对应的是低端内存中的一页,函数只会返回该页的虚拟地址;

如果页位于高端内存,则会建立一个永久映射,再返回地址


2.临时映射

    当必须创建一个映射而当前的上下文又不能睡眠是,内核提供了临时映射(也就是原子映射)。

柯林斯用在不能睡眠的地方,中断比如处理程序中,因为获取映射时不会阻塞。

通过函数kmap_atomic建立一个临时映射:

void * kmap_atomic(struct page * page,enum km_type type);

函数该不会阻塞,可用在中断上下文和其他不能调度的地方。禁止内核抢占,因为映射对于每个处理器都是唯一的。


-------------------------------------

参考资料:

《Linux内核设计与实现》
























2015-07-05 16:18:31 YuZhiHui_No1 阅读数 1771
  • 数组&字符串&结构体&共用&枚举-C语言专题第5部分

    本课程综合讲解了数组、字符串、字符数组、结构体定义及使用、结构体对齐、复杂结构体结合指针、共用体定义及使用、大小端模式、枚举常量及其与宏定义的关联。通过本部分共15节课的理论讲解加代码实战,希望大家能够对以上知识点有更深入的理解。

    13811 人正在学习 去看看 朱有鹏

        我做的主要是ssd驱动,ssd驱动通过FTL转换成对一个磁盘的操作,也就相当于一个磁盘的块设备驱动。块设备驱动程序主要通过传输固定大小的随机数据来访问设备。


注册块设备

        和字符设备驱动一样,都必须先到内核注册下,才能操作设备;在头文件<linux/fs.h>中

        注册函数: int  register_blkdev(unsigned int major,  const char *name);

        参数:major 是主设备号,name是设备的名称,在/proc/devices中显示。

        如果major传递的是0,则由内核来分配一个主设备号给设备,并且返回给调用者。如果返回值为负数,则表示函数注册失败;

        注:其实如果major不为0的话就相当于字符设备中的静态设备号申请,如果为0,则相当于动态设备申请了;


        销毁函数:int unregister_blkdev(unsigned int major, const char *name);

        参数一定要和注册函数的参数匹配,否则出错;


gendisk结构体


结构体

        结构体如下:

struct gendisk {
     /* major, first_minor and minors are input parameters only,
      * don't use directly.  Use disk_devt() and disk_max_parts().
      */
     int major;          /* major number of driver */
     int first_minor;
     int minors;                     /* maximum number of minors, =1 for
                                          * disks that can't be partitioned. */
 
     char disk_name[DISK_NAME_LEN];  /* name of major driver */
     char *(*devnode)(struct gendisk *gd, mode_t *mode);
 
     unsigned int events;        /* supported events */
     unsigned int async_events;  /* async events, subset of all */
 
     /* Array of pointers to partitions indexed by partno.
      * Protected with matching bdev lock but stat and other
      * non-critical accesses use RCU.  Always access through
      * helpers.
      */
     //整个块设备的分区信息都包含在里面,其核心结构是一个struct hd_struct的指针数组,每一项都指向一个描述分区的hd_struct结构
     struct disk_part_tbl __rcu *part_tbl;
     struct hd_struct part0;// 第一个分区的信息,如果没有分区则指向整个设备
 
     const struct block_device_operations *fops;//操作函数指针集合
     struct request_queue *queue;//请求队列
     void *private_data;
 
     int flags;
     struct device *driverfs_dev;  // FIXME: remove
     struct kobject *slave_dir;
 
     struct timer_rand_state *random;
     atomic_t sync_io;       /* RAID */
     struct disk_events *ev;
 #ifdef  CONFIG_BLK_DEV_INTEGRITY
     struct blk_integrity *integrity;
 #endif
     int node_id;
 };

        int  minors 次设备号,一个驱动器至少使用一个次设备号,如果驱动器是可分区的,则为每一个分区分配一个次设备号;

        char  disk_name[32] 设置磁盘设备的名称,该名字在/proc/partitions和sysfs中显示;

        struct  request_queue  *queue 请求队列,为设备管理I/O请求;

        sector_t  capacity  以512为一个扇区,该驱动器可以包含的扇区数,一般通过set_capacity设置;  

        set_capacity(dev->gd,   nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));KERNEL_SECTOR_SIZE是一个常量,使用该常量进行内核的512字节扇区到实际使用扇区大小的转换。


分区表

 //分区列表
 struct disk_part_tbl {
     struct rcu_head rcu_head;
     int len;
     struct hd_struct __rcu *last_lookup;
     struct hd_struct __rcu *part[];
 };


分区结构体

// 分区信息
 struct hd_struct {
     sector_t start_sect;// 当前分区的起始扇区
     sector_t nr_sects;// 分区的大小,多少个扇区
     sector_t alignment_offset;
     unsigned int discard_alignment;
     struct device __dev;// 一个分区对应一个设备
     struct kobject *holder_dir;
     int policy, partno;// partno分区编号
     struct partition_meta_info *info;
 #ifdef CONFIG_FAIL_MAKE_REQUEST
     int make_it_fail;
 #endif
     unsigned long stamp;
     atomic_t in_flight[2];
 #ifdef  CONFIG_SMP
     struct disk_stats __percpu *dkstats;
 #else
     struct disk_stats dkstats;
 #endif
     atomic_t ref;
     struct rcu_head rcu_head;
 };

操作函数指针

        在gendisk结构体中有个fops成员,这个成员是对快设备的操作函数指针的集合,如果熟悉字符设备的,应该不难理解;这个结构体是block_device_operations(相当于字符设备的file_operations结构体);

       

 struct block_device_operations {
     int (*open) (struct block_device *, fmode_t);
     int (*release) (struct gendisk *, fmode_t);
     int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
     int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
     int (*direct_access) (struct block_device *, sector_t,
                         void **, unsigned long *);
     unsigned int (*check_events) (struct gendisk *disk,
                       unsigned int clearing);
     /* ->media_changed() is DEPRECATED, use ->check_events() instead */
     int (*media_changed) (struct gendisk *);
     void (*unlock_native_capacity) (struct gendisk *);
     int (*revalidate_disk) (struct gendisk *);
     int (*getgeo)(struct block_device *, struct hd_geometry *);
     /* this callback is with swap_lock and sometimes page table lock held */
     void (*swap_slot_free_notify) (struct block_device *, unsigned long);
     struct module *owner;
 };
        这里有很多函数指针,但真正使用到的没有多少个。open/release 一般都会使用到(设备的开、关);media_changed/revalidate_disk 检查是否更换了驱动器内的介质/ 如果更换了,则做出响应,告诉驱动程序,作出相应的响应。getgeo函数是获取设置设备参数;


gendisk操作函数

        gendisk结构是一个动态分配的结构,它需要内涵的一些特殊处理来进行初始化,驱动程序不能自己动态的分配该结构体;

        struct  gendisk  *alloc_disk(int  minors);

        参数:minors是该磁盘使用次设备号的数目,以后不能再修改的;


        卸载磁盘:

        void del_gendisk(struct  gendisk *gd);

        gendisk是一个引用计数结构,gen_disk和put_disk函数负责处理引用计数,但驱动不能直接使用这两个函数;

 

        分配了一个gendisk结构并不能使磁盘对系统可用,必须初始化结构体并调用add_disk:

        void add_disk(struct  gendisk *gd);

        一旦调用add_disk,磁盘设备将被“激活”,并随时调用它的提供的方法。当第一次调用这些方法时,add_disk函数可能还没有返回;因为内核可能会调用gendisk结构体中的分区表;


block_device结构

        block_device结构代表了内核中的一个块设备。它可以表示整个磁盘或一个特定的分区。当这个结构代表一个分区时,它的bd_contains成员指向包含这个分区的设备,bd_part成员指向设备的分区结构。当这个结构代表一个块设备时,bd_disk成员指向设备的gendisk结构。这个结构体在ldd3中没有提到;

// 设备(分区)结构体
 struct block_device {
     dev_t           bd_dev;  /* not a kdev_t - it's a search key */
     int         bd_openers;//记录有多少进程打开该设备
     struct inode *      bd_inode;   /* will die */
     struct super_block *    bd_super;
     struct mutex        bd_mutex;   /* open/close mutex */
     struct list_head    bd_inodes;
     void *          bd_claiming;
     void *          bd_holder;
     int         bd_holders;
     bool            bd_write_holder;
 #ifdef CONFIG_SYSFS
     struct list_head    bd_holder_disks;
 #endif
     struct block_device *   bd_contains;// 如果是分区,则指向主设备块;否则指向自己
     unsigned        bd_block_size;
     struct hd_struct *  bd_part;// 指向对应的分区信息
     /* number of times partitions within this device have been opened. */
     unsigned        bd_part_count;// 分区打开的次数,重新扫描则为0
     int         bd_invalidated;
     struct gendisk *    bd_disk;// 指向gendisk
     struct list_head    bd_list;
     /*   
      * Private data.  You must have bd_claim'ed the block_device
      * to use this.  NOTE:  bd_claim allows an owner to claim
      * the same device multiple times, the owner must take special
      * care to not mess up bd_private for that case.
      */
     unsigned long       bd_private;
 
     /* The counter of freeze processes */
     int         bd_fsfreeze_count;
     /* Mutex for freeze */
     struct mutex        bd_fsfreeze_mutex;
 };

request_queue结构体


        gendisk结构体中有个成员queue,这个就是request_queue结构体的实例变量。每一块设备都会有一个队列,当需要对设备操作时,把请求放在队列中。因为对块设备的操作 I/O访问不能及时调用完成,I/O操作比较慢,所以把所有的请求放在队列中,等到合适的时候再处理这些请求;

struct request_queue {
     /*
      * Together with queue_head for cacheline sharing
      */
     struct list_head    queue_head;//待处理请求的链表
     struct request      *last_merge;//指向队列中首先可能合并的描述符
     struct elevator_queue   *elevator;////指向elevator对象的指针(电梯算法)
 
     /*
      * the queue request freelist, one for reads and one for writes
      */
     struct request_list rq;//为分配请求描述符所使用的数据结构
 
     request_fn_proc     *request_fn;//实现驱动程序的策略例程入口点的方法,策略例程方法来处理请求队列中的下一个请求
     make_request_fn     *make_request_fn;//将一个新请求插入请求队列时调用的方法
     prep_rq_fn      *prep_rq_fn; //该方法把这个处理请求的命令发送给硬件设备
     unprep_rq_fn        *unprep_rq_fn;//去掉块设备的方法
     merge_bvec_fn       *merge_bvec_fn; //当增加一个新段时,该方法返回可插人到某个已存在的bio结构中的字节数(通常未定义)
     softirq_done_fn     *softirq_done_fn;
     rq_timed_out_fn     *rq_timed_out_fn;
     dma_drain_needed_fn *dma_drain_needed;
     lld_busy_fn     *lld_busy_fn;
 
     /*
      * Dispatch queue sorting
      */
     sector_t        end_sector;
     struct request      *boundary_rq;
 /*
      * Delayed queue handling
      */
     struct delayed_work delay_work;
 
     struct backing_dev_info backing_dev_info;
 
     /*
      * The queue owner gets to use this for whatever they like.
      * ll_rw_blk doesn't touch it.
      */
     void            *queuedata;//指向块设备驱动程序的私有数据的指针
 
     /*
      * various queue flags, see QUEUE_* below
      */
     unsigned long       queue_flags;//描述请求队列状态的标志
 
     /*
      * queue needs bounce pages for pages above this limit
      */
     gfp_t           bounce_gfp;//回弹缓冲区的内存分配标志
 
     /*
      * protects queue structures from reentrancy. ->__queue_lock should
      * _never_ be used directly, it is queue private. always use
      * ->queue_lock.
      */
     spinlock_t      __queue_lock;
     spinlock_t      *queue_lock;
 /*
      * queue kobject
      */
     struct kobject kobj;
 
     /*
      * queue settings
      */
     unsigned long       nr_requests;    /* Max # of requests */
     unsigned int        nr_congestion_on;//如果待处理请求数超出了该闭值,则认为该队列是拥挤的
     unsigned int        nr_congestion_off;//如果待处理请求数在这个闭值的范围内,则认为该队列是不拥挤的
     unsigned int        nr_batching;//即使队列已满,仍可以由特殊进程“batcher”提交的待处理请求的最大值(通常为32)
 
     unsigned int        dma_drain_size;
     void            *dma_drain_buffer;
     unsigned int        dma_pad_mask;
     unsigned int        dma_alignment;
 
     struct blk_queue_tag    *queue_tags;
     struct list_head    tag_busy_list;
 
     unsigned int        nr_sorted;
     unsigned int        in_flight[2];
 
     unsigned int        rq_timeout;
     struct timer_list   timeout;
     struct list_head    timeout_list;
 
     struct queue_limits limits;
 
     /*
      * sg stuff
      */
 unsigned int        sg_timeout;
     unsigned int        sg_reserved_size;
     int         node;
 #ifdef CONFIG_BLK_DEV_IO_TRACE
     struct blk_trace    *blk_trace;
 #endif
     /*
      * for flush operations
      */
     unsigned int        flush_flags;
     unsigned int        flush_not_queueable:1;
     unsigned int        flush_queue_delayed:1;
     unsigned int        flush_pending_idx:1;
     unsigned int        flush_running_idx:1;
     unsigned long       flush_pending_since;
     struct list_head    flush_queue[2];
     struct list_head    flush_data_in_flight;
     struct request      flush_rq;
 
     struct mutex        sysfs_lock;
 
 #if defined(CONFIG_BLK_DEV_BSG)
     bsg_job_fn      *bsg_job_fn;
     int         bsg_job_size;
     struct bsg_class_device bsg_dev;
 #endif
 
 #ifdef CONFIG_BLK_DEV_THROTTLING
     /* Throttle data */
     struct throtl_data *td;
 #endif
 };


操作函数

        分配队列函数:

        dev->queue = blk_init_queue(request_func, &dev->lock);

        request_func函数指针是请求函数,负责执行块设备的读、写请求。


        内核认为每个磁盘都是由512字节大小的扇区所组成的线性数组;


        所有操作的第一步就是通知内核设备所支持的扇区大小,硬件扇区大小作为一个参数放在请求队列中,而不是放在gendisk结构中;

        blk_queue_hardsect_size(dev->queue,  hardsect_size);// 调用了该函数后,内核对设备使用设定的硬件扇区大小。


request结构体


        request结构体就是请求操作块设备的请求结构体,该结构体被放到request_queue队列中,等到合适的时候再处理。

 struct request {
     struct list_head queuelist; //请求结构体队列链表
     struct call_single_data csd;
 
     struct request_queue *q;//所在的队列
 
     unsigned int cmd_flags;
     enum rq_cmd_type_bits cmd_type;// 命令类型
     unsigned long atomic_flags;
 
     int cpu;
 
     /* the following two fields are internal, NEVER access directly */
     // 下面两个字段从不直接访问
     unsigned int __data_len;    /* total data len */
     sector_t __sector;      /* sector cursor */
 
     struct bio *bio;//请求的bio结构链表,不能直接访问,要使用 rq_for_each_bio来遍历
     struct bio *biotail;//应该是链表的尾部
 
     struct hlist_node hash; /* merge hash */
     /*
      * The rb_node is only used inside the io scheduler, requests
      * are pruned when moved to the dispatch queue. So let the
      * completion_data share space with the rb_node.
      */
     union {
         struct rb_node rb_node; /* sort/lookup */
         void *completion_data;
     };
 /*
      * Three pointers are available for the IO schedulers, if they need
      * more they have to dynamically allocate it.  Flush requests are
      * never put on the IO scheduler. So let the flush fields share
      * space with the three elevator_private pointers.
      */
     union {
         void *elevator_private[3];
         struct {
             unsigned int        seq;
             struct list_head    list;
             rq_end_io_fn        *saved_end_io;
         } flush;
     };
 
     struct gendisk *rq_disk;
     struct hd_struct *part;
     unsigned long start_time;
 #ifdef CONFIG_BLK_CGROUP
     unsigned long long start_time_ns;
     unsigned long long io_start_time_ns;    /* when passed to hardware */
 #endif
     /* Number of scatter-gather DMA addr+len pairs after
      * physical address coalescing is performed.
      */
     unsigned short nr_phys_segments;
 #if defined(CONFIG_BLK_DEV_INTEGRITY)
     unsigned short nr_integrity_segments;
 #endif
 
     unsigned short ioprio;
 int ref_count;
 
     void *special;      /* opaque pointer available for LLD use */
     char *buffer;       /* kaddr of the current segment if available */
 
     int tag;
     int errors;
 
     /*
      * when request is used as a packet command carrier
      */
     unsigned char __cmd[BLK_MAX_CDB];
     unsigned char *cmd;
     unsigned short cmd_len;
 
     unsigned int extra_len; /* length of alignment and padding */
     unsigned int sense_len;
     unsigned int resid_len; /* residual count */
     void *sense;
 
     unsigned long deadline;
     struct list_head timeout_list;
     unsigned int timeout;
     int retries;
 
     /*
      * completion callback.
      */
     rq_end_io_fn *end_io;
     void *end_io_data;
 
     /* for bidi */
     struct request *next_rq;
 };


操作函数

        每个块设备驱动程序的核心都是它的请求函数。

        块设备驱动程序的request函数:

        void  request(request_queue_t  *queue);

        当内核需要驱动程序处理读取、写入以及其他设备的操作时,就会调用该函数;在其返回前,request不需要完成队列中所有的请求,事实上,对大都数设备来说,它可能没有完成任何的请求。但是它必须启动对请求的响应,并且保证所有的请求最终都会被驱动程序所处理。也就是说,当有请求来了,调用该函数,把请求结构体放到队列中,但是其实是没有执行的,仅仅是放在队列中告诉驱动有时间就去完成这个请求,而所谓的响应就是表示成功放到了队列中。

        该函数和设备的队列绑定的,在设备队列生成的那一刻就绑定了该请求函数:  dev->queue = blk_init_queue(request,  &dev->lock);


BIO结构体


        bio结构体其实是request结构体的实际数据,一个request结构体中包含一个或者多个bio结构体,在底层实际是按bio来对设备进行操作的。该结构被传递给I/O代码,代码会把它合并到一个已经存在的request结构体中,或者需要的话会再创建一个新的request结构体;bio结构体包含了驱动程序执行请求的全部信息。

struct bio {
     //该bio结构所要传输的第一个(512字节)扇区:磁盘的位置
     sector_t        bi_sector;  /* device address in 512 byte
                            sectors */
     struct bio      *bi_next;   /* request queue link *///请求queue链表==指向下一个bio结构体
     struct block_device *bi_bdev; // 相关设备
     unsigned long       bi_flags;   /* status, command, etc */
     unsigned long       bi_rw;      /* bottom bits READ/WRITE,
                          * top bits priority
                          */// 低位为读/写,高位为优先级
 
     unsigned short      bi_vcnt;    /* how many bio_vec's *///多少个bio_vec数组
     unsigned short      bi_idx;     /* current index into bvl_vec *///现在指向哪个数组元素,相当于偏移量
 
     /* Number of segments in this BIO after
      * physical address coalescing is performed.
      */
     unsigned int        bi_phys_segments;// 合并后的segments数,bio中包含的物理段数目
 
     //剩余的I/O计数,也就是以字节为单位需要传送的数据大小
     unsigned int        bi_size;    /* residual I/O count */
 
     /*
      * To keep track of the max segment size, we account for the
      * sizes of the first and last mergeable segments in this bio.
      */
     unsigned int        bi_seg_front_size;
     unsigned int        bi_seg_back_size;
 
     unsigned int        bi_max_vecs;    /* max bvl_vecs we can hold */
 
     unsigned int        bi_comp_cpu;    /* completion CPU */
 atomic_t        bi_cnt;     /* pin count */
 
     struct bio_vec      *bi_io_vec; /* the actual vec list */// 实际的数组链表
 
     bio_end_io_t        *bi_end_io;
 
     void            *bi_private;
 #if defined(CONFIG_BLK_DEV_INTEGRITY)
     struct bio_integrity_payload *bi_integrity;  /* data integrity */
 #endif
 
     bio_destructor_t    *bi_destructor; /* destructor */
 
     /*
      * We can inline a number of vecs at the end of the bio, to avoid
      * double allocations for a small number of bio_vecs. This member
      * MUST obviously be kept at the very end of the bio.
      */
     struct bio_vec      bi_inline_vecs[0];
 };

        sector_t  bi_sector 该bio结构所要传输的第一个扇区;

        unsigned  int bi_size 以字节为单位所需要传输的数据大小,通常要bio_sectors(bio)获得每个扇区的大小

        unsigned  long bi_flags bio中一系列的标志位,如果写请求,最低有效位将被设置,通过 bio_data_dir(bio)查看,不能直接查看;

        unsigned  short  bio_phys_segments 和  unsigned short  bio_hw_segments:当DMA映射完成时,它们分别表示BIO中包含的物理段数目和硬件所能操作的段数目;


        bio结构体的核心是一个名为bi_io_vec的数组,结构体名为:

 struct bio_vec {
     struct page *bv_page;//指向整个缓冲区所驻留的物理页面
     unsigned int    bv_len;//这个缓冲区以字节为单位的大小
     unsigned int    bv_offset;//缓冲区所驻留的页中以字节为单位的偏移量
 };

转载地址:http://blog.csdn.net/yuzhihui_no1/article/details/46763817


2019-09-26 10:58:00 Peak_Gerry 阅读数 107
  • 数组&字符串&结构体&共用&枚举-C语言专题第5部分

    本课程综合讲解了数组、字符串、字符数组、结构体定义及使用、结构体对齐、复杂结构体结合指针、共用体定义及使用、大小端模式、枚举常量及其与宏定义的关联。通过本部分共15节课的理论讲解加代码实战,希望大家能够对以上知识点有更深入的理解。

    13811 人正在学习 去看看 朱有鹏

服务器管理和工作原理
1.简述Linux 文件系统通过i 节点把文件的逻辑结构和物理结构转换的工作过程。
参考答案:
Linux 通过i 节点表将文件的逻辑结构和物理结构进行转换。
i 节点是一个64 字节长的表,表中包含了文件的相关信息,其中有文件的大小、文件所有者、文件的存取许可方式以及文件的类型等重要信息。在i 节点表中最重要 的内容是磁盘地址表 。在磁盘地址表中有13 个块号,文件将以块号在磁盘地址表中出现的顺序依次读取相应的块。Linux 文件系统通过把i 节点和文件名进行 连接,当需要读取该文件时,文件系统在当前目录表中查找该文件名对应的项,由此得到该文件相对应的i 节点号,通过该i 节点的磁盘地址表把分散存放的文件物 理块连接成文件的逻辑结构。
 
2.简述进程的启动、终止的方式以及如何进行进程的查看。
参考答案:
在Linux 中启动一个进程有手工启动和调度启动两种方式:
(1)手工启动
用户在输入端发出命令,直接启动一个进程的启动方式。可以分为:
①前台启动:直接在SHELL 中输入命令进行启动。
②后台启动:启动一个目前并不紧急的进程,如打印进程。
(2)调度启动
系统管理员根据系统资源和进程占用资源的情况,事先进行调度安排,指定任务运行的时间和场合,到时候系统会自动完成该任务。
经常使用的进程调度命令为:at、batch、crontab。
 
3. 简述DNS 进行域名解析的过程。
参考答案:
首先,客户端发出DNS 请求翻译IP 地址或主机名。DNS 服务器在收到客户机的请求后:
(1)检查DNS 服务器的缓存,若查到请求的地址或名字,即向客户机发出应答信息;
(2)若没有查到,则在数据库中查找,若查到请求的地址或名字,即向客户机发出应答信息;
(3)若没有查到,则将请求发给根域DNS 服务器,并依序从根域查找顶级域,由顶级查找二级域,二级域查找三级,直至找到要解析的地址或名字,即向客户机所在网络的DNS服务器发出应答信息,DNS 服务器收到应答后现在缓存中存储,然后,将解析结果发给客户机。
(4)若没有找到,则返回错误信息。
 
4.系统管理员的职责包括那些?管理的对象是什么?
参考答案:
系统管理员的职责是进行系统资源管理、设备管理、系统性能管理、安全管理和系统性能监测。管理的对象是服务器、用户、服务器的进程及系统的各种资源等。
 
5.简述安装Slackware Linux 系统的过程。
参考答案:
(1)对硬盘重新分区。 (2)启动Linux 系统(用光盘、软盘等)。
(3)建立Linux 主分区和交换分区。(4)用setup 命令安装Linux 系统。
(5)格式化Linux 主分区和交换分区(6)安装Linux 软件包
(7)安装完毕,建立从硬盘启动Linux 系统的LILO 启动程序,或者制作一张启动Linux系统的软盘。重新启动Linux 系统。
 
6.什么是静态路由,其特点是什么?什么是动态路由,其特点是什么?
参考答案:
静态路由是由系统管理员设计与构建的路由表规定的路由。适用于网关数量有限的场合,且网络拓朴结构不经常变化的网络。其缺点是不能动态地适用网络状况的变化,当网络状况变化后必须由网络管理员修改路由表。动态路由是由路由选择协议而动态构建的,路由协议之间通过交换各自所拥有的路由信息实时更新路由表的内容。动态路由可以自动 学习 网络的拓朴结构,并更新路由表。其缺点是路由广播更新信息将占据大量的网络带宽。
 
7.进程的查看和调度分别使用什么命令?
参考答案:
进程查看的命令是ps 和top。
进程调度的命令有at,crontab,batch,kill。
 
8.当文件系统受到破坏时,如何检查和修复系统?
参考答案:
成功修复文件系统的前提是要有两个以上的主文件系统,并保证在修复之前首先卸载将被修复的文件系统。
使用命令fsck 对受到破坏的文件系统进行修复。fsck 检查文件系统分为5 步,每一步检查系统不同部分的连接特性并对上一步进行验证和修改。在执行 fsck 命令时,检查首先从超级块开始,然后是分配的磁盘块、路径名、目录的连接性、链接数目以及空闲块链表、i-node。
 
9.解释i 节点在文件系统中的作用。
参考答案:
在linux 文件系统中,是以块为单位存储信息的,为了找到某一个文件在存储空间中存放的位置,用i 节点对一个文件进行索引。I节点包含了描述一个文件所必须的全部信息。所以i 节点是文件系统管理的一个数据结构。
 
10.什么是符号链接,什么是硬链接?符号链接与硬链接的区别是什么?
参考答案:
链接分硬链接和符号链接。
符号链接可以建立对于文件和目录的链接。符号链接可以跨文件系统,即可以跨磁盘分区。符号链接的文件类型位是l,链接文件具有新的i 节点。硬链接不可以跨文件系统。它只能建立对文件的链接,硬链接的文件类型位是-,且硬链接文件的i 节点同被链接文件的i 节点相同。
 
11.在对linux 系统分区进行格式化时需要对磁盘簇(或i 节点密度)的大小进行选择,请说明选择的原则。
参考答案:
磁盘簇(或i 节点密度)是文件系统调度文件的基本单元。磁盘簇的大小,直接影响系统调度磁盘空间效率。当磁盘分区较大时,磁盘簇也应选得大些;当分区较小时,磁盘簇应选得小些。通常使用经验值。
 
12.简述网络文件系统NFS,并说明其作用。
参考答案:
网络文件系统是应用层的一种应用服务,它主要应用于Linux 和Linux 系统、Linux 和Unix系统之间的文件或目录的共享。对于用户而言可以通过 NFS 方便的访问远地的文件系统,使之成为本地文件系统的一部分。采用NFS 之后省去了登录的过程,方便了用户访问系统资源。
 
13.某/etc/fstab 文件中的某行如下:
/dev/had5 /mnt/dosdata msdos defaults,usrquota 1 2
请解释其含义。
参考答案:
(1)第一列:将被加载的文件系统名;(2)第二列:该文件系统的安装点;
(3)第三列:文件系统的类型;(4)第四列:设置参数;
(5)第五列:供备份程序确定上次备份距现在的天数;
(6)第六列:在系统引导时检测文件系统的顺序。
 
14.Apache 服务器的配置文件httpd.conf 中有很多内容,请解释如下配置项:
(1)MaxKeepAliveRequests 200 (2)UserDir public_html
(3)DefaultType text/plain (4)AddLanguare en.en
(5)DocumentRoot―/usr/local/httpd/htdocs‖
(6)AddType application/x-httpd-PHP.PHP.php.php4
参考答案:
(1)允许每次连接的最大请求数目,此为200;(2)设定用户放置网页的目录;
(3)设置服务器对于不认识的文件类型的预设格式;
(4)设置可传送语言的文件给浏览器;(5)该目录为Apache 放置网页的地方;
(6)服务器选择使用php4。
 
15.某Linux 主机的/etc/rc.d/rc.inet1 文件中有如下语句,请修正错误,并解释其内容。
/etc/rc.d/rc.inet1:
……
ROUTE add –net default gw 192.168.0.101 netmask 255.255.0.0 metric 1
ROUTE add –net 192.168.1.0 gw 192.168.0.250 netmask 255.255.0.0 metric 1
参考答案:
修正错误:
(1)ROUTE 应改为小写:route;(2)netmask 255.255.0.0 应改为:netmask255.255.255.0;
(3)缺省路由的子网掩码应改为:netmask 0.0.0.0;
(4)缺省路由必须在最后设定,否则其后的路由将无效。
解释内容:
(1)route:建立静态路由表的命令;(2)add:增加一条新路由;
(3)-net 192.168.1.0:到达一个目标网络的网络地址;
(4)default:建立一条缺省路由;(5)gw 192.168.0.101:网关地址;
(6)metric 1:到达目标网络经过的 路由器 数(跳数)。
 
16.试解释apache 服务器以下配置的含义:
(1)port 1080 (2)UserDir userdoc
(3)DocumentRoot ―/home/htdocs‖
(4);
Options Indexes FollowSymLinks
AllowOverride None
Order deny,allow
deny from all
allow from 192.168.1.5
;
(5)Server Type Standlone
参考答案:
Apache 服务器配置行含义如下:
(1)将apache 服务器的端口号设定为1080;
(2)设定用户网页目录为userdoc;
(3)设定apache 服务器的网页根目录:/home/htdocs;
(4)在此apache 服务器上设定一个目录/home/htdocs/inside,且此目录只允许IP 地
址为192.168.1.5 的主机访问;
(5)定义apache 服务器以独立进程的方式运行。
 
17.简述使用ftp 进行文件传输时的两种登录方式?它们的区别是什么?常用的ftp 文件传输命令是什么?
参考答案:
(1)ftp 有两种登录方式:匿名登录和授权登录。使用匿名登录时,用户名为:anonymous,密码为:任何合法email 地址;使用授权登录时,用户名为用户在远程系统中的用户帐号,密码为用户在远程系统中的用户密码。
区别:使用匿名登录只能访问ftp 目录下的资源,默认配置下只能下载;而授权登录访问的权限大于匿名登录,且上载、下载均可。
(2)ftp 文件传输有两种文件传输模式:ASCII 模式和binary 模式。ASCII 模式用来传输文本文件,其他文件的传输使用binary模式。
(3)常用的ftp 文件传输命令为:bin、asc、put、get、mput、mget、prompt、bye
 
18. 将内网 192.168.0.0/24 的原地址修改为 公网IP地址:1.1.1.1
[root@xuegod63 ~]# iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j SNAT --to 1.1.1.1
把从 eth0 进来的要访问 TCP/80 的数据包目的地址改为 192.168.0.1. 
[root@xuegod63 ~]# iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to 192.168.0.1
 
19.简述raid0 raid1 raid5 三种工作模式的工作原理及特点。
RAID 0:连续以位或字节为单位分割数据,并行读/写于多个磁盘上,因此具有很高的数据传输率,但它没有数据冗余,因此并不能算是真正的RAID 结构。RAID 0 只是单纯地提高性能,并没有为数据的可靠性提供保证,而且其中的一个磁盘失效将影响到所有数据。因此,RAID 0 不能应用于数据安全性要求高的场合。
RAID 1:它是通过磁盘数据镜像实现数据冗余,在成对的独立磁盘上产生互为备份的数据。当原始数据繁忙时,可直接从镜像拷贝中读取数据,因此RAID 1 可以提高读取性能。RAID1 是磁盘阵列中单位成本最高的,但提供了很高的数据安全性和可用性。当一个磁盘失效时,系统可以自动切换到镜像磁盘上读写 ,而不需要重组失效的数据。简单来说就是:镜象结构,类似于备份模式,一个数据被复制到两块硬盘上。
 
RAID10:高可靠性与高效磁盘结构一个带区结构加一个镜象结构,因为两种结构各有优缺点,因此可以相互补充。主要用于容量不大,但要求速度和差错控制的数据库中。
RAID5:分布式奇偶校验的独立磁盘结构,它的奇偶校验码存在于所有磁盘上,任何一个硬盘损坏,都可以根据其它硬盘上的校验位来重建损坏的数据。支持一块盘掉线后仍然正常运行
 
20.如何查看占用端口8080 的进程
lsof -i:8080
 
21.请写出apache2.X 版本的两种工作模式,以及各自工作原理。如何查看apache 当前
所支持的模块,并且查看是工作在哪种模式下?
答案:prefork(多进程,每个进程产生子进程)和worker(多线程,每个进程生成多个线程)
prefork 的工作原理是,控制进程在最初建立―StartServers 个子进程后,为了满足MinSpareServers 设置的需要创建一个进程,等待一秒钟,继续创建两个,再等待一秒钟,继续创建四个……如此按指数级增加创建的进程数,最多达到每秒32 个,直到满足MinSpareServers 设置的值为止。这就是预派生(prefork)的由来。这种模式可以不必在请求到来时再产生新的进程,从而减小了系统开销以增加性能。
worker 是2.0 版中全新的支持多线程和多进程混合模型的MPM。由于使用线程来处理,所以可以处理相对海量的请求,而系统资源的开销要小于基于进程的服务器。但是,worker 也使用了多进程,每个进程又生成多个线程,以获得基于进程服务器的稳定性。这种MPM 的工作方式将是Apache 2.0 的发展趋势。
可以通过命令httpd -l 可以查看apache 当前的模块,如果带有worker.c 就是工作在worker 模式下,如果有prefork.c 就是工作在prefork.c 的模式下。
 
22.你使用过监控软件吗?说说其特点
使用nagios 对服务器进行监控,其特点可实时实现手机短信、电子邮件、MSN、飞信报警。
使用cacti 对流量进行监控。
 
23.你对现在运维工程师的理解和以及对其工作的认识
运维工程师在公司当中责任重大,需要保证时刻为公司及客户提供最高、最快、最稳定、最安全的服务.运维工程师的一个小小的失误,很有可能会对公司及客户造成重大损失,因此运维工程师的工作需要严谨及富有创新精神。
 
24.linux 下常用的DNS服务软件是什么,举出几种常用的DNS记录,如果域名abc.com配置好了一台邮件服务器,IP 地址为202.106.0.20,我该如何做相关的解析?是否了解bind 的智能解析,如果了解请简述一下其原理
答案:
1)常用的DNS 软件是bind
2)A 记录 地址记录
MX 记录 邮件交换记录
CNAME 记录 别名域记录
3)修改abc.com 域名的配置文件,增加以下记录
IN MX 10 mail.abc.com.
mail IN A 202.106.0.20
4)bind 根据请求解析客户端的IP 地址,做出不同的解析,其原理是在配置文件中,设定了view,在每个view 都有客户端的IP 地址段,bind 服务器根据请求解析客户端的IP 地址,匹配不同的view,再根据该view 的配置,到相应的配置文件进行查询,将结果返回给请求的客户端。
 
25.通过apache 访问日志access.log 统计IP 和每个地址访问的次数,按访问量列出前10 名。日志格式样例如下
192.168.1.247 – - [02/Jul/2010:23:44:59 +0800] ―GET / HTTP/1.1″ 200 19
答案:
[root@xuegod63 ~]# service httpd restar
[root@xuegod63 ~]# curl http://192.168.1.63  #自己产生一些日志
[root@xuegod63 ~]# cat /var/log/httpd/access_log |  awk '{ print KaTeX parse error: Expected 'EOF', got '}' at position 3: 1 }̲'  | uniq -c|so… echo 1 >/proc/sys/net/ipv4/ip_forward如果您要禁用路由,请将上述命令中的 1 改为 0。
阻止 IP 欺骗:IP 欺骗会让人认为某个来自于外部的某个数据包是来自于它到达的那个接口。这一技术常被骇客(cracker)所使用。您可以让内核阻止这种入侵。请键入:$ echo 1 >/proc/sys/net/ipv4/conf/all/rp_filter这样,这种攻击就不再可能了。这些改变仅当系统运行时有效。在系统重新启动之后,它们会改会它们的默认值。要在启动时就改动这些值,您可以将您在 shell 提示符后键入的命令添加到 /etc/rc.d/rc.local 中以免每次都键入它们。另一个方法是修改/etc/sysctl.conf
 
35.实现字符串翻转
[root@localhost bin]# cat 8
qweqewqedadaddas
[root@localhost bin]# rev 8
saddadadeqweqewq
########################################第2 次电面
 
36.sed awk grep 哪个最好
我答的是 哪个掌握的精通,都很好,但是还是问我哪个最好,我只能说awk 了,对于行操作和列操作都可以操作的很好。
 
37.grep -E -P 是什么意思
我说的是-E, --extended-regexp 采用规则表示式去解释样式。 -P 不太清楚
 
38.简述Apache 两种工作模式,以及它们之间的区别。
答案:最主要的两种模式是prefork 模式与worker 模式。prefork 每个子进程只有一个线程,效率高但消耗内存大,是unix 下默认的模式;worker 模式每个子进程有多个线程,内存消耗低,但一个线程崩溃会牵连其它同子进程的线程。
 
39.用iptables 添加一个规则允许192.168.0.123 访问本机3306 端口
iptables -I INPUT 1 -p tcp -m tcp --dport 3306 -s 192.168.0.123 -j ACCEPT
 
40.个人对该工作的未来如何规划,需要加强哪些能力。
首先,我有一颗真诚的心,遇事沉着冷静,不急不躁;其次,我有相应的专业知识和工作经验。一年多的系统管理经历锻炼了我在这个行业的业务能力,并对行业前景和发展动态有相应的了解;最后,我会用踏实的作风在今后的工作中证明我自己的能力!
 
41.日常监控都需要监控哪些?
1)硬件:
CPU:/proc/cpuinfo
内存:/proc/meminfo
硬盘:fdisk -l
2)系统:
负载:/proc/loadavg
uptime 查看实时load average、swap
虚拟内存:vmstat(参数-s;2 4)
SUID,用户,进程
系统日志:tail -f /var/log/messages
logwatch --print --range Today --service SSHD --service pam_unix
3)网络:Host_Alive,Ping,端口,连接
 
42.如何将本地80 端口的请求转发到8080 端口,当前主机IP 为192.168.16.1,其中本地
网卡eth0:
答:
#iptables -t nat -A PREROUTING -d 192.168.16.1 -p tcp --dport 80 -j DNAT --to
192.168.16.1:8080
或者:
#iptables -t nat -A PREROUTING -i eth0 -d 192.168.16.1 -p tcp -m tcp --dport
80 -j REDIRECT --to-ports 8080
 
43.什么是NAT,常见分为那几种,DNAT 与SNAT 有什么不同,应用事例有那些?
NAT(网络地址转换)是将IP 数据包头中的IP 地址转换为另一个IP 地址的过程。在实际应用中,NAT 主要用于实现私有网络访问公共网络的功能。
常见的有:DNAT 目的网络地址转换,SNAT源网络地址转换
从定义来讲它们一个是源地址转换,一个是目标地址转换。
 
44.包过滤防火墙与代理应用防火墙有什么区别,能列举几种相应的产品吗?
包过滤防火墙工作在网络协议IP层,它只对IP包的源地址、目标地址及相应端口进行处理,因此速度比较快,能够处理的并发连接比较多,缺点是对应用层的攻击无能为力。
代理服务器防火墙将收到的IP包还原成高层协议的通讯数据,比如http连接信息,因此能够对基于高层协议的攻击进行拦截。缺点是处理速度比较慢,能够处理的并发数比较少。
代理应用防火墙:天融信GFW4000
包过滤防火墙:华为 NE 16E
 
45.iptables 是否支持time 时间控制用户行为,如有请写出具体操作步骤
支持。需要增加相关支持的内核补丁,并且要重新编译内核。
 
或者使用crontab配合iptables,首先:vi /deny.bat  输入/sbin/iptables -A OUTPUT -p tcp -s 192.168.1.0/24 --dport 80 -j DROP  保存退出
 
打开crontab -e
 
输入:00 21 * * * /bin/sh /deny.bat
 
46.说出你知道的几种linux/unix 发行版本
Redhat、CentOS、Fedora、SuSE、Slackware、Gentoo、Debian、Ubuntu、FreeBSD、Solaris、SCO、AIX
 
47.列出linux 常见打包工具并写相应解压缩参数(至少三种)
Tar  gz  bz
 
48.计划每星期天早8 点服务器定时重启,如何实现?
Crontab -e
 
00 08 * * 7  /sbin/init 6
 
49.列出作为完整邮件系统的软件,至少二类
Sendmail,postfix,qmail
 
50.当用户在浏览器当中输入一个网g 站,说说计算机对dns 解释经过那些流程?注:本机跟本地dns 还没有缓存。
答: a.用户输入网址到浏览器
b.浏览器发出DNS 请求信息
c.计算机首先查询本机HOST 文件,看是否存在,存在直接返回结果,不存在,继续下一步
d.计算机按照本地DNS 的顺序,向合法dns 服务器查询IP 结果,
e.合法dns 返回dns 结果给本地dns,本地dns 并缓存本结果,直到TTL 过期,才再次查询此结果
f.返回IP 结果给浏览器
g.浏览器根据IP 信息,获取页面
 
51.我们都知道,dns 既采用了tcp 协议,又采用了udp 协议,什么时候采用tcp 协议?什么时候采用udp 协议?为什么要这么设计?
答:这个题需要理解的东西比较的多,分一下几个方面
a,从数据包大小上分:UDP 的最大包长度是65507 个字节, 响应dns 查询的时候数据包长度超过512 个字节,而返回的只要前512 个字节,这时名字解释器通常使用TCP 从发原来的请求。
b,从协议本身来分:大部分的情况下使用UDP 协议,大家都知道UDP 协议是一种不可靠的协议,dns 不像其它的使用UDP 的Internet 应用 (如:TFTP,BOOTP 和SNMP 等),大部分集中在局域网,dns 查询和响应需要经过广域网,分组丢失和往返时间的不确定性在广域网比局域网上更大,这就要求dns 客户端需要好的重传和超时算法,这时候使用TCP
 
52.一个EXT3 的文件分区,当使用touch test.file 命令创建一个新文件时报错,报错的信息是提示磁盘已满,但是采用df -h 命令查看磁盘大小时,只使用了,60%的磁盘空间,为什么会出现这个情况,说说你的理由。
答:两种情况,一种是磁盘配额问题,另外一种就是EXT3 文件系统的设计不适合很多小文件跟大文件的一种文件格式,出现很多小文件时,容易导致inode 耗尽了
 
53.我们都知道FTP 协议有两种工作模式,说说它们的大概的一个工作流程?
FTP 两种工作模式:主动模式(Active FTP)和被动模式(Passive FTP)
在主动模式下,FTP 客户端随机开启一个大于1024 的端口N 向服务器的21 号端口发起连接,然后开放N+1 号端口进行监听,并向服务器发出PORT N+1 命令。服务器接收到命令后,会用其本地的FTP 数据端口(通常是20)来连接客户端指定的端口N+1,进行数据传输。在被动模式下,FTP 客户端随机开启一个大于1024 的端口N 向服务器的21 号端口发起连接,同时会开启N+1 号端口。然后向服务器发送PASV 命令,通知服务器自己处于被动模式。服务器收到命令后,会开放一个大于1024 的端口P 进行监听,然后用PORT P 命令通知客户端,自己的数据端口是P。客户端收到命令后,会通过N+1 号端口连接服务器的端口P,然后在两个端口之间进行数据传输。总的来说,主动模式的FTP 是指服务器主动连接客户端的数据端口,被动模式的FTP 是指服务器被动地等待客户端连接自己的数据端口。被动模式的FTP 通常用在处于防火墙之后的FTP 客户访问外界FTp 服务器的情况,因为在这种情况下,防火墙通常配置为不允许外界访问防火墙之后主机,而只允许由防火墙之后的主机发起的连接请求通过。因此,在这种情况下不能使用主动模式的FTP 传输,而被动模式的FTP 可以良好的工作。
 
54.apache 有几种工作模式,分别介绍下其特点,并说明什么情况下采用不同的工作模式?
apache 主要有两种工作模式:prefork(apache 的默认安装模式)和worker(可以在编译的时候加参数–with-mpm-worker 选择工作模式)
prefork 的特点是:(预派生)
1.这种模式可以不必在请求到来时再产生新的进程,从而减小了系统开销
2.可以防止意外的内存泄漏
3.在服务器负载下降的时候会自动减少子进程数
worker 的特点是:支持混合的多线程多进程的多路处理模块如果对于一个高流量的HTTP 服务器worker MPM 是一个比较好的选择,因为workerMPM 占用的内存要比prefork 要小。
 
55.简述linux 下编译内核的意义与步骤
编译内核的意义在于让硬件设备更稳定的发挥其应有的效能;
 
56.简述Linux 启动过程
Bios引导–》启动引导工具grub–》核心初始化–》载入初始程序init–》init初始化–》从inittab中读取数据,决定启动级别–》系统运行
 
57.简述DDOS 攻击的原理
黑客劫持大量傀儡主机,对目标服务器进行合理的资源请求,导致服务器资源耗尽而不能进行正常的服务。
 
58.简述Tcp 三次握手的过程
第一次握手,建立连接,客户端发送SYN包到服务器,并进入SYN_SEND状态,等待服务器确认;
 
第二次握手,服务器收到SYN,同时自己也发送一个SYN包和一个ACK包来确认客户端的SYN,并进入SYN_RECV;
 
第三次握手,客户端收到服务器发来的SYN+ACK后,回复服务器端一个ACK确认,发送完毕后,双方进入ESTABLISHED状态。
 
三次握手成功后,开始传输数据。
 
59.简述VPN,常见有哪几种?
VPN是指在公共的网络上建立专用网络的技术,但是两个节点间并没有物理上的专用的端到端链路,而是通过广域网或者运营商提供的网络平台之上的逻辑网络,用户数据在逻辑链路中传输,它可以有效的节省一般需要达到DDN专线所能达到的同样的目的,而且VPN采用身份验证和加密技术,充分保证了安全性。常见的VPN有:ipsec vpn、PPTP vpn、L2TP vpn、SSL vpn
 
60.请考虑以下系统的设计. 您可以翻阅资料,查询任何您有帮助的资料、指南等。
您有的资源:
8 台安装Linux (2.6 内核) 的双网卡PC 服务器以及相关开源软件,交换机
Apache 2.2.x
Tomcat 5.5.X
数据库系统
最多8 个Internet IP 地址,请您设计一个系统:
1、使用双apache web server 前端;
2、采用AJP 连接后段的3台Tomcat 应用服务器,这些tomcat 被配置成cluster, 因此需要考虑apache 对后端的分配, 分配采用完全平衡的方法; 配置使用cookie 来实现session stickness;
3、1台数据库服务器只有tomcat 才需要连接,也不需要对Internet 提供服务。
4、考虑系统的安全性和维护方便性;
5、通过rewrite 规则配置把下属URL 规则改写成友好的URL
http://server/webapp/getinfo?id=XXXX&name=YYYY –>
http://server/getinfo/YYYY/XXXX
您需要提交
1、服务器规划,包括:
* 网络结构图
* 每台机器的IP 地址分配
* 每台机器上运行的关键软件
* 您从安全性和维护性方面的考虑
2、Apache 的以下配置文件给我们:
* extra/http-proxy-ajp.conf
* extra/http-rewrite.conf
2.你可以采取任何设备和不同操作系统服务器设计对两台WWW服务器和两台FTP 服务器做负载均衡,用网络拓扑图表示并加以说明!(方法越多越好)
第一种方法: DNS 轮巡
www1 IN A 192.168.1.1
www2 IN A 192.168.1.2
www3 IN A 192.168.1.3
ftp1 IN A 192.1.1.4
ftp2 IN A 192.1.1.5
ftp3 IN A 192.1.1.6
www IN CNAME www1
www IN CNAME www2
www IN CNAME www3
ftp IN CNAME ftp1
ftp IN CNAME ftp2
ftp IN CNAME ftp3

Linux面试题

阅读数 93

Linux面试

阅读数 367

没有更多推荐了,返回首页