sex hikayeescort şişliavcılar escortzeytinburnu escorttopkapı escortkurtköy escorteminönü escortcihangir escortcevizlibağ escortçengelköy escortbeyoğlu escortcebeci escortistanbul escortdudullu escortbomonti escortdragos escortbeykoz escortçapa escortatalar escortçağlayan escortbeyazıt escortistanbul escortcaddebostan escortbeşiktaş escortbüyükdere escortbebek escortchatbeşiktaş escortbayrampaşa escortkurtköy escortbaşakşehir escortbakırköy escortbalmumcu escortbahçeşehir escortanadoluhisarı escortbahçelievler escortbayrampaşa escortsohbetbahçeköy escortbahariye escortataşehir escortarnavutköy escortanal escortacıbadem escortaltınşehir escortkurtköy escortbodrum escortbodrum escortbodrum escortalibeyköy escortbakırköy escortpendik escortacıbadem escortadalar escortmecidiyeköy escortadapazari escortistanbul escortkemerburgaz escortedirnekapı escortataşehir escortpendik escortömerli escortkurtköy escortferiköy escortnişantaşı escortbakırköy escortbayrampaşa escortcaglayan escortalibeyköy escortmahmutbey escortşirinevler escortbeylikdüzü escortpriligy fiyatıpriligy 60 mg fiyatql 36cialis fiyatıcialis 100 mg eczane satış fiyatılifta 20 mg yorumsertleştirici hapcialis kullananlarcialis 20 mg kullanıcı yorumlarıviagra 100 mg fiyatpriligy fiyatıpriligydapoksetinpenis büyütücü kremegzama şampuanıgeciktirici hapviagra fiyatıcialis 100 mgcialis fiyatbolu escortvan escortşahinbey escortankara escortgebze escortkayseri escortmanisa escorttokat escortvan escortdiyarbakır escorthatay escortalanya escortnilüfer escortbursa escortzonguldak escortyozgat escortyalova escortvan escortuşak escorturfa escorttunceli escorttrabzon escorttokat escorttekirdağ escortşırnak escortsivas escortsinop escortsiirt escortsamsun escortsakarya escortrize escortosmaniye escortordu escortniğde escortnevşehir escortmuş escortmuğla escortmersin escortmardin escortmaraş escortmanisa escortmalatya escortkütahya escortkonya escortkocaeli escortkırşehir escortkırklareli escortkırıkkale escortkilis escortkayseri escortkastamonu escortkars escortkaraman escortkarabük escortizmir escortistanbul escortısparta escortığdır escorthatay escorthakkari escortgiresun escortgaziantep escorteskişehir escorterzurum escorterzincan escortelazığ escortedirne escortdüzce escortdiyarbakır escortdenizli escortçorum escortçankırı escortçanakkale escortbursa escortburdur escortbolu escortbitlis escortbingöl escortbilecik escortbayburt escortbatman escortbartın escortbalıkesir escortaydın escortartvin escortardahan escortantalya escortankara escortamasya escortağrı escortafyon escortadıyaman escortadana escortgümüşhane escortaksaray escortyüreğir escorttrabzon escorttekirdağ escortizmir escortselçuklu escortşehitkamil escortşanlıurfa escortsamsun escortsakarya escortrize escortosmangazi escortnilüfer escortmuğla escortmersin escortmarmaris escortmalatya escortkuşadası escortkonya escortkocasinan escortkeçiören escortkayseri escortizmit escorteskişehir escortelazığ escortdenizli escortçorlu escortçeşme escortçankaya escortbuca escortbodrum escortatakum escortgaziantep escortantalya escortantakya escortankara escortaksaray escortgebze escortalanya escortdenizli escortadıyaman escorterzurum escortçorum escortkütahya escortadıyaman escortaydın escortbalıkesir escortçanakkale escorterzurum escorthatay escortmanisa escortvan escortsivas escortsincan escortyalova escortuşak escortsivas escorturfa escortrize escortosmaniye escortordu escortnevşehir escortmuş escortmardin escortkonya escortkırşehir escortkilis escortkarabük escorthakkari escortgiresun escortantep escorteskişehir escortbursa escortbilecik escortbitlis escortbingöl escortbatman escortaydın escortağrı escortafyon escortadana escortantakya escortistanbul escortadalar escortsilivri escortanadolu yakası escortarnavutköy escortataşehir escortavcılar escortavrupa yakası escortbahçelievler escortbakırköy escortbaşakşehir escortbayramapaşa escortbeşiktaş escortbeykoz escortbeylikdüzü escortbeyoğlu escortbüyükçekmece escortçatalca escortesenler escorteyüpsultan escortesenyurt escortfatih escortkadıköy escortkağıthane escortkartal escortmaltepe escortpendik escortsarıyer escortsultanbeyli escortşile escortşişli escorttuzla escortümraniye escortüsküdar escortzeytinburnu escortbağcılar escortküçükçekmece escortbingöl escorteskişehir escortkırklareli escortçekmeköy escortsultangazi escortsancaktepe escortadana escortadıyaman escortafyon escortağrı escortaksaray escortamasra escortamasya escortankara escortantalya escortardahan escortaydın escortbartın escortbatman escortbayburt escortbilecik escortbitlis escortbolu escortburdur escortçanakkale escortçankırı escortçorum escortdenizli escortdiyarbakır escortdüzce escortedirne escortelazığ escorterzincan escorterzurum escortgaziantep escortgiresun escortgümüşhane escorthakkari escorthatay escortığdır escortısparta escortistanbul escortizmir escortmaraş escortkarabük escortkaraman escortkars escortkastamonu escortkayseri escortkırıkkale escortkilis escortkocaeli escortkonya escortkurucaşile escortkütahya escortmalatya escortmanisa escortmardin escortmersin escortmuğla escortnevşehir escortniğde escortordu escortosmaniye escortrize escortsamsun escortsiirt escortsinop escortsivas escortşırnak escorttekirdağ escorttokat escorttrabzon escorttunceli escortulus escorturfa escortuşak escortvan escortyalova escortyozgat escortzonguldak escortmecidiyeköy escortşişli bayan escortşişli escort bayanşişli escortümraniye escortdefne samyeli kimdirashley ortizdefne samyelieskişehir apartman temizliğigoogle analyticsKim KardashianGoogle Anahtar KelimeVajina kaşıntısıVajinada Kaşıntımoz rank sorgulada pa sorgulamamoz rankda pa sorgulatanıtım yazısıserel yereli kimdirtwerkgoogle search consolesite speed testserver scanRegl dönemiinstagram takipçi hilesipubg hile alkalça büyültme esteteğiseoseo nedirpussy tattooindoxploit shellc99 shellphp shellr57 shelllogsuz shell indirwso shellc99 shelltuzluca escortegil escortergani escorthani escorthazro escortkayapinar escortkocakoy escortkulp escortlice escortsilvan escortsur escortyenisehir escortakcaova escortcilimli escortcumayeri escortgolkaya escortgumusova escortkaynasli escortyigilca escortpriligy 30 mg fiyatenez escorthavsa escortipsala escortkesan escortlalapasa escortmeric escortsuluoglu escortuzunkopru escorthozan escortguney escortkale escortmerkezefendi escortpamukkale escortsaraykoy escortsaraykoy escortserinhisar escortazdırıcı kadın viagrasıtavas escortbaglar escortbismil escortilgaz escortkizilirmak escortkorgun escortorta escortsabanozu escortgoogle sıra bulucuyaprakli escortalaca escortbayat escorterotik fotograflarbogazkale escorterotik resimlersexs resimleridodurga escortiskilip escortkargi escortlacin escortmecitozu escortoguzlar escortortakoy escortosmancik escortsungurlu escortosmancik escortugurludag escortacipayam escortbabadag escortmarlboro double fusionbaklan escortbekilli escortbeyagac escortbozkurt escortbuldan escortcal escortcameli escortcardak escortcivril escortkaramanli escorttefenni escortyesilova escortbuyukorhan escortgemlik escortgursu escortharmancik escortinegol escortiznik escortkaracabey escortkeles escortkestel escortmudanya escortorhaneli escortmustafakemalpasa escortnilufer escortorhaneli escortorhagazi escortosmangazi escortyenisehir escortyildirim escortayvacik escortbayramic escortbiga escortbozcaada escortcan escorteceabat escortezine escortgelibolu escortgokceada escortlapseki escortyenice escortatcakaracalar escortbayramoren escortcerkes escorterdivan escortsogut escortadakli escortgenc escortkarliova escortkigi escortsolhan escortyayladere escortyedisu escortadilcevaz escortahlat escortlifta 5 mg 28 tablet fiyategzama şampuanıtuzsuz şampuancaptain black sigaraburun estetiğiseo araclarıbeylikdüzü escortguroymak escorthizan escortmutki escortbursa escorttatvan escortmecidiyeköy escortdortdivan escortgerede escortgoynuk escortkibriscik escortmengen escortmağaza elektrik işleriseo nasıl yapılırofis elektrik işleriofis mekanik tesisat işlerimağaza mekanik tesisat işleridedektifmudurnu escortyenicaga escortereksiyon hapları ve ilaçlarıaglasun escorterenkoy escortaltinyayla escortavcılar escortbucak escorthalkali escortescort lazımcavdir escortesenyurt escortceltikci escortgolhisar escortkepez escortkorkuteli escortkumluca escortmanavgat escortmuratpasa escorttserik escortcildir escortdamal escortgole escorthanak escortposof escortardanuc escortarhavi escortborcka escorthopa escortkemalpasa escortmurgul escortsavsat escortyusufeli escortbozdogan escortbuharkent escortgermencik escortefeler erscortefeler erscortefeler erscortincirliova escortkaracasu escortkarpuzlu escortkocarli escortkosk escortkusadasi escortaksaray escorttaksim escortkuyucak escortsultanhisar esortyenipazar escortnazilli escortsoke escortaltieylul escortayvalik escortbalya escortbandirma escortbigadic escortbigadic escortdursunbey escortedremit escorterdek escortgomec escortgonen escorthavran escortivrindi escortkaresi escortkapsut escortmanyas escortmarmara escortsavastepe escortsindirgi escortsusurluk escortamasre escortkurucasile escortulus escortbesiri escortgercus escorthasankeyf escortkozluk escortsason escortaydintepe escortdemirozu escortbozuyuk escortgolpazari escortinhisar escortosmaneli escortpazaryeri escortsoogut escortkozlu escortdevrek escortcaycuma escortyerkoy escortsorgun escortsarikaya escortciflikkoy escortakdagmadeni escortiibradi escortkas escortkemer escortfinike escortfinike escortgazipasa escortpursaklar escortsereflikochisar escortelmali escortalanya escortsincan escortyenimahalle escortdemre escortaksu escortakseki escortpolatli escortnallihan escortelmadag escortevren escortguldul escortnallihan escoortmamak escortkizilcahamam escortkecioren escortgumushacikoy escorthaymana escortkahramankazan escortetimeskut escortevren escortkalecik escortbeypazari escortcamlidere escortcankaya escortcubuk escortbala escortbala escortayas escortaltindag escortakyurt escorttasova escortgoynucek escorthamamozu escortmerzifon escortsuluova escorttasova escortdogubayazit escorteleskirt escorthamur escortpatnos escorttaslicay escortşişli escortkıbrıs escortedremit escortmarlboro shufflekurtkoy escorttaslicay escorttutak escortagacoren escorteskil escortAnadolu yakası escortümraniye escortkadıköy escortPorno izlePornoAtaşehir escortBostancı escortAnadolu yakası escortAnkara rus escortgulagac escortguzelyurt escortgoynucek escortdiyadin escortsuhut escortiscehisar escortsultanhani escortsariyahsi escortortakoy escortviransehir escortsultandagi escortbaykan escortsinanpasa escortkiziloren escortihsaniye escorthocalar escortevciiler escorttut escortbasmakci escortbasmakci escortbayat escortbolvadin escortcay escortcobanlar escortSimge Sağın kimdirdinar escortemirdag escortbogazliyan escortarapli escortevciler escortsincik escortsamsat escortkahta escortedremit escortkaraisali escortipekyolu escortaltinova escortkaratas escortarmutlu escortkozan escortozalp escortpozanti escorttusba escortercis escortsaimbeyli escortsaricam escorttufanbeyli escortyumurtalik escortbesni escortgerger escortgolbasi escortcelikhan escortyuregir escortimamoglu escortfeke escortcukurova escortceyhan escortaladag escortatabey escortegirdir escortgelendost escortcialis eczane satışkeciborlu escortsarkiraagac escortsenirkent escortsutculer escortuluborlu escortcialis siparişyapay kızlık zarıyalvac escortviagra satın aleczanede satılan cinsel ilaçlareczane viagra satışgeç boşaltan haplaryenisarbademli escortorjinal viagra satışaliaga escortbayan azdırıcıbalcova escortvega 100 mg hapbayindir escortbayan azdırıcıSelin Ciğerci kimdirseo analizbayrakli escortbeydag escortbergama escortbornova escortbuca escortGetir Çalışma Saatleriinstagram Keşfete Çıkmacesme escortcigli escortdikili escortfoca escortgaziemir escortguzelbahce escortkarabaglar escortkaraburun escortDemi RoseGoogle Adwords ile SEOkarsiyaka escortkinik escortkiraz escortWanda Icardikonak escortZararlı Bağlantıları ReddetmeKilo Vermenin Püf NoktalarıYaşlanmayı Geciktiren Besinlermenderes escortücretsiz wordpress temalarıinstagram fotoğraflarını toplu olarak bilgisayara indirmeücretsiz backlink almamenemen escortkadınlarda orgazmın püf noktalarıtüp bebek fiyatlarıBurçin Terzioğlu kimdirnarlidere escortAdsense Nedirodemis escortseferihisar escortselcuk escorttire escorttorbali escorturla escortalexa sorgulamaspam skoru sorgulamaspam skoru öğrenistanbul escortalexa sorguwho isbacklink sorgulamagoogleafsin escortandirin escortcaglayancerit escortdulkadirli escortekinokuzu escortelbistan escortnuehak escortonikisubat escortgoksun escortpazarcik escortturkoglu escorteflani escortovacik escortsafranbolu escorteskipazar escortsusuz escortayranci escortabana escortbasyayla escortarac escortazdavay escortermenek escortkazimkarebekir escortcatalzeytin escortsariveliler escortcide escortdaday escortakyaka escortarpacay escortdevrekani escortdigor escortkagizman escortsarikamis escortselim escortdoganyurt escortinebolu escorttosya escorttaskopru escortsenpazar escortseydiler escorthasonu escortakkisla escortbunyan escortishangazi escortdeveli escortkure escortsertleştirici haplarbayan azdırıcıgeciktiricilerkadın viagrasıgeciktirici krembayan azdırıcı damla isimleripembe kadın viagrasıfelahiye escorthacilar escortpinarbasi escortincesu escortkocasinan escortmelikgazi escortozvatan escortsarioglan escortsariz escorttalas escorttomarza escortyahyali escortescobar vipeybeyli escortmusabeyli escortyesilhisar escortpolateli escortbahsili escortcelebi escortdelice escortkeskin escortsulakyurt escortkarakecili escortyahsihan escortakpinar escortkazman escortmucur escortbasiskale escortcicekdagi escortcayirova escortboztepe escortyozgat escortgiresun escortip whoisdomain analizhacklink satışdomain whoistoplu whoisip sorgulaip sorgulamasunucu taramasite hız testihtml kod şifrelemeseoseo analizseo nedirseo newsadana escortadana eskorthoşgeldin bonusuhoşgeldin bonusu veren bahis sitelerikayseri escorteryaman escortbedava bonusdeneme bonususincan escortkayseri escortgaziantep escortsohbet numaralarıbedava bonus veren bahis sitelerideneme bonusu veren bahis sitelerisohbet numaralarıgaziantep escortmersin escortinstagram takipci hilesismm panelsmm panelinstagram free likessmm panelinstagram beğeni hilesiinstagram free followersinstagram takipci hilesiinstagram takipci hilesiinstagram unfollow freeinstagram Followersinstagram takipci satın alinstagram free followersinstagram takipci hilesikorsan taksikabatas escortkocaeli escortgebze escortyahya kaptan escortescort bayanseo analizphp encodeseo analysisdarinca escortgebze escorthendek escortbartin escortkirikkale escortderince escortbacklink nedirSearch Engine Optimizationdilovasi escorttokat escortadiyaman escortgolcuk escortizmit escortkandira escortsincan escorteryaman escortkızılay escortescort bayanegzama için şampuansaç egzaması için şampuanseboreik dermatit şampuanıegzama şampuanısaç uzatıcısaç vitaminibiotinsakarya escortbursa escorttuzla escortseo sorgulamaseo sorguladövme modellerivagina tattookorfez escortkartepe escortsilopi escortkaramursel escortakoren escortaksehir escortbeysehir escortbozkir escortceltik escortcumra escortgaziantep escortderbent escortderebucak escortdoganhisar escortemirgazi escorteregli escortcihanbeyli escortguneysinir escortsakarya escorthadim escortsuruc escorthalkapinar escorthuyuk escortkadinhani escortilgin escortkarapinar escortkaratay escortkulu escortsarayonu escortselcuklu escortseydisehir escorttaskent escortmeram escortaltintas escorttaskent escortemet escortgeyve escorttuzla escort bayananal yapan escortucuz escorteve gelen escortgebze escortsinirsız escort bayangölcük escortizmit escortdarıca escortgeciktirici hap eczanekocaeli escortescort bayanotele gelen escortizmir escortgörükle escortantalya escortbornova escortyunak escortetlik escortçankaya escortaslanapa escortdomanic escortbursa escort bayangediz escortmecidiyeköy escorthisarcik escorttepeören escortbursa eskortbursa escort kızyazihan escortescort bursaorhanlı escortaydıntepe escortsimav escortputurge escortsite analizhekimhan escortkuluncak escortpriligy fiyatısağlık bakanlığı onaylı eczanelerde satılan geciktiricigeciktirici hap eczane fiyatıcialis 100 mg eczane satış fiyatıviagra kullananların yorumlarıorganik hityanık yarasına ne iyi gelirElif Domaniç kimdirRobots.txt dosyası oluşturmabotoks nasıl uygulanırbotoks markalarıbotoks eskişehirhamilelikte süt yapan yiyeceklerbotoks fiyatlarısexs videohamilelikte mide yanması neden olurhamilelikte süt yapan meyvelerhamilelikte mide yanmasıgögüs büyütme yöntemlerigögüs büyütme estetiğihamilelikte mide yanmasına ne iyi gelirMeme DikleştirmeMeme Dikleştirme estetiğipopo kaldırmapopo kaldırma estetiğidoganyol escorterdemli escortworld newscialis 20 mg kullanıcı yorumlarınilüfer escortbursa merkez escortgörükle escortbursa escort bayanaltıparmak escortcialis 100 mg fiyatıev yemekleri tarifigoogle haberYozgat escortataşehir escortkartal escortbostancı escortkadıköy escortkurtköy escortpendik escortüsküdar escortümraniye escortbostancı escortgöztepe escortataşehir escortbostanci escorttuzla escortbostancı escortkartal escortbostancı escortataşehir escortkadıköy escortümraniye escortümraniye escortataşehir escortanadolu yakası escortümraniye escortataşehir escortmaltepe escortkadıköy escortkadıköy escortkurtköy escortşişli escort bayanvan escortzonguldak escortsinop escortyozgat escortsanliurfa escorttrabzon escortaydin escortcorum escorterzurum escortgiresun escortmaras escortEskişehir Web Tasarımkutahya escortSelülitten kurtulmanın yollarıSelülitten kurtulmakurtköy escortMarlboro Touch GrayParliament Night Bluekazlicesme escortistanbul escortkanlica escortbursa sinirsiz escortgebze escortdikilitaş escortcubuklu escortçamlıca escortistanbul evde masajciftehavuzlar escortpendik escorttaksim escortcasinoOlmeca Tekilapendik escortkartal escortcaptain blackEscort Bodrumbodrum escort bayanbodrum eskortbursa escort bayanbursa escort bayangörükle escortbursa sınırsız escortgemlik escortbursa merkez escortkartal escortkartal escortpendik escortkurtkoy escorttuzla escortkent d range bluekaynarca escortbostancı escortkocaeli anal yapan escortizmit sınırsız escortizmit otele gelen escortgölcük escortizmit kendi evi olan escortpendik escortbursa escortbodrum escortbursa merkez escortcialis siparişcialis 20 mgcialisMetin2 pvpcinsel hastalıklarviagra siparişlifta 20 mgAnahtar Kelime Analizikocaeli esgortcialis fiyatcialis 5 mg fiyatcialis 5 mgviagra 100 mg fiyatcialis jelviagra satın alviagra fiyatıdegravigrandedegra 100 mgorcafilcialis nedircialis 20 mg eczane fiyatıcialis fiyatcialis 20 mgcialis 100 mgviagra fiyatliftalifta 5 mgcialis siparişgeciktiricigeciktirici mendildelay spreystag spreynely8viga spreyviga kremgeciktirici spreygeciktirici kremnovagraviagra 100 mg fiyatorijinal viagraviagra 100 mgeczane viagraviagra siparişviagra satın alviagra nedirviagra yorumviagra fiyatviagra fiyatı 2021cialiscialis fiyatcialis 20 mgcialis 100 mgcialis nedircialis 5 mg fiyatıcialis hapcialis 5 mgcialis 20 mg eczane fiyatıcialis 100 mg fiyatcialis satışjeligravigaroocombo 100 mgvigrande 100 mgvigrandesinegrasildegrasildegra 100 mgdegra 100 mgdegra fiyatlifta 20 mgflynta 20 mglifta 5 mghardcisflynta 5 mgorcafil 5 mgEreksiyon Haplarıkamagra nedirsüper kamagrakamagra 100mg fiyatkamagra 100mgcialis eczanekamagra fiyatıkamagra jel fiyatıkamagra fiyatkamagra siparişizmit escortkamagra jel siparişkamagra jelkamagrakamagra satın alonline eczane viagrapfizer viagra satışviagra satın alviagra eczaneviagra fiyatviagra fiyatlarıcialis fiyatıcialis fiyatlarıcialis satışcialis eczanecialis 5 mgcialis 20cialis hapcialis 100 mgcialis 20 mgviagra eczaneonline eczane viagraviagra satışviagra fiyatlarıcialis 100 mg fiyatizmit eve gelen escortcialis 5 mg fiyatıataşehir escortümraniye escortescort kocaeliAnkara Araç KiralamaBursa Araç KiralamaBursa Araç Kiralamaİstanbul Araç KiralamaEskişehir Araç Kiralamaeskişehir rent a cartitan gelistanbul escortbusa escortyapay kızlık zarıhtml kod şifrelemebacklink alsunucu taramawordpress ücretsiz temaizmit escort bayankocaeli escortkocaeli sınırsız escorthacklink satışbacklink saleshtml code encryptionwordpress themeswordpress free temaWordpress Free Themesseo analysisbodrum escortfree shop sigaramugla escortistanbul escortantalya escortmarmaris escortserdivan escortescort bayanzonguldak escortyozgat escortyalova escortvan escortadana escortuşak escortadıyaman escorttunceli escorttrabzon escorttokat escorttekirdağ escortsivas escortafyon escortşırnak escortsinop escortağrı escortsiirt escortsakarya escortsamsun escorturfa escortrize escortaksaray escortamasya escortankara escortosmaniye escortordu escortardahan escortniğde escortmuş escortnevşehir escortantalya escortmuğla escortmersin escortmardin escortartvin escortaydın escortmaraş escortbalıkesir escortbartın escortizmir escortbatman escortmanisa escortbayburt escortmalatya escortkütahya escortkarabük escortkaraman escortkars escortkastamonu escortkayseri escortbilecik escortkonya escortkocaeli escortkırşehir escortkilis escortkırklareli escortkırıkkale escortbingöl escortistanbul escortbitlis escortbolu escortburdur escorthakkari escortisparta escortbursa escortçanakkale escortçankırı escortçorum escortdenizli escortedirne escortelazığ escortığdır escorthatay escorterzincan escorterzurum escortgümüşhane escortgiresun escorteskişehir escortgaziantep escortpasta tarifiyemek tarifleriyemek tarifi altatlı tarifiyemek tarifihacklink satışhacklink panelsite analizsunucu taraescort bayansakarya escortsakarya escort bayanadapazarı escortşişli escortmozrankPablo EscobarAdsense onayı nasıl alınırmarmaris escortdüzce escortdiyarbakır escortcratosslotbacklink satışhacklinkhacklink alhacklink nedirhacklink panelbuy backlinksbacklinkDavidoff Sigaradjarum blackmarlboro sigaraMarlboro Touch Mix Slender SigaraMarlboro Edge Slimmarlboro shuffleMarlboro Touch BlueMarlboro Double Fusion Summer KarpuzluMarlboro Winter ShuffleMarlboro Touch AquaMarlboro Double FusionMarlboro GoldMarlboro Vista Blossom Fusion SigaraMarlboro Edge Sunset Superslim Sigara Kavun ve MentolMarlboro Silver Blue Sigarablack devil sigaraCavallo SigaraOscar Nano Blueberry Yaban Mersinli SigaraMarlboro Luna Shuffle SigaraMarlboro White SigaraMarlboro White Fresh mentolluHEETS Amber SelectionCavallo Sync MentollüCavallo Strawberry Superslim Sigarawinston slender blueWinston Dark BlueWinston Expand Duo SigaraWinston XS Blue superslimCavallo Clip Go Sigara Yabanmersini AromalıCavallo by Vasily Vinteroff Mentollü SigaraDunhill SigaraSobranie Cocktail Renkli Sigarawinston slenderWinston SigaraCavallo Apple Superslim SigaraCavallo By Gino Ferrara SigaraColts SigaraCavallo Gummint Superslim SigaraEsse SigaraHarvest sigaraparliament sigarasobranie sigara520 kalpli sigaraKent Sigaramarvel sigarasenator sigaraLucky StrikeSobranie Black Russian Sigara520 kalpli sigaraSobranie Sigaralark sigaralm sigaracamel sigaramore sigaraome sigaraviceroy sigaramuratti sigaramore sigaraal capone sigararothmans sigaraheets sigarapall mall sigaraVogue Frisson sigaraMilano SigaraMilano EjectMilano 20 Signatures Red Grape Sigara Metal KutuOscar Silver Sigaraoscar sigaraMilano Coffee SigaraMilano Strawberry SigaraMilano Apple SigaraMilano Gum Sigara Sakız AromalıMilano 20 Signatures White Sigara Metal KutuMilano Gum Mint SigaraMilano 20 Signatures Black Sigara Metal KutuMilano 20 Signatures Blue Sigara Metal KutuMilano Tech Lock Süperslim SigaraRockets sigaraoris sigaraOscar Fanpack Chocolate Flavour Sigaraeve sigaraNeo sigarabond sigaramond sigarakeno club sigaraPhilip Morris sigaraIQOS Terea RussetUnited SigaraIQOS Terea WillowIQOS Terea Mauve WaveChapman Classic Natural Aromasız SigaraChapman Vanilla Tatlı Vanilya Aromalı SigaraChapman SigaraChapman Cherry Tatlı Kiraz Aromalı SigaraChapman Coffee Tatlı Kahve Aromalı SigaraAmerican Spirit Sigaraşişli bayan escortgorukle escortzorunlu trafik sigortasısekabet girişholiganbetholiganbetBetvoleKralbetMegabahisSekabetYouwinVdcasinoNakitBahisSuperbahisPiabetSuperbetinPiabellacasinoPiabellacasinoPiabellacasino GirişPiabellacasino Twitter
目录

