Thanh ghi cờ là một thanh ghi đặc biệt trong bộ xử lý, nó chứa nhiều bit phản ánh kết quả của các phép tính, cũng như trạng thái của bộ xử lý. Trong đó có 5 cờ liên quan đến các phép toán số học, chúng sẽ được bật hoặc tắt dựa trên kết quả của lệnh tính toán trước đó. Các cờ đó là:
Carry flag (CF): bằng 1 nếu phép tính có mượn. Hay nói cách khác, CF bằng 1 nếu (A + B) có kết quả lớn hơn số bit có thể chứa, hoặc nếu A < B nếu là phép trừ.
Parity flag (PF): bằng 1 nếu tổng số bit 1 trong byte thấp nhất của kết quả là chẵn. Ví dụ kết quả là 0b_00001111 thì PF = 1 (số bit 1 là 4).
Auxiliary Carry flag (AF): Bằng 1 nếu có xảy ra “nhớ” với 4 bit thấp nhất (Tương tự CF nhưng chỉ áp dụng cho 4 bit thấp nhất).
Zero flag (ZF): Bằng 1 nếu kết quả bằng 0.
Sign flag (SF): bằng 1 khi bit dấu (bit lớn nhất) của kết quả là 1, biểu thị đây là một kết quả âm.
Các cờ được sử dụng trong các phép nhảy có điều kiện, các lệnh kiểu như JZ (Jump if Zero), JNZ (Jump if Not Zero), JC (Jump if Carry), JG (Jump if Greater), JE (Jump if Equal)… đều dựa trên các cờ tương ứng để quyết định.
Note: mỗi nhóm 4 bit (biểu thị bằng một số hệ 16) còn được gọi là 1 nibble (Mỗi byte có 2 nibble).
Sao chép giá trị từ nguồn sang đích mà không làm thay đổi giá trị nguồn.
Ví dụ: `mov rax, 1` gán giá trị 1 vào thanh ghi rax.
Lưu ý là chúng ta không thể thực hiện một lệnh mov từ bộ nhớ đến bộ nhớ, để làm vậy bạn cần hai lệnh mov riêng biệt và dùng một thanh ghi làm nơi chứa giá trị trung gian.
Không phải thanh ghi nào cũng có thể được thay đổi giá trị bằng lệnh mov.
SYSCALL – Gọi hệ thống
Thực hiện lời gọi hệ thống của Linux.
Lệnh syscall chuyển điều khiển cho hệ điều hành, đồng thời đưa CPU về kernel mode, bạn cần gán giá trị thanh ghi rax bằng mã chức năng mà bạn muốn gọi.
Đối với người học lập trình phần mềm chuyên sâu, đặc biệt là sinh viên, việc hiểu rõ kiến trúc máy tính là yêu cầu bắt buộc để có thể hiểu sâu vào những gì diễn ra bên trong máy tính. Do vậy đây là một môn học bắt buộc, không hiểu rõ những kiến thức nền tảng này sẽ khiến bạn không thể đào sâu những khái niệm liên quan như đa luồng, bất đồng bộ, các kỹ thuật tối ưu, cách sử dụng bộ nhớ hiệu quả… Và hiển nhiên bạn không thể trở thành một chuyên gia nếu chỉ biết những kiến thức cơ bản, và trong ngành này, việc không thể trở thành chuyên gia khiến bạn chỉ có thể cạnh trạnh bằng sức khỏe, một thứ mà theo thời gian sẽ ngày càng kém đi.
Assembly là ngôn ngữ lập trình sát với phần cứng nhất, khi viết bằng ngôn ngữ này, chúng ta kiểm soát chương trình đến từng byte, từng mã lệnh. Hai môn học Kiến trúc máy tính và ngôn ngữ Assembly luôn đi cùng với nhau, bởi phải có kiến thức về Kiến trúc máy tính mới có thể viết chương trình Assembly, ngược lại, viết chương trình bằng Assembly sẽ giúp bạn có một môi trường thực hành, bạn có thể “nhìn thấy” những thứ trong lý thuyết được áp dụng vào thực tế.
Một event là một sự kiện gì đó đã xảy ra trong quá khứ.
Khi bạn viết một phần mềm, chung quy lại tất cả mọi thứ chỉ để phục vụ cho mục đích cuối cùng là quản lý các đối tượng có trong hệ thống. Một phần mềm quản lý nhân sự sẽ quản lý danh sách và trạng thái của các nhân sự, một hệ cơ sở dữ liệu quản lý danh mục các bảng và dữ liệu bên trong chúng, một hệ điều hành quản lý các tiến trình và các thiết bị ngoại vi…
Lưu ý là “trạng thái” biểu thị giá trị hiện tại của tất cả các thuộc tính, ví dụ tên hay địa chỉ của nhân sự, giá trị của các dòng trong một bảng hay ID của một tiến trình…
Mỗi một sự thay đổi trạng thái sẽ dẫn đến một sự kiện, hay nói cách khác mỗi một sự kiện đại diện cho một sự thay đổi trạng thái nào đó. Vì sự kiện luôn là một thứ đã diễn ra trong quá khứ nên giá trị của nó không thể thay đổi và được đặt tên ở thì quá khứ, ví dụ như PersonNameChanged, RowAdded hay OrderDelivered. Tất nhiên chúng ta sẽ thấy rằng trong một phần mềm sẽ có rất nhiều sự kiện xảy ra, việc mô hình hóa hay viết ra tất cả các event là không thể, và thực ra là không cần thiết, chúng ta chỉ cần định nghĩa các sự kiện có ảnh hưởng đến bài toán cần giải quyết.
Ai cũng biết là ta có thể viết code trong nhiều file .c/.cpp khác nhau, các trình biên dịch cho phép điều này nhằm giúp ta tổ chức mã nguồn dễ hơn, hãy thử tưởng tượng những khó khăn nếu mỗi chương trình phải viết trong một file, chưa kể đến quá trình merge code, team tầm 5 người thì có lẽ thời gian merge còn lâu hơn thời gian code nữa.
Tìm hiểu về .NET: Bạn ít nhất phải biết .NET là gì (không biết thì đọc ở đây: https://daohainam.com/2023/10/18/gioi-thieu-ve-net/), cách nó chạy chương trình như thế nào, rồi tiếp đến tìm hiểu cách viết, dịch và chạy chương trình. Ít nhất bạn phải biết bạn làm được gì với nó, cách tải và cài đặt Visual Studio (dùng hẳn bản Visual Studio nhé, đừng dùng Visual Studio Code, tập đạp xe thì cứ kiếm cái xe bình thường mà tập, đừng bắt đầu bằng xe một bánh), cách tạo project, biên dịch, chạy và debug.
Học ngôn ngữ lập trình: C# là ngôn ngữ chính thống nhất và đầy đủ nhất trên .NET, cú pháp đơn giản, thân thiện với người đã biết C++, Java nên học rất nhanh. Bắt đầu bằng các chương trình console, giải các bài tập đơn giản để quen với cú pháp của nó.
Bạn nên lập ra một danh sách các bài tập cần viết trước khi bắt đầu, đặt mục tiêu hoàn thành là khi bạn làm hết các bài đó. Danh sách bài tập có thể tham khảo từ các sách học ngôn ngữ khác.
Hôm trước làm 2 clip về Authentication và Authorization xong nhưng vẫn cứ thấy thiếu thiếu, vì phần OAuth2 và OpenIdConnect (OIDC) mình nói hơi sơ sài, nghe lại cứ có cảm giác như nói cho xong bài hay chạy KPI vậy.
Làm cái gì mà không tới nơi tới chốn cảm giác nó rất bực mình, vậy nên nay lại ngồi cặm cụi viết một OpenIdConnect server, “from scratch” luôn, hi vọng khi nào xong mình sẽ làm một clip riêng về OAuth2, OIDC, sử dụng chương trình này làm demo, để mọi người thấy từng bước nó chạy, và id_token, access_token, refresh_token được tạo ra và quản lý như thế nào.
– Ưu tiên dùng các hàm Try*: ví dụ như khi phân tích chuỗi thành số, thay vì dùng int.Parse, ta nên dùng int.TryParse, kiểm tra kết quả trả về của nó để biết việc chuyển có thành công hay không.
– Sử dụng IDictionary.TryGetValue thay vì dùng Items và bắt Exception.
– Sử dụng ArgumentNullException.ThrowIfNull() thay vì if (v == null) throw new ArgumentNullException().
– Sử dụng ArgumentOutOfRangeException.ThrowIf* thay vì if () throw new ArgumentOutOfRangeException().
– Khi cần một IEnumerable rỗng, hãy sử dụng Enumerable.Empty<T>() thay vì new List<T>(), tương tự cho Array.Empty.
– Trong khối catch (Exception ex), bạn có thể dùng throw; để throw lại ex mà vẫn giữ stack trace, nếu bạn gọi throw ex; thì stack trace sẽ được tạo lại từ nơi bạn gọi.
– Các object hay dùng nên được “cache” lại bằng cách sử dụng các static instance.
– Từ khóa stackalloc sẽ giúp bạn khai báo một biến trên stack thay vì trên heap, sử dụng nó rất tốt với các biến kích thước nhỏ trong các hàm được gọi nhiều, nhưng cũng phải cẩn trọng nếu hàm của bạn được gọi đệ quy vì nó dễ gây stack overflow. Bạn cũng cần tránh khai báo nó bên trong vòng lặp vì lý do tương tự.
* Exception là một thứ ‘đắt đỏ’ trong .NET, nó phải thu thập nhiều thông tin như stack trace, thông tin mã nguồn…, vì vậy không nên lạm dụng. Ta chỉ nên sử dụng new Exception trong những trường hợp thực sự là ngoại lệ, hiếm gặp.
* Quá trình dọn rác trên heap tiêu tốn rất nhiều tài nguyên, vậy nên càng ít tạo object mới càng tốt.
Sau 3 tuần thì Websocket middleware đã sẵn sàng. Phần khó nhất khi viết module này là làm sao xử lý request từ dạng request/response sang 1 socket cho phép gửi/nhận bất kỳ dạng dữ liệu nào. Nếu các bạn có theo dõi các commit ban đầu thì sẽ thấy mình đã phải thử một số cách khác nhau để chọn ra cách cuối cùng.
Giải pháp mình đưa ra là cung cấp một IWebSocketManager cho IMiniAppContext, IWebSocketManager mặc nhiên sẽ luôn trả về false khi gọi đến IsUpgradeRequest, và throw một InvalidOperationException nếu gọi đến AcceptAsync.
Khi các bạn thêm WebSocket middleware (gọi services.AddWebSocketService() và appBuilder.UseWebSockets()), nó sẽ theo dõi request và kiểm tra xem request đó có phải là một yêu cầu nâng cấp HTTP request lên websocket request không, nếu đúng là vậy thì nó sẽ thay thế IWebSocketManager mặc nhiên với DefaultWebSocketManager, DefaultWebSocketManager sẽ có nhiệm vụ gửi lại response cho client thông báo chấp nhận kết nối và đồng thời tạo một WebSocket cho phép app của bạn tương tác với client.
Mỗi khi nghe đến SOLID hay Clean Architecture, nhiều người cứ nghĩ nó phải là cái gì đó phức tạp và khó hiểu lắm. Nhưng không, người ta luôn cố gắng làm cho mọi thứ thật đơn giản để càng nhiều người học được càng tốt, công nghệ càng dễ nắm bắt sẽ càng phổ biến. Clean Architecture cũng vậy, thực chất nó rất đơn giản và dễ hiểu, ngay cả trong cuốn sách “Clean Architecture: A Craftsman’s Guide to Software Structure and Design” thì phần nói riêng về nó cũng chỉ đâu đó tầm chục trang (trong khi cả cuốn sách là hơn 300 trang). Tất nhiên, muốn hiểu về nó bạn cũng cần phải trang bị trước những kiến thức khác, nhưng tôi muốn khẳng định rằng, nó không hề phức tạp như bạn nghĩ.
Xin giới thiệu với các bạn 1 project nhỏ demo cho Clean Architecture, dựa trên Mini-Web-Server, sử dụng MVC, và được viết ngắn gọn hết sức có thể, hầu như mọi thứ “râu ria” đều bị loại bỏ, ngay cả những phần như xác thực dữ liệu cũng không có. Mục đích là để bạn có thể tập trung cho câu hỏi: Trong Clean Architecture, cái gì sẽ nằm ở đâu?
– Tên các project được đặt theo các lớp trong Clean Architecture để giúp các bạn dễ theo dõi (Entity, UseCase, Adapter).
– Sử dụng Repository pattern để phân tách phần truy xuất dữ liệu, phần này thuộc vào tầng Adapter, nhưng mình vẫn tách ra để dễ theo dõi hơn.
– Dữ liệu được lưu trong file https://github.com/…/master/MiniShop/Data/products.json, bạn không cần cài bất cứ phần mềm CSDL nào thêm để chạy được MiniShop. Tất nhiên nếu muốn bạn có thể tạo thêm một SqlServerProductRepository chẳng hạn, và thay thế nó với InMemoryProductRepository.
– Một số phần như Authentication và Session mình thêm vào sẵn nhưng chưa dùng tới, các bạn xóa đi cũng không sao (tìm trong file Program.cs).