Thứ Tư, 27 tháng 3, 2013

Python Implementation of IP Checksum


Introduction

Tôi muốn viết một chương trình tính IP header checksum.

Background

Trước tiên chúng ta tìm hiểu cơ bản về IP checksum là gì, tại sao cần phải có IP checksum, cách tính IP checksum ? 

Checksum là gì?
checksum đơn giản là một giá trị được tính toán từ từ data packet nhằm kiểm tra tính toàn vẹn của nó (data packet). Thông qua checksum chúng ta có thể biết được dữ liệu được có bị lỗi hay không. Đó là bởi vì trong khi "traveling" qua mạng data packet có thể bị corrupt và phải có một cách nào đó để phía nhận dữ liệu biết được rằng dữ liệu bị corrupted hay không. Đó là lí do checksum field được thêm vào header. Ở phía nguồn, checksum được tính toán và được set trong header như là một trường. Ở phía đích checksum được tính toán lại một lần nữa và kiểm tra chéo với giá trị checksum tồn tại trong header để thấy được data packet có OK hay không. 

IP header checksum?

IP header checksum được tính toán trên IP header chỉ như phần dữ liệu, các TCP header , ICMP header đều có phần checksum riêng của nó. Now, để tính toán được một thuật toán tính checksum bạn cần phải có kiến thức cơ bản về cấu trúc của header trong IP protocol.

Cơ chế để tính IP checksum đó là : 16 bit one’s complement of the one’s complement sum of all 16 bit words in the header

có nghĩa là nếu chúng ta chia IP header thành các 16 bit words và thực hiện cộng dồn chúng lại và sau đó ta tiến hành đảo bit (0 --> 1, 1 --> 0) khi đó kết quả cuối cùng của các phép tính là checksum. 

Now, việc  tính toán phía trên được thực hiện tại phía nguồn gửi data packet. Tại phía đích nhận data packet sẽ thay thế giá trị checksum trong header bằng tất cả zeros và tính lại checksum dựa theo cơ chế giống như cơ chế tính tại nguồn. Sau bước này giá trị checksum đạt được sẽ được so sánh với giá trị checksum đến từ IP header. Việc so sánh sẽ quyết định xem IP header có bị corrupt hay fine. 

IP header checksum example

Dựa trên cơ sở nền tảng nêu phần trên, chúng ta thực hiện dump một IP header từ IP packet nhận được tại đích và tiến hành tìm hiểu cơ chế tính toán. Qua ví dụ sau:

Dưới đây là một IP header được lấy ra từ IP packet nhận được tại đích: 

                         4500 003c 1c46 4000 4006 b1e6 ac10 0a63 ac10 0a0c
* '45' tương ứng với hai trường đầu tiên trong IP header, '4 ' là IP version và '5' là header length. bởi vì header length được mô tả trong 4 byte words nên header length thực sự bắt đầu là 5 * 4 = 20 bytes.
* '00' ám chỉ đến trường TOS, giá trị này ám chỉ rằng đây là một operation bình thường. 
* '003c' là total length field của IP header. So, trong trường hợp này tổng độ dài của packet là 60.
* '1c46' là identification field.
* '4000' được chia trong 2 bytes. 2 bytes (tương ứng với 3 bits và 13 bits ) tương ứng với các trường flags và fragment offset. 
* '4006' được chia thành '40' và '06', '40' tương ứng với trường TTL, '06' tương ứng với trường protocol . '06' ám chỉ đây là TCP.
* 'be16' là giá trị checksum được tính tại nguồn (nơi gửi packet). Chú ý rằng trường này được set đến 0 khi ta tính toán checksum tại đích. 
* Tập hợp các bytes tiếp theo 'ac10' và '0a0c' tương ứng với các trường IP nguồn và IP đích.

Chúng ta thực hiện chuyển đổi tất cả giá trị của các trường theo cách phân chia 4 byte words sang giá trị nhị phân: 
4500 -> 0100010100000000
003c -> 0000000000111100
1c46 -> 0001110001000110
4000 -> 0100000000000000
4006 -> 0100000000000110
0000 -> 0000000000000000 // Note that the checksum is set to zero since we are computing checksum at destination end
ac10 -> 1010110000010000
0a63 -> 0000101001100011
ac10 -> 1010110000010000
0a0c -> 0000101000001100
Cộng các giá trị binary từng cặp với nhau 
4500 -> 0100010100000000
003c -> 0000000000111100
453C -> 0100010100111100  /// First result

453C -> 0100010100111100  // First result plus next 16-bit word.
1c46 -> 0001110001000110
6182 -> 0110000110000010 // Second result.

6182 -> 0110000110000010 // Second result plus next 16-bit word.
4000 -> 0100000000000000
A182 -> 1010000110000010 // Third result.

A182 -> 1010000110000010 // Third result plus next 16-bit word.
4006 -> 0100000000000110
E188 -> 1110000110001000 // Fourth result.

E188 -> 1110000110001000 // Fourth result plus next 16-bit word.
AC10 -> 1010110000010000
18D98 -> 11000110110011000 // One odd bit (carry),  add that odd bit to the result as we need to keep the checksum in 16 bits.

18D98 -> 11000110110011000
8D99 -> 1000110110011001 // Fifth result

8D99 -> 1000110110011001 // Fifth result plus next 16-bit word.
0A63 -> 0000101001100011
97FC -> 1001011111111100 // Sixth result

97FC -> 1001011111111100  // Sixth result plus next 16-bit word.
AC10 -> 1010110000010000
1440C -> 10100010000001100 // Again a carry, so we add it (as done before)

1440C -> 10100010000001100
440D -> 0100010000001101 // This is seventh result

440D -> 0100010000001101 //Seventh result plus next 16-bit word
0A0C -> 0000101000001100
4E19 -> 0100111000011001 // Final result.
Thực hiện việc đảo bit kết quả thu được 
4E19 -> 0100111000011001
B1E6 ->1011000111100110 // CHECKSUM

Thứ Ba, 26 tháng 3, 2013

Cấu trúc cơ bản của PE

Hình trên là cấu trúc của một PE file. Ở mức tối thiểu nhất thì PE file sẽ có 2 sections: 1 cho phần đoạn mã (code) và 1 cho phần dữ liệu (data). Một chương trình ứng dụng chạy trên nền tảng Windows NT có 9 sections được xác định trước có tên là : .text, .bss, .rdata, .data, .edata, .idata, .pdata, .debug  và .rsrc. Một số chương trình ứng dụng khác lại không cần tất cả sections này, trong khi cũng có một số chương trình lại định nghĩa nhiều sections riêng biệt để phù hợp với chúng.

 Những sections mà hiện giờ đang tồn tại và xuất hiện thông dụng nhất trong một file thực thi là :
1. Excutable Code Section, có tên là .text (Microsoft) hoặc là CODE (Borland).
2. Data Section, có những tên như .rdata, .data hoặc .bss (Microsoft) hoặc DATA (Borland).
3. Resources Section có tên là .rsrc (MicroSoft)
4. Export Data Section, có tên là .edata
5. Import Data Section, có tên là .idata.
6. Debug Information Section, có tên là .debug.

Những cái tên này thực sự là không thích hợp khi chúng bị lờ đi bởi HĐH và chúng ta tài liệu phục vụ cho lợi ích của các lập trình viên. Một điểm quan trọng nữa là cấu trúc của một PE file trên đĩa là hoàn toàn chính xác, đúng đắn giống hệt khi nó được nạp vào trong bộ nhớ nghĩa là ta có thể xác định chính xác thông tin file trên đĩa của một PE file nào đó mà sau đó ta phải kiếm tìm nó sau khi được nạp vào bộ nhớ.

Tuy nhiên nó không được sao chép lại một cách chính xác trong bộ nhớ. Các Windows loader sẽ quyết định phần nào được ánh xạ lên bộ nhớ, phần nào được bỏ qua. Phần dữ liệu mà không được ánh xạ lên sẽ được đặt phía cuối file, sau bất cứ phần nào sẽ được ánh xạ lên bộ nhớ, ví dụ Debug Information.