Unix/Linux 编程实践教程

第 3 章 目录与文件属性

fileinfo->st_mode是一个 16 位的二进制数,文件权限位:

set-user-IDSUID)位告诉内核: 运行这个文件时,认为是这个权限拥有者在运行这个程序,用户使用passwd命令可以修改/etc/passwd中自身的密码,就是这个原因。
显示特征是:user权限中的s标志。

537557 -rwsr-xr-x 1 root root 63K 3月  23 02:32 /usr/bin/passwd

set-group-ID位与set-user-ID作用类似,只是用户换成了组。group权限中的x被替换成s,即设置了set-group-ID

sticky位对于目录来说,设置了它,则在该目录里的文件只能被创建者删除。

4194305 drwxrwxrwt 25 root root 4.0K 6月   5 00:26  .

编写ls命令:

void do_ls( const char *dirname );
void show_file_info( const char *filename );
char* mode_to_letters( int mode );

// -rwxrwxr-x 1  cky  cky  1.5M     2019-06-05 00:16:35  main
int main( int argc, char *argv[] )
{
    if( argc == 1 )
        do_ls( "." );
    else
        while ( --argc ){
            do_ls( *++argv );
        }
    return EXIT_SUCCESS;
}

void do_ls( const char * dirname ) {
    DIR *dir_ptr;
    struct dirent *dir_data;
    dir_ptr = opendir( dirname );
    while ( (dir_data = readdir(dir_ptr)) != nullptr ){
        show_file_info( dir_data->d_name );
    }
    closedir(dir_ptr);
}

