- بهینه سازی زمان آغاز به کار برنامه
- کاهش صفحات کاری برنامه: از آنجا که برنامه از قبل کامپایل شده، فراهم کردن صفحه بندی از ابتدای کار امر چندان دشواری نخواهد بود؛ لذا در این حالت صفحه بندی حافظه به صورت پویاتری انجام میگردد. شیوهی کار به این صورت است که اسمبلیها به چندین پروسهی کاری کوچکتر تبدیل شده تا صفحه بندی هر کدام جدا صورت گیرد و محدودهی صفحه بندی کوچکتر میشود. در نتیجه کمتر نقصی در صفحه بندی دیده شده یا کلا دیده نخواهد شد. نتیجهی کار هم در یک فایل ذخیره میگردد که این فایل میتواند نگاشت به حافظه شود تا این قسمت از حافظه به طور اشتراکی مورد استفاده قرار گیرد و بدین صورت نیست که هر پروسهای برای خودش قسمتی را گرفته باشد.
%SystemRoot%\Assembly\NativeImages_v4.0.#####_64
معایب
احتمالا شما پیش خود میگویید این مورد فوق العاده امکان جالبی هست. کدها از قبل تبدیل شدهاند و دیگر فرآیند جیت صورت نمیگیرد. در صورتیکه ما تمامی امکانات یک CLR مثل مدیریت استثناءها و GC و ... را داریم، ولی غیر از این یک مشکلاتی هم به کارمان اضافه میشود که در زیر به آنها اشاره میکنیم:
عدم محافظت از کد در برابر بیگانگان: بعضیها تصور میکنند که این کد را میتوانند روی ماشین شخصی خود کامپایل کرده و فایل ngen را همراه با آن ارسال کنند. در این صورت کد IL نخواهد بود ولی موضوع این هست اینکار غیر ممکن است و هنوز استفاده از اطلاعات متادیتاها پابرجاست به خصوص در مورد اطلاعات چون reflection و serializationها . پس کد IL کماکان همراهش هست. نکتهی بعدی اینکه انتقال هم ممکن نیست؛ بنا به شرایطی که در مورد بعدی دلیل آن را متوجه خواهید شد.
از سینک با سیستم خارج میشوند: موقعیکه CLR، اسمبلیها را به داخل حافظه بار میکند، یک سری خصوصیات محیط فعلی را با زمانیکه عملیات تبدیل IL به کد ماشین صورت گرفته است، چک میکند. اگر این خصوصیات هیچ تطابقی نداشته باشند، عملیات JIT همانند سابق انجام میگردد. خصوصیات و ویژگیهایی که چک میشوند به شرح زیر هستند:
- ورژن CLR: در صورت تغییر، حتی با پچها و سرویس پک ها.
- نوع پردازنده: در صورت تغییر پردازنده یا ارتقا سخت افزاری.
- نسخه سیستم عامل : ارتقاء با سرویس پک ها.
- MVID یا Assemblies Identity module Version Id: در صورت کامپایل مجدد تغییر میکند.
- Referenced Assembly's version ID: در صورت کامپایل مجدد اسمبلی ارجاع شده.
- تغییر مجوزها: در صورتی که تغییری نسبت به اولین بار رخ دهد؛ مثلا در قسمت قبلی در مورد اجازه نامه اجرای کدهای ناامن صحبت کردیم. برای نمونه اگر در همین اجازه نامه تغییری رخ دهد، یا هر نوع اجازه نامه دیگری، برنامه مثل سابق (جیت) اجرا خواهد شد.
پی نوشت: در آپدیتهای دات نت فریم ورک به طور خودکار ابزار ngen صدا زده شده و اسمبلیها مجددا کمپایل و دخیره میشوند و برنامه سینک و آپدیت باقی خواهد ماند.
پس حتما نسخهی ngen شده و غیر ngen را بررسی کنید و کارآیی هر دو را با هم مقایسه کنید. برای بسیاری از برنامهها کاهش صفحه بندی یک مزیت و باعث بهبود کارآیی میشود. در نتیجه در این قسمت ngen برنده اعلام میشود.
توجه کنید برای سیستمهایی که در سمت سرور به فعالیت میپردازند، از آنجا که تنها اولین درخواست برای اولین کاربر کمی زمان میبرد و برای باقی کاربران درخواست با سرعت بالاتری اجرا میگردد و اینکه برای بیشتر برنامههای تحت سرور از آنجا که تنها یک نسخه در حال اجراست، هیچ مزیت صفحه بندی را ngen ایجاد نمیکند.
برای بسیاری از برنامههای کلاینت که تجربهی startup طولانی دارند، مایکروسافت ابزاری را به نام Managed Profile Guided Optimization Tool یا MPGO .exe دارد. این ابزار به تحلیل اجرای برنامه شما پرداخته و بررسی میکند که در زمان آغازین برنامه چه چیزهایی نیاز است. اطلاعات به دست آمده از تحلیل به سمت ngen فرستاده شده تا کد بومی بهینهتری تولید گردد. موقعیکه شما آماده ارائه برنامه خود هستید، برنامه را از طریق این تحلیل و اجرا کرده و با قسمتهای اساسی برنامه کار کنید. با این کار اطلاعاتی در مورد اجرای برنامه در داخل یک پروفایل embed شده در اسمبلی، قرار گرفته و ngen موقع تولید کد، این پروفایل را جهت تولید کد بهینه مطالعه خواهد کرد.
در مقالهی بعدی در مورد FCL صحبتهایی خواهیم کرد.
به زودی: ارائهی کرنل لینوکس به عنوان یک کامپوننت ویندوز
using System; using SimpleCqrs.Domain; namespace CqrsPattern.Cqrs.Command { public class Account : AggregateRoot { public Account(Guid id) { Apply(new AccountCreatedEvent { AggregateRootId = id }); } public void SetName(string firstName, string lastName) { Apply(new AccountNameSetEvent { FirstName = firstName, LastName = lastName }); } public void OnAccountCreated(AccountCreatedEvent evt) { Id = evt.AggregateRootId; } } }
using SimpleCqrs.Commanding; namespace CqrsPattern.Cqrs.Command { public class CreateAccountCommand : ICommand { public string FirstName { get; set; } public string LastName { get; set; } } }
using System; using SimpleCqrs.Commanding; using SimpleCqrs.Domain; namespace CqrsPattern.Cqrs.Command { public class CreateAccountCommandHandler : CommandHandler<CreateAccountCommand> { private readonly IDomainRepository repository; public CreateAccountCommandHandler(IDomainRepository repository) { this.repository = repository; } public override void Handle(CreateAccountCommand command) { var account = new Account(Guid.NewGuid()); account.SetName(command.FirstName, command.LastName); repository.Save(account); } } }
using SimpleCqrs.Eventing; namespace CqrsPattern.Cqrs.Command { public class AccountCreatedEvent : DomainEvent { } }
using SimpleCqrs.Eventing; namespace CqrsPattern.Cqrs.Command { public class AccountNameSetEvent : DomainEvent { public string FirstName { get; set; } public string LastName { get; set; } } }
using System.Linq; using SimpleCqrs.Eventing; using CqrsPattern.Cqrs.Db; namespace CqrsPattern.Cqrs.Command { public class AccountEventHandler : IHandleDomainEvents<AccountCreatedEvent>, IHandleDomainEvents<AccountNameSetEvent> { private readonly FakeAccountTable accountTable; public AccountEventHandler(FakeAccountTable accountTable) { this.accountTable = accountTable; } public void Handle(AccountCreatedEvent domainEvent) { accountTable.Add(new FakeAccountTableRow { Id = domainEvent.AggregateRootId }); } public void Handle(AccountNameSetEvent domainEvent) { var account = accountTable.Single(x => x.Id == domainEvent.AggregateRootId); account.Name = domainEvent.FirstName + " " + domainEvent.LastName; } } }
using System.Collections.Generic; namespace CqrsPattern.Cqrs.Db { public class FakeAccountTable : List<FakeAccountTableRow> { } }
using System; namespace CqrsPattern.Cqrs.Db { public class FakeAccountTableRow { public Guid Id { get; set; } public string Name { get; set; } } }
using SimpleCqrs; using SimpleCqrs.Unity; namespace CqrsPattern { public class SampleRunTime : SimpleCqrsRuntime<UnityServiceLocator> { } }
using System; namespace CqrsPattern.Cqrs.Query { public class AccountReadModel { public string Name { get; set; } public Guid Id { get; set; } } }
using CqrsPattern.Cqrs.Db; using System.Collections.Generic; using System.Linq; namespace CqrsPattern.Cqrs.Query { public class AccountReportReadService { private FakeAccountTable fakeAccountDb; public AccountReportReadService(FakeAccountTable fakeAccountDb) { this.fakeAccountDb = fakeAccountDb; } public IEnumerable<AccountReadModel> GetAccounts() { return from a in fakeAccountDb select new AccountReadModel { Id = a.Id, Name = a.Name }; } } }
using System; using SimpleCqrs.Commanding; using CqrsPattern.Cqrs.Query; using CqrsPattern.Cqrs.Command; namespace CqrsPattern { class Program { static void Main(string[] args) { var runtime = new SampleRunTime(); runtime.Start(); var fakeAccountTable = new FakeAccountTable(); runtime.ServiceLocator.Register(fakeAccountTable); runtime.ServiceLocator.Register(new AccountReportReadService(fakeAccountTable)); var commandBus = runtime.ServiceLocator.Resolve<ICommandBus>(); var cmd = new CreateAccountCommand { FirstName = "Ali", LastName = "Kh" }; commandBus.Send(cmd); var accountReportReadModel = runtime.ServiceLocator.Resolve<AccountReportReadService>(); Console.WriteLine("Accounts in database"); Console.WriteLine("####################"); foreach (var account in accountReportReadModel.GetAccounts()) { Console.WriteLine(" Id: {0} Name: {1}", account.Id, account.Name); } runtime.Shutdown(); Console.ReadLine(); } } }
public override void Handle(CreateAccountCommand command) { var account = new Account(Guid.NewGuid()); //line 1 account.SetName(command.FirstName, command.LastName); //line 2 repository.Save(account); //line 3 }
public Account(Guid id) { Apply(new AccountCreatedEvent { AggregateRootId = id }); }
public void SetName(string firstName, string lastName) { Apply(new AccountNameSetEvent { FirstName = firstName, LastName = lastName }); }
public void Handle(AccountCreatedEvent domainEvent) { accountTable.Add(new FakeAccountTableRow { Id = domainEvent.AggregateRootId }); } public void Handle(AccountNameSetEvent domainEvent) { var account = accountTable.Single(x => x.Id == domainEvent.AggregateRootId); account.Name = domainEvent.FirstName + " " + domainEvent.LastName; }
var accountReportReadModel = runtime.ServiceLocator.Resolve<AccountReportReadService>(); var accounts = accountReportReadModel.GetAccounts();
<html> <body> <div> Prevent default: <input type="checkbox" id="keydownStop" value="1" /> keydown <input type="checkbox" id="keypressStop" value="1" /> keypress <input type="checkbox" id="keyupStop" value="1" /> keyup </div> Ignore: <input type="checkbox" id="keydownIgnore" value="1" /> keydown <input type="checkbox" id="keypressIgnore" value="1" /> keypress <input type="checkbox" id="keyupIgnore" value="1" /> keyup <div> Focus on the input below and press any key. </div> <div> <input type="text" style=" width: 600px" id="keyInput" /> </div> Log: <div> <textarea id="keyLogger" rows="18" onfocus="this.blur()" style="width: 600px; border: 1px solid black"></textarea> </div> <input type="button" value="Clear" onclick="clearLog()" /> <script type="text/javascript"> document.getElementById('keyInput').onkeydown = keyHandler; document.getElementById('keyInput').onkeyup = keyHandler; document.getElementById('keyInput').onkeypress = keyHandler; document.getElementById('keyInput').focus(); function keyHandler(e) { e = e || window.event; if (document.getElementById(e.type + 'Ignore').checked) return; var evt = e.type; while (evt.length < 10) evt += ' ' + log(evt + ' keyCode=' + e.keyCode + ' which=' + e.which + ' charCode=' + e.charCode + ' char=' + String.fromCharCode(e.keyCode || e.charCode) + (e.shiftKey ? ' +shift' : '') + (e.ctrlKey ? ' +ctrl' : '') + (e.altKey ? ' +alt' : '') + (e.metaKey ? ' +meta' : '')); if (document.getElementById(e.type + 'Stop').checked) { e.preventDefault ? e.preventDefault() : (e.returnValue = false); } } function clearLog() { document.getElementById('keyLogger').value = ''; document.getElementById('keyInput').focus(); } function log(text) { var area = document.getElementById('keyLogger'); area.value += text + '\n'; area.scrollTop = area.scrollHeight; } </script> </body> </html>
<!doctype html>
document.onkeydown = function (e) { e = e || event; console.log(e); }
function getCharacter(event) { if (event.which == null) return String.fromCharCode(event.keyCode); // IE else if (event.which != 0 && event.charCode != 0) return String.fromCharCode(event.which); // All others return null; // special key }
<html> <body> <input id="inputText" type="text" /> <script> document.onkeydown = keyDown; document.onkeypress = keyPress; document.onkeyup = keyUp; function keyDown(e) { var input = document.getElementById('inputText'); input.value = 'keyDown started ...'; input.disabled = true; var j = 0; for (var i = 0; i < 999999999; i++) { j = i - j; } console.log(j); //alert('keyDown'); input.value = 'keyDown finished.'; input.disabled = false; } function keyPress(e) { alert('keyPress'); //console.log('keyPress'); } function keyUp(e) { alert('keyUp'); //console.log('keyUp'); } </script> </body> </html>
<html> <head> <script type="text/javascript"> function process() { var above = 0, below = 0; for (var i = 0; i < 200000; i++) { if (Math.random() * 2 > 1) { above++; } else { below++; } } } function test() { var result1 = document.getElementById('log'); var start = new Date().getTime(); console.log('start'); for (var i = 0; i < 200; i++) { result1.value = 'time=' + (new Date().getTime() - start) + ' [i=' + i + ']'; process(); } result1.value = 'time=' + (new Date().getTime() - start) + ' [done]'; console.log('end'); } window.onload = test; </script> </head> <body> <input id='log' /> </body> </html>
<input onkeydown="return false" /> <input onkeypress="return false" />
document.getElementById('myInputText').onkeypress = function (e) { var char = getCharacter(e || window.event); if (!char) return; // special key this.value += char.toUpperCase(); return false; // برای اینکه کاراکتر اضافی نمایش داده نشود }
e ? e : window.event;
document.getElementById('numberInputText').onkeypress = function (e) { e = e || window.event; var chr = getCharacter(e); if (!isNumeric(chr) && chr !== null) return false; } function isNumeric(n) { return !isNaN(parseFloat(n)) && isFinite(n); } function isNumber(val) { return val !== "NaN" && (+val) + '' === val + '' }
0==false // true 0===false // false, because they are of a different type 1=="1" // true, auto type coercion 1==="1" // false, because they are of a different type
e.ctrlKey && e.keyCode == 'A'.charCodeAt(0)
<html> <body> <div id="dotnettips" style="width: 35px; height: 35px; background-image: url(https://www.dntips.ir/favicon.ico); position: absolute; left: 10px; top: 10px;" tabindex="0"> </div> <script> document.getElementById('dotnettips').onkeydown = function (e) { e = e || event; switch (e.keyCode) { case 37: // left this.style.left = parseInt(this.style.left) - this.offsetWidth + 'px'; return false; case 38: // up this.style.top = parseInt(this.style.top) - this.offsetHeight + 'px'; return false; case 39: // right this.style.left = parseInt(this.style.left) + this.offsetWidth + 'px'; return false; case 40: // down this.style.top = parseInt(this.style.top) + this.offsetHeight + 'px'; return false; } } </script> </body> </html>
<html> <body> <input id="inputText" type="text" /> <div id="divCapsLock" style="color: Red; display: none;">Caps Lock is ON!</div> <script> var capsLock = null; document.onkeypress = keyPress; document.onkeydown = keyDown; function keyDown(e) { e = e || event; capsLock = (e.keyCode == 20 && capsLock !== null) ? !capsLock : capsLock; document.getElementById('divCapsLock').style.display = capsLock ? 'block' : 'none'; } function keyPress(e) { if (capsLock != null) return; e = e || window.event; var charCode = e.charCode || e.keyCode; capsLock = (charCode >= 97 && charCode <= 122 && e.shiftKey) || (charCode >= 65 && charCode <= 90 && !e.shiftKey); document.getElementById('divCapsLock').style.display = capsLock ? 'block' : 'none'; } </script> </body> </html>
<html> <body> <div id='divInput'> <input id="inputText" type="text" /> </div> <script> document.getElementById('inputText').onkeydown = inputKeyDown; document.getElementById('divInput').onkeydown = divKeyDown; document.onkeydown = documentKeyDown; window.onkeydown = windowKeyDown; function divKeyDown(e) { console.log('divKeyDown'); } function inputKeyDown(e) { console.log('inputKeyDown'); } function documentKeyDown(e) { console.log('documentKeyDown'); } function windowKeyDown(e) { console.log('windowKeyDown'); } </script> </body> </html>
<html> <body> <input id="inputText" type="text" /> <div id="divCapsLock" style="color: Red; display: none;">Caps Lock is ON!</div> <script> var capsLock = null; var hasFocus = false; document.onkeyup = keyUp; document.onkeypress = keyPress; document.getElementById('inputText').onfocus = focus; document.getElementById('inputText').onblur = focus; function warnCapsLock() { document.getElementById('divCapsLock').style.display = (capsLock != null && capsLock && hasFocus) ? 'block' : 'none'; } function focus() { hasFocus = !hasFocus; warnCapsLock(); } function keyUp(e) { e = e || event; capsLock = (e.keyCode == 20 && capsLock !== null) ? !capsLock : capsLock; warnCapsLock(); } function keyPress(e) { if (capsLock != null) return; e = e || window.event; var charCode = e.charCode || e.keyCode; capsLock = (charCode >= 97 && charCode <= 122 && e.shiftKey) || (charCode >= 65 && charCode <= 90 && !e.shiftKey); warnCapsLock(); } </script> </body> </html>
window.onblur = function () { capsLock = null; }
var Client = {}; Client.Keyboard = {}; Client.Keyboard.EnableKeyDown = true; Client.Keyboard.EnableKeyUp = false; Client.Keyboard.CurrentKeyEvent = null; window.onkeydown = function (event) { if (!Client.Keyboard.EnableKeyDown) return true; return KeyboardEvents(event); }; window.onkeyup = function (event) { if (!Client.Keyboard.EnableKeyUp) return true; return KeyboardEvents(event); }; function Rise(event) { var e = Client.Keyboard.CurrentKeyEvent; if (event) { var data = { shift: e.shiftKey, ctrl: e.ctrlKey, alt: e.altKey }; if (!event(data)) return false; return event(data); } return true; } function KeyboardEvents(e) { e = e || window.event; Client.Keyboard.CurrentKeyEvent = e; switch (e.keyCode) { case 8: return Rise(Client.Keyboard.Backspace); case 9: return Rise(Client.Keyboard.Tab); case 13: return Rise(Client.Keyboard.Enter); case 16: return Rise(Client.Keyboard.Shift); case 17: return Rise(Client.Keyboard.Ctrl); case 18: return Rise(Client.Keyboard.Alt); case 19: return Rise(Client.Keyboard.Pause); case 20: return Rise(Client.Keyboard.CapsLock); case 27: return Rise(Client.Keyboard.Esc); case 33: return Rise(Client.Keyboard.PageUp); case 34: return Rise(Client.Keyboard.PageDown); case 35: return Rise(Client.Keyboard.End); case 36: return Rise(Client.Keyboard.Home); case 37: return Rise(Client.Keyboard.Left); case 38: return Rise(Client.Keyboard.Up); case 39: return Rise(Client.Keyboard.Right); case 40: return Rise(Client.Keyboard.Down); case 44: return Rise(Client.Keyboard.PrtScr); case 45: return Rise(Client.Keyboard.Insert); case 46: return Rise(Client.Keyboard.Delete); ////////////////////////////////////////////////////////////////////////////////////////////////// case 48: return Rise(Client.Keyboard.Num0); case 49: return Rise(Client.Keyboard.Num1); case 50: return Rise(Client.Keyboard.Num2); case 51: return Rise(Client.Keyboard.Num3); case 52: return Rise(Client.Keyboard.Num4); case 53: return Rise(Client.Keyboard.Num5); case 54: return Rise(Client.Keyboard.Num6); case 55: return Rise(Client.Keyboard.Num7); case 56: return Rise(Client.Keyboard.Num8); case 57: return Rise(Client.Keyboard.Num9); ////////////////////////////////////////////////////////////////////////////////////////////////// case 65: return Rise(Client.Keyboard.A); case 66: return Rise(Client.Keyboard.B); case 67: return Rise(Client.Keyboard.C); case 68: return Rise(Client.Keyboard.D); case 69: return Rise(Client.Keyboard.E); case 70: return Rise(Client.Keyboard.F); case 71: return Rise(Client.Keyboard.G); case 72: return Rise(Client.Keyboard.H); case 73: return Rise(Client.Keyboard.I); case 74: return Rise(Client.Keyboard.J); case 75: return Rise(Client.Keyboard.K); case 76: return Rise(Client.Keyboard.L); case 77: return Rise(Client.Keyboard.M); case 78: return Rise(Client.Keyboard.N); case 79: return Rise(Client.Keyboard.O); case 80: return Rise(Client.Keyboard.P); case 81: return Rise(Client.Keyboard.Q); case 82: return Rise(Client.Keyboard.R); case 83: return Rise(Client.Keyboard.S); case 84: return Rise(Client.Keyboard.T); case 85: return Rise(Client.Keyboard.U); case 86: return Rise(Client.Keyboard.V); case 87: return Rise(Client.Keyboard.W); case 88: return Rise(Client.Keyboard.X); case 89: return Rise(Client.Keyboard.Y); case 90: return Rise(Client.Keyboard.Z); //////////////////////////////////////////////////////////////////////////// case 91: //case 219: // opera return Rise(Client.Keyboard.LeftWindow); case 92: return Rise(Client.Keyboard.RightWindow); case 93: return Rise(Client.Keyboard.ContextMenu); //////////////////////////////////////////////////////////////////////////// case 96: return Rise(Client.Keyboard.NumPad0); case 97: return Rise(Client.Keyboard.NumPad1); case 98: return Rise(Client.Keyboard.NumPad2); case 99: return Rise(Client.Keyboard.NumPad3); case 100: return Rise(Client.Keyboard.NumPad4); case 101: return Rise(Client.Keyboard.NumPad5); case 102: return Rise(Client.Keyboard.NumPad6); case 103: return Rise(Client.Keyboard.NumPad7); case 104: return Rise(Client.Keyboard.NumPad8); case 105: return Rise(Client.Keyboard.NumPad9); //////////////////////////////////////////////////////////////////////////// case 106: return Rise(Client.Keyboard.Multiply); case 107: return Rise(Client.Keyboard.Add); case 109: return Rise(Client.Keyboard.Subtract); case 110: return Rise(Client.Keyboard.DecimalPoint); case 111: return Rise(Client.Keyboard.Divide); //////////////////////////////////////////////////////////////////////////// case 112: return Rise(Client.Keyboard.F1); case 113: return Rise(Client.Keyboard.F2); case 114: return Rise(Client.Keyboard.F3); case 115: return Rise(Client.Keyboard.F4); case 116: return Rise(Client.Keyboard.F5); case 117: return Rise(Client.Keyboard.F6); case 118: return Rise(Client.Keyboard.F7); case 119: return Rise(Client.Keyboard.F8); case 120: return Rise(Client.Keyboard.F9); case 121: return Rise(Client.Keyboard.F10); case 122: return Rise(Client.Keyboard.F11); case 123: return Rise(Client.Keyboard.F12); //////////////////////////////////////////////////////////////////////////// case 144: return Rise(Client.Keyboard.NumLock); case 145: return Rise(Client.Keyboard.ScrollLock); case 186: case 59: // opera & firefox return Rise(Client.Keyboard.SemiColon); case 187: case 61: // opera //case 107: //firefox return Rise(Client.Keyboard.Equal); case 188: return Rise(Client.Keyboard.Camma); case 189: return Rise(Client.Keyboard.Dash); case 190: return Rise(Client.Keyboard.Period); case 191: return Rise(Client.Keyboard.Slash); case 192: return Rise(Client.Keyboard.GraveAccent); case 219: return Rise(Client.Keyboard.OpenBracket); case 220: return Rise(Client.Keyboard.BackSlash); case 221: return Rise(Client.Keyboard.CloseBracket); case 222: return Rise(Client.Keyboard.SingleQuote); } };
// ctrl + s Client.Keyboard.S = function (e) { if (e.ctrl) { // انجام عملیات موردنظر } return true; }
- آدرسهای مجازی به صورت پیوسته و پشت سر هم هستند و آدرس دهی بسیار راحت است ولی دادهها بر روی یک حافظه به صورت متصل به هم یا پیوسته ذخیره یا خوانده نمیشوند و کار آدرس دهی مشکل است. پس یکی از مزایای داشتن آدرس دهی مجازی پشت سر هم قرار گرفتن آدرس هاست.
- برنامه از آدرسهای مجازی برای دسترسی به بافر حافظه استفاده میکند که بزرگتر از حافظه فیزیکی موجود هست. موقعی که نیاز به حافظه بیشتر باشد و حافظه سیستم کوچکتر یا کمتر از تقاضا باشد، مدیر حافظه، صفحات حافظه فیزیکی را به صورت یک فایل (عموما 4 کیلیویی) بر روی دیسک سخت ذخیره میکند و صفحات دادهها در موقع نیاز بین حافظه فیزیکی و دیسک سخت جابجا میشود.
- هر پردازشی که بر روی آدرسهای مجازی کار میکند ایزوله شده است. یعنی یک پروسه هیچ گاه نمیتواند به آدرسهای یک پروسه دیگر دسترسی داشته باشد و باعث تخریب دادههای آن شود.
در شکل بالا دو پروسه 64 بیتی به نامهای notepad.exe و myapp.exe قرار دارند که هر کدام فضای آدرسهای مجازی خودشان را دارند و از آدرس 0x000'0000000 شروع و تا آدرس 0x7FF'FFFFFFFF ادامه میابند. هر قسمت شامل یک صفحه 4 کیلویی از حافظه مجازی یا فیزیکی است. به برنامه نوتپد دقت کنید که از سه صفحه پشت سر هم یا پیوسته تشکیل شده که آدرس شروع آن 0x7F7'93950000 می باشد ولی در حافظه فیزیکی خبری از پیوسته بودن دیده نمیشود و حتما این نکته را متوجه شدید که هر دو پروسه از یک آدرس شروع استفاده کردهاند، ولی به آدرسی متفاوت از حافظه فیزیکی نگاشت شده اند.
فضای کاربری و فضای سیستمی User space and system space
گفتیم بسیاری از پروسهها در حالت user mode و پروسههای هسته سیستم عامل و درایورها در حالت kernel mode اجرا میشوند. هر پروسه مد کاربر از فضای آدرس دهی مجازی خودش استفاده میکند ولی در حالت کرنل همه از یک فضای آدرس دهی استفاده میکنند که به آن فضای سیستمی میگویند و برای مد کاربری میگویند فضای کاربری.
در سیستمهای 32 بیتی نهایتا تا 4 گیگ حافظه میتوان به اینها تخصیص داد؛ 2 گیگ ابتدایی به user space و دو گیگ بعدی به system space :
در ویندوزهای 32 بیتی شما امکان تغییر این مقدار حافظه را در میان بوت دارید و میتوانید حافظه کاربری را تا 3 گیگ مشخص کنید و یک گیگ را برای فضای سیستمی. برای اینکار میتوانید از برنامه bcedit استفاده کنید.
در سیستمهای 64 بیتی میزان حافظههای مجازی به صورت تئوری تا 16 اگزابایت مشخص شده است؛ ولی در عمل تنها بخش کوچکی از آن یعنی 8 ترابایت استفاده میشود.
- برنامه جهت اجرا در مد کاربر یک درخواست را برای خواندن دادههای یک device را آماده میکند. سپس برنامه آدرس شروع یک بافر را برای دریافت داده، مشخص میکند.
- وظیفه این درایور یک قطعه در مد کرنل این است که عملیات خواندن را شروع کرده و کنترل را به درخواست کننده ارسال میکند.
- بعد device یک وقفه را به هر تردی thread که در حال اجراست ارسال میکند تا بگوید، عملیات خواندن پایان یافته است. این وقفه توسط ترد درایور مربوطه دریافت میشود.
- حالا دیگر درایور نباید دادهها را در همان جایی که گام اول برنامه مشخص کرده است ذخیره کند. چون این آدرس که برنامه در مد کاربری مشخص کرده است، با نمونهای که این فرآیند محاسبه میکند متفاوت است.
تزریق خودکار وابستگیها در SignalR
- من SmObjectFactory را از این جهت اضافه کردم، چون قرار است ObjectFactory را در نگارش بعدی استراکچرمپ حذف کنند. همین مساله سبب بروز مشکلات زیادی در مدیریت Containerهای آن میشود؛ چون هر Container مشخصی، وهلههای مجزا و تنظیمات مجزایی را خواهد داشت و در این حالت بسیاری با مدیریت برای مثال یک وهله از IUnitOfWork در طول یک درخواست مشکل پیدا میکنند. وهلهی a از IUnitOfWork توسط container 1 با وهله مشابهی از آن توسط container 2 یکی نیست. حتی اگر مدیریت طول عمر container 1 به درستی انجام نشود، باز هم به چندین وهله از container 1 خواهیم رسید.
public boolean isNetworkAvailable(Context context) { ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); return activeNetworkInfo != null && activeNetworkInfo.isConnected(); }
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
البته توصیه ما نیز استفاده از این کد هست و توصیه میشود که استثناءها یا وضعیت خروجیها را کنترل نمایید. روشهای زیر تنها تحلیل کوچکی برای بررسی وضعیت اینترنت است یا اینکه واقعا به این بررسیهای نیاز داشته باشید. در غیر این صورت برای بسیاری از برنامهها همین کد کفایت میکند. راه حلهای پایین مواردی است که از تجربه خود به دست آوردهام تا شاید راهنمایی برای افرادی باشد که میخواهند این کار را آغاز کنند تا در وقتشان صرفه جویی شود.
مثالهای زیر وضعیتهایی را نشان میدهند که شبکه موجود است ولی اینترنتی در دسترس نیست:
- مودم وای فای را فعال کرده است، ولی اینترنت را در اختیار ندارد که این علل میتواند عدم ثابت شدن چراغ DSL یا راه اندازی مجدد مودم باشد که وای فای زودتر از DSL فعال میشود و یا اینکه اشتراک شما تمام شده است یا در شبکه به مشکل برخورد کردهاید.
- شما از طریق mobile data قصد اتصال دارید. در این حالت یا اعتبار شما پایان یافته است یا شبکه آنان دچار اختلال است.
- شما در یک محیط اداری هستید که به عنوان مثال سیستمشان توسط روترهای میکروتیک هدایت میشود. در این حالت شما میتوانید وارد شبکه بدون کلمه عبور آنان شوید. ولی نیاز دارید که حتما صفحه لاگین را رد نمایید تا اینترنت در اختیار شما قرار بگیرد.
در همه مثالهای بالا اینترنتی وجود ندارد، ولی تکه کد بالا true را برخواهد گرداند.
public boolean IsInternetConnected() { try { Process ipProcess = Runtime.getRuntime().exec("/system/bin/ping -c 1 4.2.2.4"); int exitValue = ipProcess.waitFor(); return (exitValue == 0); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } return false; }
System.Diagnostics.Process.Start("processName");
فرمان یا دستور بالا به شرح زیر است:
در سیستم عامل لینکوس تمام برنامههای سیستمی مورد نیاز، در شاخه bin قرار میگیرند و پینگ، یکی از آن هاست. سوییچ c هم که مخفف count است، به معنی تعداد درخواستهای یک اکو است که در این دستور گفتهایم تنها یک درخواست اکو echo ارسال کن. (ااطلاعات بیشتر در مورد دستور پینگ ).
در خط بعدی از آنجا که این دستور، یک دستور زمان بر است، باید مدتی در این کد توقف شود تا مقدار مورد نظر دریافت شود. در صورتی که مقدار 0 بازگردانده شود، اکو پاسخ داده شده است و یعنی اینکه شما به اینترنت متصلید. (مشاهده کدهای وضعیتی ICMP )
وجود catchهای بالا الزامی است از آنجا که متدهای استفاده شده توسط استثناءهای زیر throw شدهاند، جاوا شما را ملزم به استفاده از catchهای این استثناها خواهد کرد.
public Process exec(String prog) throws java.io.IOException { return exec(prog, null, null); }
public abstract int waitFor() throws InterruptedException;
برای اجرا این تکه کد شما نیاز به مجوز اتصال به اینترنت دارید:
<uses-permission android:name="android.permission.INTERNET"/>
نکته مهم اینکه نگران اجرای این دستور در گوشی کاربر نباشید. این دستور نیاز به مجوز روت ندارد.
تکه کد زیر صفحه گوگل را درخواست میکند و در نهایت وضعیت کد http آن را دریافت میکنیم و اگر این کد وضعیت برابر 200 بود به این معنی است که اینترنت متصل میباشد. ولی در یک سیستم میکروتیک که هنوز وارد سیستم آن نشده باشید، به صفحه لاگین هدایت میشوید و وضعیت دیگری را دریافت خواهید مانند آدرس درخواستی شما redirect شده است یا اینکه باز هم کد 200 را دریافت میکنید که در بیشتر حالات هم به همین شکل است. برای رفع این مسئله بهتر است url فعلی را با url درخواستی مطابقت دهیم. برای این قضیه گوگل در بخش Handling Network Sign-On این صفحه چنین کدی را پیشنهاد داده است:
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); try { InputStream in = new BufferedInputStream(urlConnection.getInputStream()); if (!url.getHost().equals(urlConnection.getURL().getHost())) { // we were redirected! Kick the user out to the browser to sign on? ... } finally { urlConnection.disconnect(); } }
ولی با توجه به تحقیقات و مشاهداتی که کردهام، در بعضی از گوشیها این کد کارکرد مناسبی ندارد و برای خودم هم پاسخی دریافت نکردم. اگر واقعا باز هم مصر هستید که این وضعیت را بررسی کنید، میتواند بررسی یک url با محتوای خاص باشد و بعد از دریافت این صفحه محتوای آن را بررسی کنید.
ولی با این همه بسیاری از برنامهها از همان تکه کد بالا استفاده میکنند و با مدیریت استثناءها سعی در جلوگیری از خطا دارند. در غیر این صورت شما باید مدام در حال بررسی وضعیت اینترنت به شکل بالا باشید که بسیار زمان بر خواهد شد.
در صورتی که شما روش بهتری را برای بررسی وضعیت اینترنت دارید یا راه حل خاصی به نظرتان میرسد بسیار عالی خواهد بود که آن را با ما به اشتراک بگذارید.
{ "name": "dntwebpack", "version": "1.0.0", "description": "a webpack tutorial", "main": "main.js", "scripts": { }, "author": "mehdi", "license": "MIT" }
<html> <!-- index.html --> <head> first part of webpack tut! </head> <body> <h1>webpack is awesome !</h1> <script src="bundle.js"></script> </body> </html>
//main.js //start of the journey with webpack console.log(`i'm bundled by webpack`);
فایل bundle.js را تولید میکنیم.
{ "name": "dntwebpack", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" ,"webpack":"webpack" }, "author": "mehdi", "license": "ISC", "devDependencies": { "webpack": "^1.13.1" } }