Cũng vì vậy vị trí của một mục trong file trên đĩa sẽ luôn khác biệt so với vị trí của nó khi được nạp vào bộ nhớ bởi sự quản lý bộ nhớ ảo dựa trên các trang mà Windows sử dụng. Khi các Sections được nạp vào RAM chúng được căn sao cho ăn khớp với 4KB memory Pages. Mỗi Sections sẽ bắt đầu trên một page mới. Một trường trong PE header sẽ thông báo cho hệ thống biết bao nhiêu bộ nhớ cần để riêng ra cho việc ánh xạ file

Main Memory

Lại phải vật lộn với một mớ hệ điều hành haizzz.

Như đã biết bộ nhớ là trung tâm trong hoạt động của hệ thống máy tính hiện đại. Bộ nhớ chứa mảng lớn các words hay bytes, mỗi trong số chúng có địa chỉ riêng. CPU lấy các instructions từ bộ nhớ dựa theo giá trị của program counter (pc). Nhiều instructions có thể gây ra việc loading thêm hay lưu trữ thêm đến các địa chỉ bộ nhớ xác định.

Một vòng thực thi lệnh tiêu biểu như sau: thứ nhất, lấy một instruction từ bộ nhớ. instruction này sau đó được decoded và có thể tạo ra các operands được fetch từ bộ nhớ. Sau khi lệnh được thực thi trên các operands, kết quả trả lại được lưu trong bộ nhớ. memory unit được xem như là một stream của các memory addresses, không biết làm thế nào chúng được sinh ra (bởi instruction counter (ic),  bởi indexing, indirection, literal address ...) hay chúng là gì ? (là lệnh hay dữ liệu). Theo đó chúng ra lờ đi việc làm thế nào chương trình sinh ra memory address thay vào đó chúng ta tập trung chú ý vào sequence các memory addresses sinh ra bởi chương trình đang chạy (running program).

1. Basic hardware
main memory và các registers được xây dựng bên trong processor chỉ là những nơi lưu trữ (storage) mà CPU có thể truy cập trực tiếp. Có nhiều machine instructions lấy các memory addresses như là các arguments, nhưng không một machine instructions nào sử dụng disk addresses. Do đó bất cứ lệnh nào được thực thi, hay bất cứ dữ liệu nào được sử dụng bới lệnh phải nằm trong bộ lưu trữ có thể truy cập trực tiếp. Nếu dữ liệu không nằm trong bộ nhớ, nó cần phải được di chuyển vào đó trước khi CPU operate trên chúng.
Registers được xây dựng sẵn trong CPU có thể được truy cập đến trong thời hạn một vòng của CPU clock.
Hầu hết CPU có thể decode các instructions và thực hiện được các operations đơn giản trên register contents ở tộc độ một hay nhiều operations trên clock tick. Điều này lại không đúng đối với bộ nhớ chính. Những gì có thể được truy cập via một giao dịch (transaction ) trên memory bus. hoàn thành một memory access có thể mất nhiều cycles của CPU clock. Trong những trường hợp như vậy processor cần phải stall, bởi vì nó không có dữ liệu yêu cầu để hoàn thành instruction mà nó được excuting. Hoàn cảnh như vậy là không thể chấp nhận đươc bởi vì tần số của memory addresses. Biện pháp khắc phục là thêm vào fast memory giữa CPU và main memory. Một memory buffer được sử dụng để accommodate tốc độ khác nhau, được gọi là cache. Chúng ta không chỉ concern về tốc độ truy cập bộ nhớ vật lý mà chúng ta còn phải đảm bảo correct operation để bảo vệ OS từ các user processes và thêm vào đó bảo vệ các user processes từ cái khác. Sự bảo vệ này phải được cung cấp từ hardware. Nó có thể được implemented theo một vài cách. Chúng ta outline một trường hợp có thể.

Thứ nhất chúng ta phải đảm bảo rằng mỗi process có một không gian bộ nhớ riêng biệt. Để làm được việc đó chúng ta cần phải xác định được cái range của legal addresses mà process có thể truy cập đến và phải đảm bảo rằng process chỉ có thể truy cập duy nhất đến những legal addresses đó. Chung ta có thể cung cấp sự bảo vệ đó bằng cách sử dụng 2 thanh ghi : limit và base. Thanh ghi base chứa địa chỉ hợp lệ nhỏ nhất mà process có thể truy nhập đến, thanh ghi limit chứa kích thước của range.

Việc bảo vệ không gian bộ nhớ được hoàn thành bởi có một CPU hardware thực hiện nhiệm vụ so sánh mọi địa chỉ sinh ra tại user mode với các registers. Bất cứ một nỗ lực nhằm vào việc truy nhập đến OS memory hoặc memory của user khác bởi một chương trình thực thi trong user mode sẽ rơi vào trap của OS, những gì "chăm sóc" sư nỗ lực này như là một fatal error. Cơ chế này nhằm ngăn một user program (cố ý hay tai nạn) thực hiện việc modifying code hay cấu trúc dữ liệu của OS hay của user khác.

Các thanh ghi limit và base chỉ có thể được loaded bởi OS những gì sử dụng một special priviled instruction. Bởi vì priviled instructions chỉ có thể được thực thi tại kernel mode, và bởi vì chỉ có OS thực thi trong kernel mode, chỉ có OS mới có thể load các thanh ghi limit và base. Cơ chế này giúp cho OS có thể thay đổi được các thanh ghi nhưng ngăn không cho user thay đổi nội dung thanh ghi.

OS thực thi trong kernel mode, nó được cấp các quyền truy nhập không giới hạn đến cả OS memory và cả user memory. Sự cung cấp này cho phép OS load các chương trình người dùng vào bộ nhớ của người dùng, để dump out nhiều chương trình trong trường hợp nó có lỗi, để truy cập và modify các parameters của các system calls và so on.

2. Address Binding

Thông thường, một chương trình đặt trong đĩa như là một binary excutable file. Để được thực thi, chương trình phải được mang vào trong bộ nhớ và đặt bên trong một process nào đó. Phụ thuộc vào cơ chế quản lý bộ nhớ ta sử dụng, process có thể di chuyển giữa đĩa và bộ nhớ trong suốt quá trình thực thi của nó. Các processes được đặt trên đĩa chờ đợi đến lượt mình thực thi có thể form thành input queue.

Thủ tục thông thường là ta lựa chọn một process trong input queue vào load process đó vào trong bộ nhớ. Như mỗi lần process được thực thi nó tiến hành truy nhập đến các instructions và data từ bộ nhớ. Cuối cùng khi process kết thúc thì không gian bộ nhớ của nó được khai báo là available.

Hầu hết OS cho phép một user process reside bên trong bất cứ phần nào của physical memory. Do đó, mặc dù không gian địa chỉ của máy tính bắt đầu là 00000, địa chỉ đầu tiên của user process không cần là 00000. Cách tiếp cận này ảnh hưởng đến các địa chỉ mà user program sử dụng. Trong hầu hết trường hợp user program có thể trải qua một vài bước, một số bước trong đó là optional trước khi thực thi. Các địa chỉ có thể được biểu diễn theo những cách khác nhau trong các bước. Addresses trong source program là biểu tượng (ví dụ như count). compiler sẽ thực hiện bind các symbolic addresses đến các relocatable addresses (ví dụ như: "14 bytes bắt đầu từ mở đầu của module này" ). editor hoặc loader bind các relocatable addresses đến absolute addresses (ví dụ như 74014). Mỗi binding là việc maping từ một không gian địa chỉ này đến một không gian địa chỉ khác.

Theo cách cổ điển, việc binding của instructions và data đến memory addresses có thể hoàn thành tại bất cứ step nào trong dây chuyền sau:
* compile time : nếu bạn biết rằng tại compile time nơi mà process sẽ được reside bên trong bộ nhớ khi đó absolute code được sinh ra. Cho ví dụ, nếu bạn biết rằng user process sẽ được reside trong memory bắt đầu từ location R, khi đó generated compiler code sẽ bắt đầu ở vị trí đó và mở rộng lên từ đó. Nếu sau đó starting location thay đổi, sau đó sẽ cần phải recompile this code. MSDOS .COM format programs được bind tại compile time.

* loading time : Nếu không biết được rằng tại compile time nơi process sẽ reside trong bộ nhớ, khi đó compiler sẽ sinh ra relocatable code. Trong trường hợp đó final binding sẽ được delayed cho đến loading time. Nếu starting address thay đổi chúng ta chỉ cần reload user code để kết hợp giá trị vừa thay đổi.