void show_file_info( const char *filename ){
    struct stat info;
    stat( filename, &info );

    cout << mode_to_letters( info.st_mode ); // 权限

    cout << " " << info.st_nlink << " ";     // lnode 数
    cout << " " << getgrgid(info.st_gid) -> gr_name << " ";  // 用户组名
    cout << " " << getpwuid(info.st_uid) -> pw_name << " ";  // 用户名

    // 小数显示 文件大小
    cout << " " << setiosflags( ios::fixed ) << setprecision( 1 );
    if( info.st_size >= 1024 * 1024 * 1024 ){
        cout << info.st_size / (1024.0 * 1024.0 * 1024.0)  << "G\t";
    }else if( info.st_size < 1024 * 1024 * 1024 && info.st_size >= 1024 * 1024 ) {
        cout << info.st_size / (1024.0 * 1024.0)  << "M\t";
    }else if(info.st_size < 1024 * 1024 ){
        cout << info.st_size / 1024.0 << "K\t";
    }

    char time_str[30];
    strftime( time_str, 30, "%Y-%m-%d %H:%M:%S", localtime( &info.st_mtime ) );

    cout << " " << time_str << " ";
    cout << " " << filename << endl;
}

char* mode_to_letters( int mode ){
    char *str = new char[11];
    strcpy( str, "----------" );
    str[10] = '\0';

    if( S_ISDIR(mode) ) str[0]  = 'd';
    if( S_ISCHR(mode) ) str[0]  = 'c';
    if( S_ISBLK(mode) ) str[0]  = 'b';
    if( mode & S_IRUSR ) str[1] = 'r';
    if( mode & S_IWUSR ) str[2] = 'w';
    if( mode & S_IXUSR ) str[3] = 'x';
    if( mode & S_IRGRP ) str[4] = 'r';
    if( mode & S_IWGRP ) str[5] = 'w';
    if( mode & S_IXGRP ) str[6] = 'x';
    if( mode & S_IROTH ) str[7] = 'r';
    if( mode & S_IWOTH ) str[8] = 'w';
    if( mode & S_IXOTH ) str[9] = 'x';
    return str;
}