* excution time : nếu một process được di chuyển trong suốt quá trình thực thi của nó từ một memory segment sang memory segment khác, khi đó binding sẽ được delayed cho đến run time. Special hardware sẽ phải có sẵn cho cơ chế này làm việc. Hầu hết HĐH hiện nay sử dụng method này.

Một phần quan trọng là xét xem có bao nhiêu binding khác nhau được implement hiệu quả trong hệ thống máy tính và thảo luận về hardware support thích hợp.

Ta có thể mô tả multi step của processing một user program:
Ban đầu một source program trong giai đoạn compile time sẽ đi qua một bộ compiler or assembler kết quả sẽ sinh ra object module.
Tại giai đoạn load time object module sinh ra trong giai đoạn compile time sẽ kết hợp với các object module khác nhờ vào linkage editor kết quả sinh ra sẽ là load module. Cũng trong giai đoạn load time này còn có sự xuất hiện của loader với nhiệm vụ kết hợp load module và system library.
Trong giai đoạn execution time kết của sự kết hợp phía trên sẽ được kết hợp với các thư viện liên kết động và kết quả sẽ là các in-memory binary memory image.

3. Không gian địa chỉ ảo và không gian địa chỉ vật lý

Một địa chỉ được sinh bởi CPU thường được nhắc tới như là một logical address, trái lại một địa chỉ được nhìn như là một memory unit , đó là một thứ được load vào trong memory-address register của memory, đó thường được nhắc tới như physical address. Các phương thức address-bindings tại compile time và load time sinh ra các địa chỉ physical và logical giống hệt nhau, tuy nhiên execution-time address-binding scheme đưa ra kết quả các địa chỉ logical và physical khác nhau. Trong trường hợp đó chúng ta thường refer logical address như là virtual address. Tập hợp của tất cả logical addresses sinh ra bởi chương trình được gọi là logical address space, tập hợp tất cả các physical addresses tương ứng với các logical addresses được gọi là physical address space. Do đó trong executable-time address binding scheme, không gian địa chỉ logical và physical là khác nhau.

run-time mapping từ virtual đến physcal addresses dưới sự hỗ trợ của một thiết bị phần cứng đó là memory-management unit (MMU). Chúng ta có thể sử dụng các methods khác để hoàn thành việc mapping này. Chúng ta sẽ thực hiện mapping với một MMU scheme đơn giản đó là một sự tổng quát của base-register scheme. base register bây giờ được gọi là relocation register. giá trị của relocation register sẽ được cộng vào mọi địa chỉ sinh ra bởi process tại thời điểm địa chỉ được gửi tới memory. MS DOS chạy trên họ processor 8086 sử dụng 4 relocation registers khi loading và running processes.

User program không bao giờ thấy được real physical addresses. user program giải quyết với logical addresses. memory-mapping hardware converts logical addresses vào trong physical addresses.

Chúng ta có 2 kiểu địa chỉ : logical address (trong range từ 0 đến max) và physcal addresses (trong range từ R + 0 đến R + max, trong đó R là base value). users chỉ sinh ra địa chỉ logical và nghĩ rằng process run trong locations 0 đến max. Tuy nhiên logical addresses phải được map đến physical addresses trước khi chúng được sử dụng.

4. Dynamic Loading
Toàn bộ chương trình và tất cả dữ liệu của process phải nằm trong physical memory để process được thực thi. Do đó kích thước của process bị giới hạn bởi kích thước của physical memory. Để sử dụng tốt hơn memory-space, chúng ta có thể sử dụng dynamic loading, với dynamic loading một routine sẽ không được load cho đến khi nó được gọi. Tất cả các routines được giữ trên disk trong một relocatable load format. main program được load vào trong bộ nhớ và được thực thi. Khi một routine cần gọi một routine khác, calling routine đầu tiên kiểm tra có hay không routine khác đã được loaded. Nếu chưa có thì relocatable link loader được gọi để load routine mong muốn vào trong memory và update program's address tables để phản ánh sự thay đổi đó. Sau đó control được chuyển đên routine mới được loaded.
Lợi thế của dynamic routine là một unused routine sẽ không bao giờ được loaded. Phương thức này hữu dụng khi một lượng lớn code cần được handle không thường xuyên, ví dụ như error routines. Trong trường hợp đó mặc dù chương trình là lớn nhưng phần cần được handle là nhỏ.

Dynamic loading không yêu cầu một special support từ OS. Nó thuộc về trách nhiệm của user để thiết kế chương trình của họ để tạo lợi thế với phương thức này. OS có thể giúp programers qua việc cung cấp library routines để implement dynamic loading.

5. Dynamic linking và Shared libraries
Một vài OS chỉ support static linking, nơi những system language libraries được treated giống như bất cứ object module khác và được combined bởi loader vào trong binary program image. Dynamic linking ngược lại, tương tự với dynamic loading. Ở đây, mặc dù, linking, hơn loading, được trì hoãn cho tới excution time. Đặc tính này được sử dụng với system libraries, như là language subroutine libraries. Nếu không có facility đó, mỗi chương trình trên hệ thống phải include một bản sao language library của nó. với requirement như vậy sẽ lãng phí disk space và main memory.

Với dynamic linking, một stub đượ included trong image cho mỗi library routine tham chiếu tới. Stub là một piece of code nó chỉ ra làm thế nào để locate một memory-resident library thích hợp hoặc làm thế nào để load memory nếu routine không present. Khi stub excuted, nó kiểm tra xem có routine cần thiết đã có sẵn trong bộ nhớ hay chưa. Nếu chưa có, chương trình sẽ load routine vào trong memory. Hay một cách khác, Stub sẽ thay thế chính nó bằng địa chỉ của routine và executes routine. Do đó, next time particular code segment được reached, library routine được thực thi trực tiếp, không phải gánh chịu thêm chi phí cho dynamic linking. Dưới cơ chế này, tất cả các processes sử dụng language library chỉ thực thi duy nhất một bản sao của library code.

Tính năng này có thể mở rộng sang library updates (ví dụ như bug fixes ). Một library có thể được thay thế bởi new version, và tất cả programs tham chiếu đến thư viện đó sẽ tự động sử dụng new version. Nếu không có dynamic linking, tất cả các chương trình như trên sẽ phải được relinked để đạt được truy cập đến new library. Vì thế chương trình sẽ không vô tình execute các thư viện mới, không tương thích, các thông tin về version có thể đươc included trong cả chương trình và thư viện. Nhiều hơn 1 version của library có thể được loaded vào trong memory, và các chương trình sử dụng thông tin về version để quyết định xem bản sao nào của library được sử dụng. Versions với những sự thay đổi nhỏ sẽ giữ nguyên version number, trái lại versions với những sự thay đổi quan trọng sẽ tăng con số này. Do đó, chỉ các chương trình được biên dịch với new library version sẽ bị ảnh hưởng bởi bất cứ sự thay đổi không tương thích kết hợp trong nó. Các chương trình cũ được linked trước khi new library được cài đặt sẽ tiếp tục sử dụng older library. Hệ thống đó được biết đến như shared libraries.

Không giống như dynamic loading, dynamic linking yêu cầu sự trợ giúp từ OS. Nếu các processes trong memory được bảo vệ tránh khỏi những processes khác, khi đó OS chỉ là một thực thể để kiểm tra xem có hay không một needed routine nằm trong memory space của process khác hay là có thể cho phép nhiều process truy nhập đến cùng memory addresses.

6. SWAPPING
Một process phải được nằm trong memory để excuted. Tuy nhiên, một process có thể được swapped tạm thời ra ngoài memory đên một backing store và sau đó được đưa trở về cho continued excution. Cho ví dụ, trong một môi trường mutiprogramming với round-robin CPU scheduling algorithm. khi một quantum hết hạn, memory manager sẽ bắt đầu swap out process vừa kết thức, và swap một process khác vào trong memory space đã được giải phóng. Cũng trong lúc này, CPU sẽ scheduler sẽ allocate một time slice cho một số process khác trong bộ nhớ. khi mỗi process kết thúc quantum của nó, nó được swapped với process khác. CPU có thể swap processes đủ nhanh để một vài processes sẽ nằm trong bộ nhớ, ready to execute, khi CPU scheduler muốn reschedule CPU. Thêm vào đó, quantum phải đủ rộng để cho phép một số lượng tính toán hợp lý được hoàn thành giữa swaps.