第 4 章 文件系统:编写 pwd

创建一个新文件,文件系统该如何存储:

  1. 找到一个空i-node节点,存储属性信息

  2. 存储实际数据

  3. 存储数据的块序列号到i-node节点

  4. save i-node number and filename to dir-file

创建一个新文件

硬连接的存储秘密 : i-节点号才是文件的唯一标识,在目录表中,出现同一i-节点号对应两个以上不同名字的,这些文件名互为硬链接,也就是同一个文件的多个名字。文件是一个 i-节点 和一些数据块的结合,链接是对i-节点的引用。文件没有文件名,链接有名字,对一个文件可以 创建任意多的链接。内核记录了一个文件的链接数。

读取一个文件的过程:

  1. 在目录中寻找文件名,然后找到对应的i-node节点号

  2. 定位到i-node节点,然后找到文件实际内容存储所在的块序号

  3. 访问实际的数据块,通过系统调用read()依次读取数据块上内容,不断的将字节从磁盘复制到内核缓冲区,进而到达用户空间

读取文件

固定长度的i-node节点,如何跟踪大文件?答案是,使用i-node最后空间存储一个数据块的序号,该序号对应的数据存储区域里存的不是数据,而是文件接下来的存储序号,这个块称为间接块。

如果间接块也饱和了,那就继续使用这种方式构造下一个间接块,称为二级间接块。

利用间接块存储大文件

目录树的两种视图:

目录树的两种视图

常用 API:

int result = mkdir( char *pathname, mode_t mode );        // 创建新目录
int result = rmdir( const char *path );                   // 删除目录
int result = unlink( const char *path );                  // 删除一个文件,i-node链接数减一
int result = link( const char *origin, const char *new ); // 创建一个硬链接,i-node链接数加一
int result = rename( const char *from, const char *to );  // 重命名一个链接,或移动到新目录
int result = chdir( const char *path );                   // 改变所调用进程的当前目录

Linux的每个分区有自己的文件系统树,Linux提供一种方法将这些树整合成一棵更大的树。

其中一个文件系统被命名为根文件系统,这棵树的顶端是整个树真正的根。其他的文件系统都是附加到根文件系统的某个子目录上,这个过程称为挂载。在内部,内核将根文件系统的一个目录作为指针,指向另一个文件系统的根,这样两个文件系统就联系起来了。

树的嫁接

不同文件系统的i-node节点号会重复。所以,无法在不同文件系统使用linkrename,即不允许跨设备创建硬链接。软链接 符号链接 : 通过名字引用文件,而不是i-节点号

i-node节点号

只能向上回溯到当前文件系统的根(不能跨文件系统)的pwd命令:

ino_t get_inode( const char * );
void printpathto( ino_t );
void inum_to_name( ino_t , char *, int );

int main( int argc, char *argv[] ){
    printpathto( get_inode(".") );
    cout << endl;
}

void printpathto( ino_t this_inode ){
    ino_t my_inode;
    char its_name[BUFSIZ];
    if( get_inode("..") != this_inode ){
        chdir( ".." );
        inum_to_name( this_inode, its_name, BUFSIZ );
        my_inode = get_inode( "." );
        printpathto( my_inode );
        cout << "/" << its_name;
    }
}

ino_t get_inode( const char *fname ){
    struct stat info;
    stat( fname, &info );
    return info.st_ino;
}

void inum_to_name( ino_t inode_to_find, char *namebuf, int buflen ){
    DIR *dir_ptr;
    struct dirent *direntp;
    dir_ptr = opendir( "." );
    while ( ( direntp = readdir( dir_ptr ) ) != NULL ){
        if( direntp->d_ino == inode_to_find ){
            strncpy( namebuf, direntp->d_name, buflen );
            namebuf[buflen - 1] = '\0';
            closedir( dir_ptr );
            return ;
        }
    }
    return;
}

第 5 章 链接控制:学习 stty

设备如同文件

Linux来说,声卡、终端、鼠标、磁盘文件是同一种对象。每个设备都被当作一个文件,拥有文件名、节点号、文件所有者、权限位以及最近修改时间。通常表示设备的文件,位于/dev目录下。

从磁带中读取数据:

int fd = open( "/dev/tape", O_RDONLY );
lseek( fd, (long)4096, SEEK_SET );
n = read( fd, buf, buflen );
close( fd );

终端就像文件。传统定义的终端是“键盘和显示单元”,实际包含的设备包括了打印机、键盘、一个串行接口的显示器或是一个调制解调器。而现在telnetssh窗口也被认为是一个终端。终端最重要的功能就是接受来自用户的输入,以及输出信息给用户。

➜  ~ git:(master) ✗ tty         // 查看当前终端对应的文件
/dev/pts/2
➜  ~ git:(master) ✗ who > /dev/pts/2
work     pts/2        2019-06-06 08:19 (61.141.250.115)

磁盘文件与终端文件的区别:

磁盘文件与终端文件的区别

磁盘链接的属性

  • 缓冲默认是写入是带缓冲的,通过O_SYNC位,去关闭缓冲,使得立即写入到磁盘

  • 自动添加模式,若干进程,写入同一个文件时,文件指针自动移动到末尾,O_APPEND, 原子操作

磁盘链接的属性

int s  = fcntl( fd, F_GETFL ); // get flags
result = fcntl( fd, F_SETFL, s | O_SYNC ); // set SYNC bit set flags
int result = fcntl( int fd, int cmd );
int result = fcntl( int fd, int cmd, long arg );
int result = fcntl( int fd, int cmd, struct flock *lockp );