Một biến đổi của chính sách swapping này được sử dụng cho priority-based scheduling algorithms. Nếu một process có độ ưu tiên cao hơn đến và wants service, memory manager có thể swap out process có độ ưu tiên nhỏ và sao đó load và thực thi process có độ ưu tiên lớn hơn. Khi hoàn thành xong process có độ ưu tiên lớn hơn, process có độ ưu tiên thấp hơn có thể  được swap trở lại để thực thi tiếp. Biến thể này của swapping thỉnh thoảng còn được gọi là roll out, roll in.

Thông thường, một process được swap out sẽ được swapped back lại trong cùng memory space nó chiếm lúc trước khi bị swap out. Sự hạn chế này được chi phối bởi phương thức address binding. Nếu binding kết thúc tại assembly hay là load time, sau đó process không thể dễ dàng đến location khác. Nếu execution binding được sử dụng, process có thể được swap vào bên trong memory space khác, bởi vì physical address được tính toán trong suốt execution time.

Swapping yêu cầu một backing store. backing store phổ biến có thể là một fast disk. Nó phải đủ lớn để chứa các bản sao của tất cả memory images cho tất cả users, và nó phải cung cấp truy nhập trực tiếp đến các memory images. Hệ thông duy trì một ready queue bao gồm tất cả processes sở hữu memory images lưu trên backing store hoặc trong memory và ready to run. Bất cứ khi nào CPU scheduler quyết định thực thi process, nó gọi một dispatcher. dispatcher sẽ kiểm tra xem có hay không next process trong queue nằm trong memory. Nếu không có, và nếu không có free memory region, dispatcher swap out process hiện hành trong memory và swap in process mong muốn. Nó sau đó reload registers và chuyển điều khiển đến selected process.

context switch time trong hệ thống swapping là khá cao, để có thể tính được context switch time, ta giả sử user process có kích thước là 100 MB và backing store là một standard hard disk với transfer rate là 50MB/s. Tốc độ thực để transfer 100MB-process đến main memory hay lấy từ memory ra là
                               100MB/50MB per second  =  2 seconds.

Giả sử độ trễ trung bình là 8 miliseconds và swap time là 2008 miliseconds. Bởi vì chúng ta vừa phải swap in và swap out thế nên tổng thời gian swap là 4016 miliseconds.

Chú ý rằng phần chính của swap time là transfer time. Tổng thời gian transfer tỷ lệ trực tiếp với lượng memory swapped. Nếu chúng ta có một computer system với 4G main memory và OS chiếm 1G, kích thước tối đa của user process là 3G. Tuy nhiên, nhiều user processes có thể nhỏ hơn so với mức này

Một cách tiếp cận String hay

Cách tiếp cận String trong cuốn Headfirst khá thú vị:

Bài toán đặt ra rằng: CEO của Starbuzz Coffee muốn có một chương trình show cho khách hàng của ổng xem giá cả hiện tại của coffee beans, công việc của chúng ta là viết một chương trình hiển thị kết quả đó ?

Hướng đi của bài toán là ta sẽ sử dụng một đoạn code để lấy thông tin trên từ địa chỉ http://beans.itcarlow.ie/prices.html . Đoạn code đó như sau :
import urllib.request
page = urllib.request.urlopen("http://www.beans-r-us.biz/prices.html")
text = page.read().decode("utf8")
print(text)

Đây là ouput :

<html><head><title>Welcome to the Beans'R'Us Pricing Page</title>
<link rel="stylesheet" type="text/css" href="beansrus.css" />
</head><body>
<h2>Welcome to the Beans'R'Us Pricing Page</h2>
<p>Current price of coffee beans = <strong>$6.50</strong></p>
<p>Price valid for 15 minutes from Tue Mar 26 08:02:01 2013.</p>
</body></html>

Đoạn HTML trên chính là một string được lưu trong biến text. Tuy nhiên với output như vậy vẫn chưa làm hài lòng vị CEO thế nên ta cần từ đoạn text trên có thể lấy ra chỉ giá trị của coffee, tới đây lại liên quan đên khái  niệm như index, substring... Để lấy được giá trị 6.50 ta cần phải xác định được offset của kí tự 6 và kí tư 0 (offset đầu tiên và cuối cùng của substring) sau đó sử dụng slice để trích giá trị đó ra, trong trường hơp này là text[234, 238].

Yeah một bài toán mà có thể cho ta những kiến thức cơ bản về sử dụng text trong Python.

Tạo button có thể click được trong Python


>>> from tkinter import *   # import nội dung của tkinter module (1)
>>> tk = Tk()                    # Tạo một biến chứa một đối tượng của class Tk  (2)
>>> btn = Button(tk, text="click me")  # Tạomột button (3)
>>> btn.pack()   # Hiển thị button (4)

(1) import theo cách trên chúng ta sẽ có thể sử dụng nội dung trong module tkinter mà không cần phải gọi tên của nó. Ví dụ trong trường hợp của module turtle ta có hai cách import sau
Cách thứ nhất : import turtle
                        t = turtle.Pen()
Cách thứ hai : from turtle import *
                      t = Pen()
Sự khác nhau giữa hai cách là chúng ta có thể gọi một đối tượng trong một lớp thuộc một module mà không cần phải gọi tên module đó, điều này thuận lợi khi chúng ta muốn sử dụng nhiều class trong một module nhưng không muốn gõ nhiều chứ về bản chất hai cách trên giống nhau.

(2): Giống như việc chúng ta tạo Pen object của module turtle, tk object tạo ra một cửa sổ, trên cửa sổ ấy chúng ta có thể thêm những thứ khác, như là button, input boxes hay là một canvas để vẽ trên đó. Đó là class chính cung cấp bởi tkinter module, nếu bạn không tạo một đối tượng của Tk class bạn không thể làm bất cứ thứ gì liên quan đến đồ họa, hoạt hình.

(3) Tạo một button với tk là tham số đầu tiên, và "click me" là một text mà button sẽ display

(4) Dẫu cho ta đã add button vào cửa sổ nhưng nó sẽ không displayed cho đến khi ta enter dòng này.

click me  button lúc này chưa làm được gì cả, bạn có thể click chúng cả ngày nhưng sự thực là chẳng có gì thay đổi cả. Đóng cửa sổ ta mới tạo hồi nãy, và thực hiện một số thay đổi trong code:

Định nghĩa một hàm với nhiệm vụ in ra dòng "hello there"
def hello():
      print('Hello there')

Sửa lại đoạn code phía trên
from tkinter import *
tk = Tk()
btn = Button(tk, text = "click me", command = hello)
btn.pack()

Một sự thay đổi thấy rõ khi ta có thêm thông số command, thông số này nói cho ta biết sẽ thực hiện hàm hello khi ta click vào button.


Thứ Sáu, 22 tháng 3, 2013

Tổng quan về PE