// open 第二个参数,用于设定 读写磁盘 属性
open( filepath, O_WRONLY | O_APPEND | O_SYNC );
O_CREAT 如果文件不存在,则创建它
O_TRUNC 如果文件存在,则长度清零
O_EXCL 防止两个进程创建 同名 文件

终端链接的属性

终端输入:

  • 进程在用户输入return后,才接收数据,说明终端输入被 缓存了

  • 用户输入在到达程序时,被终端转换过

  • 程序输出到达屏幕时,被终端转换过

终端链接的属性

查看终端链接属性:

➜  ~ stty --all
speed 38400 baud; rows 63; columns 116; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q;
stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho -extproc

终端链接设置:

  • 输入: 处理从 终端 到 程序 的字符

  • 输出: 处理从 程序 到 终端 的字符

  • 控制: 字符如何被表示--位的个数、奇偶性、停止位

  • 本地: 如何处理来自驱动程序内部的字符

如何设置:

struct termios attribs;
tcgetattr( fd, &settings );             // 获取设置
settings.c_lflag |= ECHO;               // 修改设置
tcsetattr( fd, TCSANOW, &settings );    // 写回设置

设置操作

其他设备链接编程: ioctl

比如 CD 刻录机,可擦写的 CD,不同的刻录速度等。

int result = ioctl( int fd, int operation [, arg ...] );

第 6 章 为用户编程:终端控制与信号

用户程序:

  • 立即响应键盘事件

  • 有限的输入集

  • 输入的超时

  • 屏蔽Ctrl + C

终端模式小结:

  • 规范模式:字符有缓冲,按下return键才将数据发送给程序,中间有转换,有基本的编辑功能: 删除字符、终止命令

  • 非规范模式: 有转换,但是不再缓冲数据,所以没有编辑功能

  • raw模式,所有处理都被关闭,驱动程序将输入直接传递给程序。

进程如何处理信号:

  • 默认处理

signal( SIGINT, SIG_DFL );
  • 忽略

signal( SIGINT, SIG_IGN );
  • 调用自定义函数处理

signal( signum, func_name );

设置 无缓冲、无回显、非阻塞的示例程序,也包含了信号处理部分:

#define QUESTION "Do you want another transaction"

int get_response( const char *question, int try_num = 3 );
int get_enable_char( const char *str ); // 获得指定的字符之一
void set_crmode();                      // 无缓冲,一次输入一个字符,就发送到程序
void set_noecho_mode();                 // 无回显
void set_nodelay_mode();                // 非阻塞
void ctrl_c_handler( int );             // 处理 Ctrl + C 信号
void init_terminal();                   // 初始化 终端程序
void restore_terminal();                // 恢复终端程序

// 原始属性
termios original_mode = {};
int original_flags = 0;

int main( int argc, char *argv[] )
{
    init_terminal();

    signal( SIGINT, ctrl_c_handler );
    signal( SIGQUIT, SIG_IGN );

    set_crmode();
    set_noecho_mode();
    set_nodelay_mode();

    int response = get_response( QUESTION );

    restore_terminal();

    return response;
}

void init_terminal(){
    tcgetattr( STDIN_FILENO, &original_mode );
    original_flags = fcntl( STDIN_FILENO, F_GETFL );
}

void restore_terminal(){
    tcsetattr( STDIN_FILENO, TCSANOW, &original_mode );
    fcntl( STDIN_FILENO, F_SETFL, original_flags );
}

void ctrl_c_handler( int sig_num ){
    cout << "\n" << sig_num << " signal called " << endl;
    restore_terminal();
    exit( EXIT_FAILURE );
}

int get_response( const char *question, int try_num ){
    cout << question << "?(Y/N)";
    fflush( stdout );

    char input;
    while ( true ){
        sleep( 2 );
        switch( input = tolower( get_enable_char( "YyNn" ) ) )
        {
            case 'y':
                return 0;

            case 'n':
                return 2;

            case EOF:
                if( --try_num > 0 ){
                    cout << "\n try num left " << try_num << " : ";
                    break;
                }else{
                    cout << endl;
                    return 1;
                }
        }
    }
}

int get_enable_char( const char *str ){
    int c;
    // 跳过无用的 输入字符
    while ( ( c = getchar() ) != EOF && strchr( str, c ) == nullptr )
    {
        continue;
    }
    return c;
}

void set_crmode()
{
    termios ttystate;

    tcgetattr( STDIN_FILENO, &ttystate );

    ttystate.c_lflag &= ~ICANON; // no buffering
    ttystate.c_cc[VMIN] = 1;     // get 1 char at a time

    tcsetattr( STDIN_FILENO, TCSANOW, &ttystate );
}

void set_noecho_mode(){
    termios ttystate;

    tcgetattr( STDIN_FILENO, &ttystate );

    ttystate.c_lflag &= ~ECHO; // no echo

    tcsetattr( STDIN_FILENO, TCSANOW, &ttystate );
}

// 非阻塞输入,就是 getchar() 如果没读到数据,就直接返回了,不会等待用户输入了
void set_nodelay_mode(){
    int termflags;
    termflags = fcntl( STDIN_FILENO, F_GETFL );
    termflags |= O_NDELAY;
    fcntl( STDIN_FILENO, F_SETFL, termflags );
}

第 7 章 事件驱动编程:编写一个游戏

一个基于字符终端的单人弹球游戏:

  • 空间: 游戏必须在计算机屏幕特定位置画像

  • 时间: 影像以不同速度在屏幕移动

  • 中断: 程序在移动影像的同时,用户还可以在任意时刻输入,程序如何响应中断?

  • 同时处理: 程序如何同时做多件事情?

单人弹球游戏

屏幕编程 curses 库

curses库

sudo apt-get install libncurses5-dev libncursesw5 # 安装 curses 库
initscr();   // 初始化 curses 库 与 tty
endwin();    // 关闭 curses 并重置 tty
refresh();   //
move( r,c ); // 移动光标到 (r, c)位置
addstr( s ); // 当前位置画 字符串
addch( c );  // 当前位置画 字符
clear();     // 清屏
standout();  // 反色模式
standend();  // 关闭 反色模式

真实屏幕 与 虚拟屏幕: moveaddstr 等函数只在 虚拟屏幕(一个数组) 上工作,refresh 比较 两个屏幕的差异,向真实屏幕发送,能让它跟 虚拟屏幕 一致的 控制码 和 字符。

真实屏幕 与 虚拟屏幕

时钟编程

时间

每个进程都有三个时间,ITIMER_REAL 真实世界的时间、ITIMER_VIRTUAL 进程在用户态运行的时间、ITIMER_PROF

三个时钟

计时器是内核的一种机制,通过这种机制,内核在一定时间之后向进程发送SIGALRM信号,alarm()系统调用在特定的实际秒数后,内核发送SIGALRM信号。setitimer系统调用精度更高,并且还能设置以固定的时间间隔发送信号。

void countdown( int a );        // 处理时钟信号
int set_ticker( int n_msecs );  // 设置间隔调用时钟

int main( int argc, char *argv[] )
{
    signal( SIGALRM, countdown ); // 处理时钟信号

    if( set_ticker( 500 ) == -1 ){
        perror("set_ticker");
    }else{
        while ( true ){
            pause(); // or do something else
        }
    }
    return 0;
}

void countdown( int a ){
    static int num = 10;
    printf( "%d .. ", num-- );
    fflush( stdout );
    if( num < 0 ){
        printf("DONE!\n");
        exit(0);
    }
}

int set_ticker( int n_msecs ){

    long n_sec = n_msecs / 1000;
    long n_usecs = ( n_msecs % 1000 ) * 100L;

    itimerval new_timeset;

    // time to next timer expiration
    new_timeset.it_value.tv_sec = n_sec;
    new_timeset.it_value.tv_usec = n_usecs;

    // 间隔调用时间
    new_timeset.it_interval.tv_sec = n_sec;
    new_timeset.it_interval.tv_usec = n_usecs;

    return setitimer( ITIMER_REAL, &new_timeset, NULL );
}

处理多个信号

使用signal()函数面临的问题:

  • 处理函数每次使用后,需要重新设置么?就像捕鼠器抓到老鼠后,需要被重新设置一样?

  • 如果 SIGY 信号在进程处理 SIGX 信号时到达,会发生什么?

  • 在进程处理 SIGX 信号时, 第二个、第三个 SIGX 信号又到达了,会发生什么?

  • 如果信号到来时,程序正在处理getcharread之类的输入而阻塞,会发生什么?

#define INPUTLEN 100

void inthandler( int s ); // 处理 Ctrl + C 信号
void quithandler( int s); // 处理 Ctrl + \ 信号

int main( int argc, char *argv[] )
{
    char input[INPUTLEN];
    int nchars;

    signal( SIGINT, inthandler );
    signal( SIGQUIT, quithandler );

    do{
        cout << "type a message: ";
        nchars = read( STDIN_FILENO, input, ( INPUTLEN -1 ) );
        if( nchars == -1 ){
            perror("read returned an error");
        } else {
            input[nchars] = '\0';
            printf("you typed: %s", input );
        }
    }while( strncmp( input, "quit", 4 ) != 0 );

    return 0;
}

void inthandler( int s ){
    printf("Received signal %d .. waiting\n", s );
    sleep(2);
    printf("Leaving inthandler\n");
}

void quithandler( int s){
    printf("Received signal %d .. waiting\n", s );
    sleep(2);
    printf("Leaving quithandler\n");
}

上述信号处理机制的弱点:

  • 不知道信号被发送的原因,只知道 是 某信号

  • 处理函数中不能安全地 阻塞其他 信号,比如想让程序在处理SIGINT时,忽略SIGQUIT

void inthandler( int s ){
    void ( *prev_qhandler )();
    prev_qhandler = signal( SIGQUIT, SIG_IGN ); // 忽略 SIGQUIT 信号

    // 处理 SIGINT 信号

    signal( SIGQUIT, prev_qhandler ); // 恢复 SIGQUIT 信号
}

sigaction 机制

后面出现了sigaction库(POSIX 模型)解决了上述问题。

int result = sigaction( int signum, const struct sigaction *action, struct sigaction *prevaction );
struct sigaction{
    // 二者选其一 使用
    void (*sa_handler)();       // SIG_DFL, SIG_IGN, or function
    void (*sa_sigaction)( int, siginfo_t *, void *); // sigaction handler

    sigset_t sa_mask; // 指定哪些信号要被阻塞
    int sa_flags;     // 如下图
}
int result = sigprocmask( int how, const sigset_t *sigs, sigset_t *prev ); // 修改信号 mask

sa_flags

使用例子:

#define INPUTLEN 100

void inthandler( int s ){
    printf("Received signal %d .. waiting\n", s );
    sleep(2);
    printf("Leaving inthandler\n");
}

int main( int argc, char *argv[] )
{
    struct sigaction newhandler = {};
    sigset_t blocked;
    char x[INPUTLEN];

    newhandler.sa_handler = inthandler;
    newhandler.sa_flags = SA_RESETHAND | SA_RESTART; // 第二个 Ctrl + C 将会杀死进程
    sigemptyset( &blocked );
    sigaddset( &blocked, SIGQUIT );
    newhandler.sa_mask = blocked;

    if( sigaction( SIGINT, &newhandler, NULL ) ==  -1 )
    {
        perror("sigaction");
    }else{
        while(true){
            fgets( x, INPUTLEN, stdin );
            printf("input: %s", x );
        }
    }

    return 0;
}

阻塞信号:

  • 在处理一个信号时,通过设置struct sigaction中的sa_mask成员位

  • 通过sigprocmask设置当前进程需要阻塞的信号

  • 通过sigsetops来添加和删除信号

sigemptyset( sigset_t *setp ); // 清除 setp 列表中所有信号
sigfillset( sigset_t *setp );  // 添加所有信号,到 setp 指向的列表
sigaddset( sigset_t *setp, int signum );
sigdelset( sigset_t *setp, int signum );

暂时阻塞用户信号例子:

sigset_t sigs, prevsigs;
sigemptyset( &sigs );
sigaddset( &sigs, SIGINT );
sigaddset( &sigs, SIGQUIT );

sigprocmask( SIG_BLOCK, &sigs, &prevsigs );  // 阻塞 SIGINT 与 SIGQUIT 信号

// ... do something

sigprocmask( SIG_SET, *prevsigs, NULL );     // 恢复 之前的 设置

一个利用间隔定时器 以及 信号 产生动画效果的 弹球,还能通过 用户 输入 改变 球的速度:

#define MESSAGE "O"
#define BLANK   " "

void move_msg( int n );
int set_ticker( int n_msecs );

int row;   // 当前行
int col;   // 当前列
int x_dir; // x轴方向
int y_dir; // y轴方向

int main( int argc, char *argv[] )
{
    initscr();
    crmode();
    noecho();
    clear();

    int delay  = 200;

    row = 0, col = 0, x_dir = +1, y_dir = +1;

    move( row, col );
    addstr( MESSAGE );

    signal( SIGALRM, move_msg );

    set_ticker( delay );

    while ( true )
    {
        int ndelay = 0;

        int c = getch();

        if( c == 'q' )
            break;

        switch ( c )
        {
            case ' ':
                x_dir = -x_dir;
                break;
            case 'f':
                if( delay > 2 )
                    ndelay = delay / 2;
                break;
            case 's':
                ndelay = delay * 2;
                break;
        }
        if( ndelay > 0 )
            set_ticker( delay = ndelay );
    }

    endwin();
    return 0;
}

void move_msg( int n ){
    signal( SIGALRM, move_msg ); // reset, just in case

    move( row, col );
    addstr( BLANK );

    col += x_dir;
    row += y_dir;

    move( row, col );
    addstr( MESSAGE );

    move( LINES - 1, 0 );
    refresh();

    if( x_dir == -1 && col <= 0 )
        x_dir = 1;
    else if( x_dir == 1 && col + strlen(MESSAGE) >= (unsigned int)COLS )
        x_dir = -1;

    if( y_dir == -1 && row <= 0 )
        y_dir = 1;
    else if( y_dir == 1 && row >= LINES - 1 )
        y_dir = -1;
}

int set_ticker( int n_msecs ){
    long n_sec   = n_msecs / 1000;
    long n_usecs = ( n_msecs % 1000 ) * 100;

    itimerval new_timeset = {};

    // time to next timer expiration
    new_timeset.it_value.tv_sec = n_sec;
    new_timeset.it_value.tv_usec = n_usecs;

    // 间隔调用时间
    new_timeset.it_interval.tv_sec = n_sec;
    new_timeset.it_interval.tv_usec = n_usecs;

    return setitimer( ITIMER_REAL, &new_timeset, nullptr );
}