Một vài khái niệm trong bài viết
- VxDs là một mô hình device driver được sử dụng trong Microsoft Windows/386, các mode 386 tăng cường của Windows 3.x và Windows 9.x và một vài mở rộng bởi Novell DOS 7, OPEN DOS 7.01 và DR DOS 7.02 multitasker. VxDs có truy nhập đến bộ nhớ của kernel và các running process cũng như có những raw access đên hardware, VxD là viết tắt của "virtual xxx driver", trong đó xxx là một vài class của hardware . Nó xuất phát từ việc hầu hết các drivers có filenames dưới dạng vxxxd.386 trong Windows 3.x. Một vài ví dụ là: vjoyd.386(joystick), vmm.386 (memory management). VxDs thường có filename mở rộng dưới dạng .386 trong Windows 3.x và .vxd trong Windows 95. VxDs được viết trong Windows 3.x có thể sử dụng trong Windows 95 nhưng điều ngược lại là không đúng.
- Shareware: là loại phần mềm người dùng được dùng thử một thời gian (free trial) khi thời hạn dùng thử đã hết người dùng phải trả tiền bản quyền để có thể tiếp tục sử dụng. Đây là một mô hình kinh doanh trong phân phối phần mềm.
- Windows NT: là một họ của hệ điều hành được sản xuất bởi Microsoft, phiên bản đầu tiên được đưa ra vào tháng 6 năm 1993. Nó là một hệ điều hành mạnh mẽ dựa trên ngôn ngữ lập trình bậc cao, processor - independent, multiprocessing, hệ điều hành đa người dùng với các đặc tính được so sánh với Unix. Nó được dự tính bổ sung cho consumer versions của Windows dựa trên MS-DOS. NT là phiên bản 32 bit đầy đủ đầu tiên của Windows, trái với các đối tác hướng người dùng của nó, Windows 3.x và Windows 9.x là hệ điều hành lai 16bit/32bit, Windows 2000, Windows XP, Windows Server 2003, Windows Vista, Windows Home Server, Windows server 2008, Windows 7, Windows Server 2008 R2, Windows 8, Windows Phone 8, Windows RT và Windows Server 2012 là các thành viên của họ Windows NT.
Trước tiên chúng ta cần định nghĩa PE là gì ? PE là định dạng file riêng của Win32. Tất cả các file có thể thực thi trong Win32 (ngoại trừ các tập tin VxDs và các file Dlls 16bit) đều sử dụng định dạng PE. Các file Dlls 32 bit, các file COMs, các điều khiển OCX, các trình ứng dụng nhỏ trong Control Panel (.CPL files) và các ứng dụng .NET tất cả đều là định dạng PE. Thậm chí các chương trình được khiển tại kernel mode của hệ điều hành NT cũng sử dụng định dạng file PE.

Thế tại sao chúng ta cần tìm hiểu về PE? Có hai lý do chính như sau : Thứ nhất, chúng ta muốn thêm code vào trong những file thực thi (Ví dụ kĩ thuật keygen Injection hoặc thêm chức năng) và thứ hai là công việc unpacking bằng tay (manual unpacking ) các file thực thi. Hầu hết mọi sự quan tâm đều dồn về lý do thứ hai, đó là hầu như ngày nay các phần mềm shareware nào cũng được "Packed" với mục đích làm giảm kích thước của file đồng thời cung cấp thêm một lớp bảo vệ cho file.

Ở bên trong một file thực thi đã bị packed thì các bàng import tables thường thường là đã bị thay đổi, làm mất hiệu lực và phần dữ liệu thì luôn luôn bị mã hóa. Các chương trình packer sẽ chèn thêm mã lệnh để unpack file trong bộ nhớ vào lúc thực thi và sau đó nhảy tới OEP (original entry point) (Đây là nơi chương trình gốc thực sự bắt đầu thực thi, thi hành.). Nếu chúng ta tìm đươc cách để dump (kết xuất) sau khi chương trình packer hoàn tất được quá trình unpacking file thực thi, đồng thời thêm vào đó chúng ta cũng cần chỉnh sửa lại Section và import tables trước khi mà ứng dụng của chúng ta sẽ run. Làm thế nào chúng ta có thể làm được điều này nếu không có một tí tẹo hiểu biết nào về PE file ?

Chương trình thực thi được tôi sử dụng trong suốt bài viết này là BASECALC.EXE một chương trình rất hữu ích từ trang web của Fravia, nó cho phép tính toán và chuyển đổi giữa các hệ số decimal, hexa, binary và octal. Chương trình này được tác giả của nó coded bằng ngôn ngữ Borland Delphi 2.0, chính vì thế nó là một file lý tưởng để tôi lấy làm mình họa cho việc làm thế nào trình biên dich Borland để cho OriginalFirst Thunks null

Thứ Năm, 21 tháng 3, 2013

Principles of Reliable Data Transfer

Trong phần này chúng ta cùng suy ngẫm về vấn đề truyền tải đáng tin trong một context chung. Điều đó là hợp lý bởi vì bởi vì vấn đề implementing reliable data transfer (rdt) không chỉ diễn ra tại transport layer, cũng như là application layer. Với một kênh truyền tải đáng tin cậy, không một dữ liệu chuyển đi nào bị hư (corrup) (lật từ 0 sang 1 hay ngược lại) hoặc bị mất và tất cả được vận chuyển theo đúng thứ tự như chúng được gửi. Đó rõ ràng là một service model cung cấp bởi TCP đến các Internet applications yêu cầu nó (TCP).

Thứ Tư, 20 tháng 3, 2013

Modules and packages in Python

1. Modules
Khi tiếp xúc với Python chắc hẳn chúng ta bắt gặp nhiều modules, vậy modules là gì ? Module là một thành phần cung cấp cho Python định nghĩa về các hàm, các biến, các lớp, tất cả đều tương ứng với một chủ đề đã xác định trước. tất cả các definitions được chứa trong một file Python. Dành lời cảm ơn chân tình đến module, nhờ nó mà ta có thể tái sử dụng lại các definitions trong chương trình của mình. Python khuyến khích bạn xây dựng những module cho chính mình với những cách đơn giản nhất.

1.1 Sử dụng modules
Để sử dụng modules bạn phải sử dụng câu lệnh import. Chúng ta cùng xét một ví dụ. Python chứa đựng rất nhiều modules trong nó, một trong số những module rất hữu dụng đó là sys module (sys là viết tắt của từ system). Nó cung cấp các thông tin liên quan đến hệ thống khi chạy và môi trường chạy. Ngoài ra Python sys module cung cấp khả năng truy cập đến bất cứ command line arguments thông qua sys.argv. Nó phục vụ hai mục đích chính sau:
- sys.argv là một danh sách của command-line arguments.
- len(sys.argv) là số lượng của command-line arguments.
ở đây sys.argv[0] là chương trình ví dụ script name

Vấn đề về ghép và tách kênh