第 8 章 进程和程序: 编写命令解释器 sh

一个程序是存储在文件中的机器指令序列,运行一个程序意味着把它装载到内存,然后让 CPU 逐条执行这些指令。

系统把内存看作由页面构成的数组,将进程分割到不同的页面:

进程在内存中

查看系统中运行的程序的两个命令:

ps auxf
ps alxf
pstree -aph

shell程序的功能:

  • 运行其他程序,shell可看作是程序启动器

  • 管理输入与输出,包括< > | 等重定向与管道的功能

  • 可编程,可以编写shell脚本,提供变量与控制语句

shell的主循环执行下面 4 步:

  • 等待用户输入程序名 a.out

  • 建立一个新进程来运行 a.out

  • shella.out文件从磁盘加载到新进程

  • shell等待程序运行结束

while( ! end_of_input ){
    get command;
    excute command;
    wait for command to finish;
}

shell时间轴

一个程序如何运行另一个程序?利用execvp系统调用:

main(){
    char *arglist[3];

    arglist[0] = "ls";
    arglist[1] = "-l";
    arglist[2] = NULL; // 最后一个元素必须是 NULL

    execvp( "ls", arglist );
}

fork 创建新进程

pid_t result = fork( void );
pid_t result = wait( int *statusptr );

进程调用fork,然后内核中的fork代码执行:

  • 分配新内存块 和 内核数据结构

  • 复制原来的进程到新的进程

  • 向运行进程集添加新的进程

  • 将控制返回给两个进程

当一个进程调用fork之后,就有两个二进制代码相同的进程。而且它们都运行到相同的地方。但是每个进程都可以开始它们自己的旅程。

fork

最简单的fork调用例子:

int fork_rv;
printf("Before: my pid is %d\n", getpid());
fork_rv = fork();
if( fork_rv == -1 ){
    perror("fork");
    exit(1);
}else if( fork_rv == 0 ){
    printf("child process, pid : %d\n", getpid());
}else{
    printf("parent process, pid : %d\n", getpid());
}
return 0;

父进程可以等待子进程结束吗?

父进程可以等待子进程结束

父进程调用wait,内核挂起父进程直到子进程结束。子进程调用exit结束,内核唤醒父进程,并传入子进程的运行数据等信息。

父子进程执行流程

父进程在wait处阻塞,然后在子进程退出后,继续运行。wait还会返回调用exit的子进程的pid,它的目的之一就是通知父进程:哪个子进程运行结束了,是如何结束的。

最简单的wait例子:

void child_code( int delay );
void parent_code( int childpid );

int main( int argc, char *argv[] )
{
    printf("Before: my pid is %d\n", getpid());
    int fork_rv = fork();
    if( fork_rv == -1 ){
        perror("fork");
        exit(1);
    }
    else if( fork_rv == 0 )
        child_code( 30 );
    else
        parent_code( fork_rv );

    return 0;
}

void child_code( int delay ){
    printf("child %d here, will sleep for %d second\n", getpid(), delay );
    sleep( delay );
    printf("child done. about to exit\n");
    exit( 0 );
}

void parent_code( int childpid ){
    int child_status = 0;
    int high_8, low_7, bit_7 = 0;

    int wait_rv = wait( &child_status );
    printf("done wating for %d. Wait returned: %d\n", childpid, wait_rv );

    high_8 = child_status >> 8;
    low_7  = child_status & 0x7F;
    bit_7  = child_status & 0x80;

    printf("status: exit = %d, sig = %d, core = %d\n", high_8, low_7, bit_7 );
}

所以,shell的实现流程如下:

shell实现流程

char *makestring( char *buf );
void excute( char *arglist[] );
void parent_code( int childpid );

#define MAXARGS 20
#define ARGLEN 100

int main( int argc, char *argv[] )
{
    char *arglist[MAXARGS + 1] = {};
    int numargs = 0;
    char argbuff[ARGLEN];
    while ( numargs < MAXARGS ){
        printf("Arg[%d] ", numargs);
        fgets(argbuff, ARGLEN, stdin);
        if(  *argbuff != '\n' ){
            arglist[numargs++] = makestring( argbuff );
        }else{
            if( numargs > 0 ){
                arglist[numargs] = nullptr;
                excute( arglist );
                numargs = 0;
            }
        }
    }
    return 0;
}

char *makestring( char *buf ){
    char *cp;
    buf[strlen(buf) - 1] = '\0';
    cp = (char *)malloc( strlen(buf) + 1 );
    if( cp == nullptr ){
        fprintf(stderr, "no memory");
        exit(1);
    }
    strcpy( cp, buf );
    return cp;
}

void excute( char *arglist[] ){
    int pid = fork();
    if( pid == -1 ){
        perror("fork failed!");
        exit(1);
    }else if( pid == 0 ){
        execvp( arglist[0], arglist );
        perror("execvp failed");
        exit(1);
    }else{
        parent_code( pid );
    }
}

void parent_code( int childpid ){
    int child_status = 0;
    int high_8, low_7, bit_7 = 0;

    int wait_rv = wait( &child_status );
    printf("done wating for %d. Wait returned: %d\n", childpid, wait_rv );

    high_8 = child_status >> 8;
    low_7  = child_status & 0x7F;
    bit_7  = child_status & 0x80;

    printf("status: exit = %d, sig = %d, core = %d\n", high_8, low_7, bit_7 );
}

上述程序的问题: 按下Ctrl + C 会将父进程 与 子进程一起杀死。原因是: 键盘信号发给所有链接的进程,父子进程都链接到终端,当Ctrl + C按下时,ttr驱动告诉内核向由这个终端控制的所有进程发送SIGINT信号。

解决方案是,让父进程忽略Ctrl + C信号:

signal( SIGINT, SIG_IGN ); // 让父进程 忽略 Ctrl + C 信号
parent_code( pid );

用进程编程

对于函数是call/return,而对于父子进程之间是execvp/exit。能否将这种通过 参数和返回值 在 拥有私有数据的 函数间 通信的模式,拓展到程序之间?

函数调用和程序调用

事实就是: Unix 程序常常设计成一组子程序,而不是一个带有很多函数的大程序。

exec传递的参数必须是字符串,所以进程间通信的参数类型必须为字符串,这种基于文本的程序接口类型天然支持跨平台。

exitfork的逆操作:

  • 刷新所有流

  • 调用由 atexiton_exit 注册的函数

  • 执行当前系统定义的其他与exit相关的操作

  • 调用_exit, 它是一个内核操作,处理所有分配给这个进程的内存,关闭所有这个进程打开的文件,释放所有内核用来管理和维护这个进程的数据结构

    • 关闭所有文件描述符 和 目录描述符

    • 将该进程的PID置为init进程的PID

    • 如果父进程调用waitwaitpid来等待子进程结束,则通知父进程

    • 向父进程发送SIGCHLD

第 9 章 shell

#!/bin/bash
BOOK=$HOME/phonebook.data
echo "find what name in phonebook";
read NAME
if grep $NAME $BOOK > /tmp/pb.tmp
then
    echo "Entries for " $NAME
    cat /tmp/pb.tmp
else
    echo "No entries for " $NAME
fi
rm /tmp/pb.tmp

上述脚本包含:

  • 变量

  • 用户输入

  • 控制

  • 环境,比如$HOME

environ环境变量的问题