Trong phần này chúng ta sẽ thảo luận về vấn đề ghép kênh và tách kênh tại lớp truyền tải. Đó là một dịch vụ vận chuyển mở rộng host to host được cung cấp bởi network layer đến dịch vụ vận chuyển host to host của các ứng dụng chạy trên các hosts. Để giữ các vấn đề thảo luận đạt mức cụ thể, chúng ta sẽ thảo luận về các dịch vụ cơ bản của lớp truyền tải trong môi trương Internet. Chúng tôi nhấn mạnh rằng, dịch vụ ghép và tách kênh là cần thiết cho tất cả các kiểu mạng máy tính.
Ở host đích, lớp truyền tải nhận các segments từ lớp mạng phía dưới. Lớp vận tải có nhiệm vụ vận chuyển dữ liệu trong các segments đến các process ứng dụng thích hợp đang chạy trên host. Nào hãy cùng nhìn vào một ví dụ. Giả sử bạn đang ngồi trước máy tính và download Web pages trong khi đang chạy một phiên FTP và hai phiên Telnet. Do đó bạn có 4 processes ứng dụng đang chạy trên máy của mình- 2 processes Telnet, 1 process FTP và 1 process Http. Trong khi lớp truyền tải trên máy tính của bạn nhận dữ liệu từ lớp mạng bên dưới nó cần định hướng dữ liệu của bạn đến một trong 4 ứng dụng. Nào hãy cùng nhau phân tích làm sao để nó làm được điều này.
Chúng ta biết rằng mỗi process có thể có một hoặc nhiều sockets, những cái cửa mà dữ liệu từ process đi qua rồi tới lớp mạng. Lớp transport tại host nhận sẽ không thực sự vận chuyển dữ liệu trực tiếp đến các process, thay vào đó nó chuyển đến các socket trung gian. Bởi vì trong một khoảng thời gian cho trước có thể có nhiều hơn một socket tại host nhận, mỗi socket sẽ có một số định danh duy nhất. Định dạng của các định danh (identifier) phụ thuộc vào socket đó là UDP hay TCP socket.
Nào hãy cùng suy ngẫm thử xem làm thế nào để các host nhận có thể định hướng trực tiếp các segments tới (incoming segments) đến socket thích hợp. Mỗi transport-layer segment có một tập hợp các trường trong segment để làm nhiệm vụ này. ở host nhận, transport layer sẽ thực hiện việc phân tích các segment để nhận dạng được các socket đích và chuyển segment tới socket đó. Công việc của việc vận chuyển dữ liệu trong transport layer segment đến đúng socket cần nhận được gọi là tách kênh. Công việc thu gôm dữ liệu tại host nguồn từ các nguồn socket khác nhau và đóng gói mỗi data chunk với thông tin header(được sử dụng khi tách kênh) để tạo các segments và chuyển tiếp các segments đên lớp mạng được gọi là ghép kênh. Lớp truyền tải sở dĩ tách kênh các segments đến các process thích hợp là do việc direct các dữ liệu trong segment đến  (arriving segment's data ) đên các sockets thích hợp. Những host trung gian còn phải thu thập các dữ liệu ra từ các sockets, form các transport layer segment, và chuyển các segments đi xuống lớp mạng. Mặc dù được giới thiệu ghép kênh và tách kênh trong môi trường Internet, điều quan trọng là bạn nhận ra rằng chúng (ghép kênh và tách kênh) liên quan đến hầu hết giao thức nào là duy nhất đặt tại một layer (ví dụ như transport layer) được sử dụng bởi nhiều giao thức lớp trên.

Để hiểu về công việc tách kênh, ta nhắc lại về khái niệm tương tự : hộ gia đình. Mỗi đứa trẻ được nhận dạng bởi tên của cậu bé hay cô bé đó. Khi Bill nhận được một bó các mails từ mail carrier, anh ấy thực hiện công việc ''tách kênh'' thông qua việc quan xét xem ai được đánh địa chỉ trong bức thư và sau đó chuyển thư đến cho chị  hay anh của anh ấy. Ann thực hiện việc ghép kênh khi cô ấy thu các mail từ chị và anh của cô ấy và gửi cho mail person.

Nào bây giờ chúng ta đã hiểu về việc ghép kênh và tách kênh tại lớp truyền tải, chúng ta hãy cùng phân tích làm thế nào chúng được hoàn thành trong host. Chúng ta biết răng transport-layer multiplexing yêu cầu các sockets có các định danh duy nhất (1). và mỗi segment có những trường đặc biệt để chỉ ra cho socket biết segments nào được vận chuyển. Các trường đặc biết đó là source port number field destination port number field  (đối với từng trường hợp TCP và UDP sẽ có những trường khác nhau). Mỗi chỉ số port là một số 16 bit chạy từ 0 đến 65535, các ports từ 0 - 1023 được gọi là các well-known port numbers và được hạn chế sử dụng, có nghĩa là các port này được sử dụng cho các ứng dụng well-known như là  HTTP(80), FTP(21). Danh sách các ports nổi tiếng bạn có thể tham khảo trong RFC 1700 và được cập nhật trong RFC 3232. Khi chúng ta phát triển một ứng dụng chúng ta phải gán port ứng dụng.
Bây giờ rõ ràng bạn nên biết: Làm thế nào để transport layer có thể triển khai một ứng dụng tách kênh: Mỗi socket trong host có thể được gắn một port number, và khi segment đi tới một host, transport layer sẽ phân tích địa chỉ port đích trong segment và direct segment đó đên socket tương ứng. Dữ liệu của segment có thể băng qua socket để tìm tới process được dán vào socket đó. Như ta có thể thấy đó là cách UDP làm, riêng đối với TCP việc ghép tách kênh mang màu sắc tinh tế hơn.

Connectionless Multiplexing and Demultiplexing

Nhắc lại trong chương trước, một chương trình Java chạy trên host có thể tạo một UDP socket với dòng dưới đây:
                 DatagramSocket mySocket  = new DatagramSocket();
Khi một UDP socket được tạo theo cách trên thì thì transport layer tự động gán một port  number đến socket. Cụ thể hơn, transport layer sẽ gắn các port từ 1024  đến 65535 đến socket với một chú ý nhỏ rằng port được gán hiện không được sử dụng bởi socket nào khác trên host. Một sự thay thế, Java có thể tạo một UDP socket theo cách sau đây:
                 DatagramSocket mySocket = new DatagramSocket(19157);
Trong trường hợp này ứng dụng gán một port được chỉ định, 19157 đến UDP socket. Nếu người dụng viết code để triển khai một ứng dụng sử dụng "well-known protocol" khi đó người viết ứng dụng phải gán một giá well-known port number tương ứng. Về phía client các port sẽ tự động được gán trong khi tại phía server các port được chỉ định.
Với các port numbers được gán đến UDP sockets ta có thể mô tả chính xác định quá trinh UDP Multiplexing/ Demultiplexing. Giả sử process trong host A với UDP port number là 19157 muốn gửi một khối dữ liệu ứng dụng tới process với UDP port number là 46428 tại host B. Transport layer trong host A sẽ tạo một transport layer segment chứa dữ liệu của ứng dụng (application data), source port 19157 và destination port 46428 và 2 giá trị khác (sẽ được thảo luận sau). transport layer sau đó chuyển segment kết quả (the result segment) đến network layer. network layer sau đó đóng gói (encapsulate) segment trong IP datagram và tạo ra những điều kiện tốt nhất để vận chuyển segment tới host nhận. Nếu segment đến tại host nhận B, transport layer trong host nhận sẽ kiểm tra destination port number trong segment(46428) và vận chuyển segment đến socket của nó nhận dạng bởi port 46428. Chú ý rằng tại host B có thể chạy nhiều process với mỗi process sẽ có UDP socket cũng như port number tương ứng của riêng nó. Như đã biết khi các UDP segments đến từ mạng, host B sẽ direct (demultiplexing) mỗi segment đến nhưng socket tương xứng với nó thông qua việc kiểm tra destination port number.

Một điều quan trọng cần ghi chú đó là UDP socket hoàn toàn được định danh bởi một bộ destionation IP address và destination port number. Kết quả cho thấy, nếu 2 UDP segments có source IP address và/ hoặc source port number nhưng có chung destination IP address và destionation port number khi đó 2 segments sẽ được directed đến cùng một destionation process qua cùng một destination socket.

Chúng ta có thể tự hỏi ? Mục đích của source port number để làm gì ? Ví dụ trong A-to-B segment (segment gửi từ A đến B) source port number phục vụ như là một "return address"- Khi B muốn gửi segment ngược lại cho A, destination port trong B-to-A segment sẽ là source port number trong A-to-B segment (Địa chỉ đầy đủ trả về bao gồm địa chỉ IP nguồn của A và source port number). Trong UDPServer.java, server sử dụng một phương thức để extract source port number trong segment nó nhận được từ phía client, với source port number vừa extract được ở trên sẽ được sử dụng làm destination port number trong segment kế tiếp.

Connection-Oriented Multiplexing and Demultiplexing

Để hiểu về TCP demultiplexing , chúng ta cần quan sát kĩ TCP socket và TCP connection establishment (thiết lập kết nối TCP) . Một sự khác biệt tinh tế giữa TCP socket và UDP socket đó là TCP socket được định dạng bởi bộ bốn (four-tuple): (source IP address, source port number, destination IP address, destination port number). Do vậy, khi TCP segment đi từ network đến host, host sử dụng cả thẩy 4 giá trị để direct (demultiplex) segment đến socket thích hợp. Cụ thể hơn, trái ngược với UDP, 2 segments đến với địa IP source khác nhau hoặc source port numbers sẽ (với ngoại lệ là các TCP segment mang yêu cầu thiết lập kết nối ban đầu ( the original connection-establishment request) ). Được direct đến hai sockets khác nhau. Để có được cái nhìn cụ thể hơn. Hãy cùng suy ngẫm về TCP client-server programming
- TCP server application có một "welcoming socket " , những gì chờ đợi các connection-establishment requests từ phía TCP clients  trên một port number, giả sử là 6789
- TCP client sẽ sinh ra một segment thiết lập kết nối (connection-establishment segment) với dòng sau
Socket clientSocket = new Socket ("serverHostName", 6789);

-Một request thiết lập kết nối đơn thuần chỉ là một TCP segment với destination port number 6789 và một bit đặc biệt trong TCP header phục vụ cho việc thiết lập kết nối. Segment còn bao gồm cả source port number, những gì được chọn bởi client. Dòng phía trên tạo một TCP socket cho client process, thông qua đó những data có thể được enter và rời khỏi client process.

- Khi hệ điều hành của host chạy server process nhận được một yêu cầu kết nối đến (incoming-connection request ) với destination port là 6789, nó xác định server process đang đợi để chấp nhận kết nối trên port 6789. Server process sau đó tạo ra một new socket:
Socket connectionSocket = welcomeSocket.accept()

- transport layer tại server ghi chú 4 giá trị trong connection-request segment: (1) source port number trong segment, (2) địa chỉ IP của host nguồn, (3) destination port number trong segment, (4) địa chỉ IP của chính nó. socket mới được tạo ra được nhận dạng bởi 4 giá trị trên. Tất cả các segments sau đó nếu match tất cả 4 giá trị trên sẽ được demultiplexed đên socket đó. Với TCP connection như trên, client và server có thể gửi dữ liệu cho nhau.

Server host có thể support nhiều TCP sockets đồng thời, với mỗi socket được gắn với một process, và mỗi socket được nhận dạng bởi four-tuple nêu trên. Khi một TCP segment đến host tất cả 4 trường (source IP address, source port, destination port number, destionation IP address) được sử dụng để direct  (demultiplex) segment đến socket thích hợp.

Web Servers and TCP

Trước khi kết thúc bài thảo luận, thật hữu ích khi chúng ta nói một chút về Web servers và chúng sử dụng các port numbers như thế nào ?. Suy ngẫm, một host đang running một Web server, ví dụ như Apache web server, trên cổng 80. Khi clients (ví dụ như browsers) gửi các segments đến server, tất cả segments sẽ có destination port 80. Cụ thê hơn, cả các segments khởi động kết nối (initial connection-establishment segments) và các segments mang các HTTP request messages sẽ có destination port 80. Như đã mô tả trên, server sẽ phân biệt các clients khác nhau bằng cách sử dụng source IP address và source port numbers.
Web server sẽ sinh ra một new process cho mỗi kết nối, mỗi trong processes có connection socket riêng của nó, những gì HTTP requests đi tới và HTTP responses được gửi đi. Tuy nhiên không hẳn chỉ tồn tại một sự tương ứng one-to-one giữa connection sockets và processes. Các high performing Web servers ngày nay thường chỉ sử dụng một process và tạo một new thread với new connection socket cho mỗi client connection (thread có thể được nhìn nhận như một tiến trình con gọn nhẹ). Đối với server như trên, tại một thời gian cho trước có nhiều connection sockets (với các định danh (identifiers) khác nhau) gắn vào cùng một process.

Nếu client và server đang sử dụng sử dụng persistent HTTP(HTTP lâu dài), sau đó trong suốt quá trình của persistent connection nếu client và server trao đổi các HTTP messages thông qua cùng một server socket. Tuy nhiên, nếu client và server sử dụng non-persistent socket, một new TCP connection sẽ được tạo và đóng đối với mỗi request/response và hiển nhiên đi kèm nó là mỗi new socket connection được tạo cho mỗi request/response. Việc thường xuyên tạo và đóng các sockets có thể ảnh hưởng đến performance của các busy Web server (mặc dù cũng có những mẹo về hệ điều hành có thể giúp giảm thiểu điều này )

Hiểu về hàm repr() trong Python

cú pháp: repr(object)
Trả về một string chứa đại diện có thể in được (printable representation) của đối tượng.  Kết quả trả về có cùng giá trị thu được qua những lần chuyển đổi (reverse quotes). Nó thỉnh thoảng hữu ích để có thể truy nhập đến operation này như là một hàm thông thường. Đối với nhiều dạng, hàm này có thể cố gắng lấy về một string giống như ta thực hiện hàm eval() cho string đó. Nếu không đại diện  được đóng trong các angle brackets  chứa tên của kiểu đối tượng cùng với những thông tin thêm về đối tượng đó như là: địa chỉ và tên. Một class có thể điều khiển những gì mà hàm này trả về cho mỗi instance của nó qua việc định nghĩa __repr()__ method.

Chúng ta cùng đi vào một vài ví dụ để hiểu rõ hơn về hàm này.
Tôi có
>>>  x = 'foo' (1)
Thực hiện hàm repr():
>>> repr(x) (2)
Kết quả trả về:
"'foo'"
Tới đây có hai câu hỏi được đặt ra đó là:
1. Tại sao trong kết quả trả về lại xuất hiện dấu nháy kép ("" "")? Trong khi sử dụng hàm str() tôi không thấy?
2. Điều gì xảy ra khi ta sử dụng hàm eval() với kết quả vừa nhận, tức là eval("''foo")

Ta sẽ trả lời các câu hỏi trên sau khi tiếp cận một ví dụ sau đây:
Tôi có
>>>  x = 'foo'
sau đó
>>> x
tôi thu được
'foo'
Ta thấy kết quả khác so với ví dụ phía trên, một cặp dấu nháy kép đúng không.
Quay trở lại ví dụ trên, tại dòng (1) ta thực hiện gán giá trị 'foo' cho biến x, khi ta gọi hàm repr(x) tại dòng (2)   thì trình thông dịch sẽ thay thế x bởi 'foo ' và sau đó gọi hàm repr('foo')

>>> repr(x)
"'foo'"

>>> x.__repr__()
"'foo'"

repr thực sự gọi một magic method __repr__ của đối tượng x, có thể xem xét hàm repr() thực ra cũng chỉ là quá trình "function" hóa một phương thức trong đối tượng, thực sự thì trong hoàn cảnh này thì phương thức hay đối tượng cũng không thực sự khác nhau. Cả phương thức hay đối tượng đều trả về một string chứa đại diện cho giá trị 'foo' được gán cho biến x. Vì vậy nó trả lại một 'foo' nằm bên trong string ("""") và kết quả là "'foo'" như chúng ta thấy. Ý tưởng của repr là đưa ra một string chứa hàng loạt các biểu tượng (symbol) những gì chúng ta có thể gõ (type) trong trình thông dịch và có cùng giá trị với đầu vào của hàm repr.


>>> eval("'foo'")
'foo'

khi chúng ta gọi eval("'foo'") nó cũng giống như ta đánh 'foo' trong trinh thông dịch


>>> eval('foo')
Traceback (most recent call last):
  File "<pyshell#83>", line 1, in <module>
    eval('foo')
  File "<string>", line 1, in <module>
NameError: name 'foo' is not defined

Nếu chúng ta gọi eval('foo') nó đồng nghĩa với việc chúng ta gõ foo trong trinh biên dịch và lỗi xảy ra bởi vì không giá trị foo nào có sắn hay nói cách khác là ta chưa tạo ra biến foo.


>>> str(x)
'foo'
>>> x.__str__()
'foo'

str chỉ là một biểu diễn string của đối tượng, với lẽ đó nó chỉ xuất ra kết quả là string
Một vài ví dụ

>>> x.__str__()
'foo'
>>> str(5)
'5'
>>> str('foo')
'foo'








Thứ Ba, 19 tháng 3, 2013

Access Networks

Ta xem xét các end systems và các ứng dụng đặt tại "edge of network". Nào bây giờ hay suy nghĩ về access networks. Các liên kết vậy lý kết nối một end system đến đên router đầu tiên (edge router) trên con đường đi từ end system này đến một system ở xa. Có rất nhiều công nghệ truy nhập được sử dụng, các mức độ cũng như các thành phần khác nhau của cấu trúc mạng điện thoại cố định truyền thống. Các hạ tầng đường dây điện thoại được cung cấp bởi các nhà cung cấp dịch vụ điện thoại địa phương, thường được gọi với cái tên telco. ví dụ cho các local telcos có thể kể đến như Verizon ở Mỹ hay France Telecom tại Pháp. Mỗi bộ phận cư trú (hộ gia đình hay chung cư) có một đường liên kết twisted-pair đấu nối trực tiếp đến telco switch gần nhất, những gì được đặt trong một tòa nhà gọi là Central Office (CO) theo thuật ngữ điện thoại. Một local telco sẽ có hàng trăm COs, và sẽ liên kết khách hàng của họ đến telco gần nhất.

1. Dial Up
Nhìn lại những năm 1990, hầu hết các khách hàng trong khu dân cư truy cập Internet thông qua kết nối sử dụng đường dây điện thoại thông thường  sử dụng các dial up modem. Ngày nay còn rất nhiều quốc gia kém phát triển và các vùng nông thôn tại các quốc gia phát triển vẫn kết nối đến Internet thông qua dial up. Năm 2008 người ta ước lượng được rằng có 10% khách hàng tại Mỹ sử dụng kết nối dial up để đi vào internet.
thuật ngử "dial up" được sử dụng bởi vì khách hàng thực sự quay (dial) số điện thoại của ISP và tạo ra một cuộc gọi thông thường đến các ISP. PC được gắn với dial up modem, những gì cũng được gắn trên home's analog phone linet. analog phone line được tạo ra từ twisted-pair cooper wire và giống với đường dây điện thoại được sử dụng để tạo ra cuộc gọi. home modem thực hiện convert tín hiệu số từ PC sang tín hiệu analog để phù hợp cho việc vận chuyển trên analog phone line. Phía bên kia kết nối, một modem của ISP thực hiện convert tín hiệu analog thành tín hiệu số để phù hợp với đầu vào của các router.
Dial up Internet access có hai nhược điểm chính. Đầu tiên và quan trong nhất, nó thực sự chậm, dial up cung cấp tốc độ tối đa là 56Kbps, trung bình mất 8 phút để download một file MP3 dài 3 phút và mất đến một vài ngày để download một bộ phim với dung lượng 1Gbyte. thứ hai, dial up cạnh tranh đường dây điện thoại dùng để gọi điện, trong khi một thành viên trong gia đình sử dụng dial up modem để lướt web thì thành viên khác không thể gọi điện hay nhận điện thoại trên đường dây điện thoại được.

2. DSL
Ngày nay có hai kiểu phổ biến của việc truy cập Internet băng thông rộng đó là digital subscriber line (DSL) và cable. Trong xã hội phát triển hiện nay, có hơn 50% hộ gia đình có kết nối băng thông rộng tại Hàn Quốc, Iceland, Netherlands, Denmark và Switzerland dẫn đâu với 74% trong các hộ gia đình vào năm 2008. ở Mỹ, DSL và cable chia sẻ nhau thị phần trong kết nối băng thông rộng. Bên ngoài Mỹ và Canada, DSL chiếm ưu thế, tỷ dụ như khu vực Châu Âu trong nhiều nước DSL chiêm tới 90%.
Các thành phần cư trú (residence) có được Internet DSL access từ cùng một công ty cung cấp wired local telecom access (ví dụ như local telco). Do đó khi một DSL được sử dụng, một customer's telco cũng là một ISP của nó. mỗi customer's DSL modem sử dụng đường dây điện thoại đã tồn tại sắn (twisted-pair cooper wire) để trao đổi dữ liệu với Digital Subscribr Line Access Multiplexer (DSLAM), được đặt trong telco's CO đường dây điện thoại vận chuyển đồng thời cả dữ liệu và tín hiệu thoại, những gì được encoded tại các tần số khác nhau:
- A high speed downstream channel, trong khoảng từ 50khz đến 1MHz
- A medium speed upstream channel trong khoảng từ 4khz đến 50khz
- An ordinary two way telephone channel trong khoảng từ 0 đến 4khz
tại cùng một thời điểm. về phía khách hàng, đối với các tín hiệu đi đến nhà, bộ tách sẽ phân chia phần dữ liệu và phần tín hiệu thoại và chuyển tiếp phần dữ liệu đên DSL modem. về phía telco, DSLAM phân chia dữ liệu và tín hiệu điện thoại chuyển phần dữ liệu đi vào Internet. Có đến hàng trăm thậm chí là hàng ngàn hộ gia đinh kết nối đên một DSLAM đơn.
DSL có hai lợi thế chính so với dial up Internet access. Thứ nhất nó có thể chuyển và nhận dữ liệu ở tốc độ cao. Tiêu biểu như, DSL customer sẽ có tốc độ downstream (từ CO về đến dân cư) từ 1 đến 2 Mbps và trong khoảng từ 128Kbps đến 1 Mbps đối với upstream. bởi vì tỉ lệ downstream và upstream là khác nhau cho nên kiểu cập này là bất đối xứng. Đặc điểm lợi thế thứ hai là khách hàng có thể vừa sử dụng điện thoại vừa truy cập Internet, không giống như dial up , khách hàng không thể dial số điện thoại của ISP để có được kết nối Internet, thay vào đó chúng có một kết nối thường trực đến ISP's DSLAM (và hiển nhiên là đến Internet)
tỉ lệ transmission thực sự đối với downstream và upstream còn phụ thuộc vào khoảng cách từ nhà đên CO, sự đánh giá về twisted pair line và sự can thiệp của các tín hiệu điện. Các kĩ sư có thể thiết kế DSL với khoảng cách ngắn từ khách hàng đến các CO cho phép một tỉ lệ transmission cao hơn so với dial up. Để ''bật'' (boost) tỉ lệ dữ liệu. DSL phụ thuộc vào khả năng xử lý tín hiệu cấp cao và cơ chế chỉnh sửa lỗi những gì có thể làm cho độ trì hoãn packet cao. Tuy nhiên, nếu khách hàng không nằm trong phạm vi 5 đến 10 miles của CO thì việc xử lý tín hiệu DSL là không có hiệu quả và khách hàng phải cân nhắc đến sử dụng một kiểu kết nối Internet khác.
Có một vài công nghệ DSL tốc độ cao khác nhau thâm nhập vào thị trường và hữu dụng cho ngày hôm nay. Cho ví dụ very high speed DSL (VDSL) được sử dụng nhiều tại Nam Hàn và Nhật bản, cung cấp một tỉ lệ ấn tượng 12 đên 55 Mbps cho downstream và 1.6 đến 20Mbps cho upstream.

3. Cable
Nhiều residences (người sử dụng) ở Bắc Mỹ và một số nơi khác nhận hàng trăm kênh truyền hình thông qua coaxial cable networks. Trong hệ thống truyền hình cable truyền thống, cable head end phát các kênh truyền hình thông qua mạng lưới phân bố coaxial cable và các ampliers đến với các residences.
trong khi DSL và dial up sử dụng hạ tầng đường dây điện thoại thì cable Internet access sử dụng các đường cable truyền hình tồn tại sẵn trên hệ tầng truyền hình. các residence đạt được truy cập Internet từ chính các công ty cung cấp truyền hình cable....

Client và Server programs

Trong bối cảnh của networking software, có một định nghĩa khác về client và server, một client program có nghĩa là một chương trình chạy trên một end system thực hiện việc truy vấn và nhận dịch vụ từ server program chạy trên một end system khác. Các ứng dụng phổ biến như Web, email, remote-login hay FTP đều hoạt động dựa theo mô hình client-server. Bởi vì client program chạy trên một end system trong khi một server program chạy trên một end system khác nên ta gọi các ứng dụng client-server trên Internet là các distributed applications. Tới đây có lẽ phải nhắc lại, thế distributed applications là gì ? Các ứng dụng được gọi là distributed applications bởi vì chúng khi hoạt động yêu cầu nhiều end systems trao đổi dữ liệu với nhau. Một điều quan trọng hơn đó là các distributed applications này chỉ chạy được trên các end systems, chúng không chạy được trên các packet switches trong các network core. Thế network core là gì ? network core cũng chỉ là tập hợp của các packet switches và các communication links. Các client program và server program thực hiện việc tương tác thông qua sự trao đổi các thông điệp với nhau trên Internet. Trừu tượng hóa, ta có thể coi các routers, links và các nuts and bolts của mạng Internet phục vụ như một chiếc hộp đen có nhiệm vụ vận chuyển các messages giữa các thành phần ứng dụng, các thành phần giao tiếp của ứng dụng Internet.
Không hoàn toàn tất cả các ứng dụng Internet hiện nay là dựa trên sự tương tác giữa client program và server program. Sự tăng trưởng của nhiều ứng dụng Peer to Peer (P2P), những gì mà end systems tương tác và chạy các chương trình thực hiện cả nhiệm của client lẫn server. Cho ví dụ, trong P2P file sharing applications (ví dụ như BitTorent và eMule), chương trình trong máy user hoạt động như là một client khi nó yêu cầu một file từ peer khác, và nó hoạt động như server khi nó gửi file đến peer khác. Trong ứng dụng điện thoại Internet, có hai thành phần giao tiếp đóng vai trò như các peers, phiên giao tiếp là đối xứng, với cả hai thành phần gửi và nhận dữ liệu.