青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

T9的空間

You will never walk alone!

  C++博客 :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
  69 隨筆 :: 0 文章 :: 28 評(píng)論 :: 0 Trackbacks

#

線程控制

APUE講的destroy會(huì)free空間,這件事情看起來(lái)不太對(duì),也許是Base on實(shí)現(xiàn)
起碼我看到的實(shí)現(xiàn)是沒(méi)有做這件事情的。

 

int pthread_attr_init(pthread_attr_t * attr)
{
    
*attr = gDefaultPthreadAttr;
    
return 0;
}


int pthread_attr_destroy(pthread_attr_t * attr)
{
    memset(attr, 
0x42sizeof(pthread_attr_t));
    
return 0;
}


Attribution有下面這些。

 

typedef struct
{
    uint32_t flags;
    
void * stack_base;
    size_t stack_size;
    size_t guard_size;
    int32_t sched_policy;
    int32_t sched_priority;
}
 pthread_attr_t;

我們可以用malloc和mmap給Thread分配stack,指定stack大小和位置(低地址)

mutex屬性
兩個(gè)東西
1.進(jìn)程共享屬性,PTHREAD_PROCESS_PRIVATE or SHARED
意思是說(shuō)如果這個(gè)Mutex是分配在兩個(gè)進(jìn)程共享的memory,然后設(shè)置為shared mutex就可以用于進(jìn)程同步
2.類型屬性,normal;errorcheck;recurive
意思是定義Mutex本身的特性,這里更正下前面那章提到的錯(cuò)誤
只有normal的mutex才會(huì)在再次加鎖時(shí)發(fā)生deadlock
errorcheck的會(huì)直接返回錯(cuò)誤
recurive本身就允許,會(huì)有計(jì)數(shù)

rw lock和condition屬性
只支持第一個(gè)

TLS變量
先看下TLS的位置

 * +---------------------------+
 
* |     pthread_internal_t    |
 
* +---------------------------+
 
* |                           |
 
* |          TLS area         |
 
* |                           |
 
* +---------------------------+
 
* |                           |
 
* .                           .
 
* .         stack area        .
 
* .                           .
 
* |                           |
 
* +---------------------------+
 
* |         guard page        |
 
* +---------------------------+

pthread_internal_t是記錄線程自己本身的一些屬性的變量,guard page就是線程attr上設(shè)置的防止棧溢出的擴(kuò)展內(nèi)存

創(chuàng)建TLS變量的方法
1.創(chuàng)建Key,這個(gè)Key是這個(gè)Process中所有Thread可以共享訪問(wèn)的,然后每個(gè)Thread自己去關(guān)聯(lián)自己的Thread Local變量
int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void*))
創(chuàng)建key的時(shí)候需要保證唯一調(diào)用,也就是不會(huì)有兩個(gè)Thread同時(shí)調(diào)用,然后發(fā)生一些base on實(shí)現(xiàn)的不確定的結(jié)果,可以使用pthread_once

pthread_once的實(shí)現(xiàn)很簡(jiǎn)單,用mutex做互斥量保證pthread_once_t的訪問(wèn),然后利用pthread_once_t做flag來(lái)調(diào)用init_routine

int  pthread_once( pthread_once_t*  once_control,  void (*init_routine)(void) )
{
    
static pthread_mutex_t   once_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
    
volatile pthread_once_t* ocptr = once_control;

    pthread_once_t tmp 
= *ocptr;
    ANDROID_MEMBAR_FULL();
    
if (tmp == PTHREAD_ONCE_INIT) {
        pthread_mutex_lock( 
&once_lock );
        
if (*ocptr == PTHREAD_ONCE_INIT) {
            (
*init_routine)();
            ANDROID_MEMBAR_FULL();
            
*ocptr = ~PTHREAD_ONCE_INIT;
        }

        pthread_mutex_unlock( 
&once_lock );
    }

    
return 0;
}

所以這個(gè)pthread_once_t必須是全局or靜態(tài)變量
pthread_once最常用的case除了拿到TLS key外,init函數(shù)經(jīng)常會(huì)使用這個(gè),避免多線程同時(shí)調(diào)用。

然后就是thread cancel
Android Bionic庫(kù)沒(méi)有實(shí)現(xiàn) pthread_setcancelstate(int state, int* oldstate)以及pthread_cancel()
這里thread的取消,有些是取消不掉的,例如一些blocking call,所有的取消均是需要設(shè)定一些取消點(diǎn)的,

有人有專門針對(duì)Android沒(méi)有Thread Cancel來(lái)想辦法
基本就是thread signal,在signal handler中去call pthread_exit();
or用PIPE在多路復(fù)用中break出來(lái)
http://blog.csdn.net/langresser/article/details/8531112

如果信號(hào)與硬件故障or計(jì)時(shí)器超時(shí)相關(guān),那么信號(hào)會(huì)發(fā)送到引起事件的該線程中去,其他信號(hào)會(huì)發(fā)送到任意一個(gè)線程。
這里是說(shuō)的POSIX線程模型,一般的信號(hào)都會(huì)發(fā)送給進(jìn)程中沒(méi)有阻塞該信號(hào)的某個(gè)線程。APUE講的某個(gè)沒(méi)有阻塞該信號(hào)的線程,其實(shí)有點(diǎn)模糊。

但是Linux的實(shí)現(xiàn)不一樣,linux的thread是clone出來(lái)的,就是共享資源的獨(dú)立進(jìn)程,這樣子其實(shí)蠻自然的,也不容易復(fù)雜。
linux中,有可能線程就不會(huì)注意到該信號(hào),這樣講也有點(diǎn)模糊,總之終端驅(qū)動(dòng)程序的信號(hào)會(huì)將信號(hào)通知到進(jìn)程組,這樣子所有的thread就會(huì)都收到信號(hào)
如果你不想所有的線程都去關(guān)心信號(hào),那么可以使用一個(gè)專門處理信號(hào)的線程
先將其他線程的signal都pthread_sigmask SIG_BLOCK掉,然后使用sigwait在特定的線程來(lái)等
pthread_sigmask和sigprocmask很像

thread與fork
fork后的子進(jìn)程會(huì)繼承mutex, rw lock, condition的狀態(tài)
但是子進(jìn)程中只會(huì)存在一個(gè)線程,也就是那個(gè)調(diào)用fork的線程的副本,有個(gè)復(fù)雜的事情是關(guān)于上面繼承下來(lái)的那些東西,子進(jìn)程由于只有一個(gè)線程,沒(méi)辦法知道
上面那三個(gè)東西的具體狀態(tài),需要有個(gè)清理鎖的動(dòng)作 -->pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
我目前不知道這個(gè)東西的使用場(chǎng)景,而且不容易控制。
因?yàn)橐话闳绻覀僨ork后都會(huì)接execu,這樣地址空間就會(huì)改變,那么這些繼承的東西就沒(méi)用了。

然后有談一點(diǎn)關(guān)于線程IO的東西
pwrite/pread
也就是原子的進(jìn)行l(wèi)seek和r/w,但是這個(gè)并不能保證大家常見(jiàn)的沖突問(wèn)題,也就是一個(gè)thread在寫另外一個(gè)要讀,所以這兩個(gè)東西并不能解決同步的問(wèn)題。

作業(yè):
12.3 理論上函數(shù)開(kāi)始時(shí)屏蔽所有信號(hào),函數(shù)結(jié)束時(shí)恢復(fù),是可以做到異步信號(hào)安全的。如果你只是屏蔽一些信號(hào),那么沒(méi)辦法做到,因?yàn)槿绻衅渌盘?hào)進(jìn)來(lái),你call的函數(shù)里面有一些不可重入的函數(shù),同樣不能保證是異步安全的。

12.5 fork可以在拿來(lái)執(zhí)行可執(zhí)行程序,現(xiàn)在應(yīng)該就這一個(gè)Case。

posted @ 2013-06-04 15:41 Torres 閱讀(294) | 評(píng)論 (0)編輯 收藏

線程函數(shù)
int pthread_equal(pthread_t tid1, pthread_t tid2)
pthread_t pthread_self(void)

int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr
     void* (*start_rtn)(void), void* restrict arg)

thread被創(chuàng)建的時(shí)候會(huì)繼承調(diào)用線程的浮點(diǎn)環(huán)境和信號(hào)屏蔽字,但是該線程的未決信號(hào)集將會(huì)被清楚,
清除未決信號(hào)集這件事情應(yīng)該是沒(méi)有疑問(wèn)的,在thread創(chuàng)建之前pending住的信號(hào),不應(yīng)該deliver給下一個(gè)
Ps: 線程的浮點(diǎn)環(huán)境指的是啥? 看來(lái)以后我應(yīng)該去注意下浮點(diǎn)數(shù)的運(yùn)算原理。

pthread相關(guān)的函數(shù)會(huì)直接返回錯(cuò)誤碼,而不會(huì)和一些system call一樣,置全局errno,兩種方式都有好處,一個(gè)可以講返回值
帶回的錯(cuò)誤代碼集中起來(lái),范圍縮小;另外一個(gè)非常方便,關(guān)鍵點(diǎn)在于這一類共用errno的是否真的異常是可以共用的。

pthread_create返回之前有可能新的線程就已經(jīng)開(kāi)始run了

啟動(dòng)函數(shù) void* (*start_rtn)(void)

可以通過(guò)return給回來(lái),也可以通過(guò)pthread_exit給
這個(gè)會(huì)存在一個(gè)地方
通過(guò)pthread_join(tid, void**)取回來(lái)

這里join的和java join是一樣的功能

如果這個(gè)東西是一個(gè)很大的東西:),那么放到heap是最好的選擇,不要放到stack上了,不然線程返回這東西就沒(méi)了,join取到的內(nèi)存地址就變成一個(gè)無(wú)效的了,SIGSEGV就會(huì)被發(fā)出來(lái)

pthread_cancel,同一個(gè)進(jìn)程可以call,提出請(qǐng)求終止線程

pthread_cleanup_push
pthread_cleanup_pop

線程分離,這樣子線程終止后可以釋放一些資源,而不用一定要其他人來(lái)join
方法有兩種,新建的時(shí)候加上分離屬性
    pthread_attr_init (&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);

或者call pthread_detach(pthread_t tid)

線程互斥與同步

typedef struct
{
    
int volatile value;
}
 pthread_mutex_t;

多注意volatile變量,這個(gè)東西理論上就是讓編譯器不要做優(yōu)化,不要cache volatile類型的變量,
每次都去內(nèi)存地址中拿,而不是寄存器/高速緩存副本,這種變量極容易被編譯器不知道的人改變,例如其他線程。

靜態(tài)初始化:
#define  PTHREAD_MUTEX_INITIALIZER             {0}
#define  PTHREAD_RECURSIVE_MUTEX_INITIALIZER   {0x4000}
#define  PTHREAD_ERRORCHECK_MUTEX_INITIALIZER  {0x8000}
所謂的動(dòng)態(tài)初始化
pthread_mutex_init; pthread_mutex_destroy

然后就是一些pthread mutex的基本處理函數(shù)了
lock,unlock
trylock;

這個(gè)trylock需要好好理解下,嘗試獲取lock,如果OK,那么lock他然后 return 0; 否則也不會(huì)suspend住,而是直接返回EBUSY

pthread_mutex_destroy, 會(huì)先去try lock,然后處理掉這個(gè)mutex的值。

這里稍微提一下

int pthread_mutex_trylock(pthread_mutex_t *mutex)
{
    
int mtype, tid, oldv, shared;

    
if (__unlikely(mutex == NULL))
        
return EINVAL;

    mtype  
= (mutex->value & MUTEX_TYPE_MASK);
    shared 
= (mutex->value & MUTEX_SHARED_MASK);

    
/* Handle common case first */
    
if ( __likely(mtype == MUTEX_TYPE_NORMAL) )
    
{
        
if (__atomic_cmpxchg(shared|0, shared|1&mutex->value) == 0{
            ANDROID_MEMBAR_FULL();
            
return 0;
        }


        
return EBUSY;
    }




__likely/__unlikely函數(shù)用來(lái)告訴編譯器優(yōu)化代碼,類似if else中最有可能or最沒(méi)有可能發(fā)生的Case

mutex就有deadlock的問(wèn)題,單線程,如果有代碼重入重復(fù)獲取鎖就會(huì)deadlock,因?yàn)槟阕卟坏侥鉼nlock的地方去;另外
常見(jiàn)的deadlock就是lock比較多,又沒(méi)有設(shè)計(jì)好順序,這個(gè)應(yīng)該從業(yè)務(wù)邏輯上就應(yīng)該定義好,當(dāng)然有時(shí)候有的人用的時(shí)候or改代碼的時(shí)候
并沒(méi)有理清這些lock的關(guān)系,是否有dependency,比較難通過(guò)借用一些編譯系統(tǒng)來(lái)Cover住,然后改完就有bug。

然后還有一種需要設(shè)計(jì)好的是鎖的粒度
太粗太細(xì)都不好
粒度太粗,lock住的東西太多,很多線程都要等lock,最后這個(gè)東西會(huì)演變成一個(gè)串行的東西
粒度太細(xì),lock又變的太多,不停的需要lock/unlock,performance就會(huì)變差。
目前看到的Android上的很多l(xiāng)ock都太粗。

rw鎖 ->讀寫鎖
基本理念就是讀不會(huì)影響臨界區(qū)發(fā)生變化
所以讀模式的rw lock可以多個(gè)人占用,寫模式的rw lock時(shí)能被一個(gè)線程lock

只要有寫模式lock請(qǐng)求,那么后面的讀模式lock請(qǐng)求一般實(shí)現(xiàn)是都會(huì)被Suspend住,不然因?yàn)樽x模式下,可以重復(fù)lock,如果不
suspend,那么寫模式的lock請(qǐng)求有可能永遠(yuǎn)得不到相應(yīng)。
rw鎖一般用在 read比 write行為多的多的場(chǎng)景,允許多線程并發(fā)去讀,單一線程去寫。

然后會(huì)想到spinlock,可以去網(wǎng)上search看下基本概念,spinlock一般在SMP架構(gòu)下會(huì)比較有效果。

mutex是一種同步機(jī)制or講這是一種互斥機(jī)制 -> Java synchronize
還一種就是條件變量 condition.. -> wait/notify

這里有段話很好
條件變量給多個(gè)線程提供了一個(gè)回合的場(chǎng)所,條件變量與互斥量一起使用的時(shí)候,允許線程以無(wú)競(jìng)爭(zhēng)方式等待特定的條件發(fā)生。

作業(yè):
1.線程之間傳遞數(shù)據(jù)不要用stack變量,用放到下面這些地方的變量就好,RW/RO/ZI/Heap就沒(méi)事了
4.
現(xiàn)在一般都是這樣

    pthread_mutex_lock(&s_startupMutex);

    s_started = 1;
    pthread_cond_broadcast(&s_startupCond);

    pthread_mutex_unlock(&s_startupMutex);

會(huì)在broadcast后才unlock
否則有比較高的概率,unlock后,被其他線程將條件改掉,這個(gè)時(shí)候broadcast出去就沒(méi)有意義了。
但是這種也有可能會(huì)被另外一個(gè)線程,并非wait在那里的線程改變條件值

所以 pthread_cond_wait 返回并不意味著條件一定為真了
最好是always check條件
類似這種
    while (s_started == 0) {
        pthread_cond_wait(&s_startupCond, &s_startupMutex);
    }

posted @ 2013-06-03 17:03 Torres 閱讀(218) | 評(píng)論 (0)編輯 收藏

信號(hào)
提供異步時(shí)間處理方式
觸發(fā)時(shí)機(jī):
1.終端命令
2.硬件異常,由kernel拋向?qū)?yīng)的Process
3.kill函數(shù)/kill命令(超級(jí)用戶or Process的user相同,這里的user id一般是指實(shí)際用戶ID or 有效用戶ID,如果支持 _POSIX_SAVED_IDS,那么檢查saved-user-id)
4.軟件觸發(fā)(滿足信號(hào)條件)

這里也稍微解釋下Kill這個(gè)東西,kill函數(shù),kill命令并不是字面上殺掉某些東西,kill只是在特定的時(shí)間發(fā)送信號(hào),
具體的處理取決于信號(hào)本身和信號(hào)的處理方式。

信號(hào)的處理方式:
這個(gè)有點(diǎn)類似Java Exception的處理方式,catch住or往上throw

APUE講的信號(hào)處理方式有三種
1.忽略,SIGKILL/SIGSTOP不能忽略;一般如果Process自己不管的話,應(yīng)該會(huì)走到系統(tǒng)默認(rèn)的處理流程中,所以不能忽略這件事情是系統(tǒng)自己就會(huì)保證的。
2.捕捉信號(hào),例如SIGCHLD,一般parent會(huì)call waitpid取子進(jìn)程終止?fàn)顟B(tài),避免他處理Zombie狀態(tài)。
3.執(zhí)行系統(tǒng)默認(rèn)動(dòng)作

我感覺(jué)1和3可以歸為一類吧,只是有些signal沒(méi)有系統(tǒng)默認(rèn)動(dòng)作,然后就跳過(guò)去了。一般的系統(tǒng)默認(rèn)動(dòng)作也就是終止進(jìn)程。

SIGKILL/SIGSTOP不能被忽略的原因我覺(jué)得講的不錯(cuò),提供超級(jí)用戶終止進(jìn)程的可靠方法,不然后續(xù)有可能進(jìn)程的行為是未定義的.

signal函數(shù)定義:(

void (*signal(int signo, void(*func)(int)))(int);

這個(gè)C函數(shù)的聲明有點(diǎn)難看懂

APUE講signal的語(yǔ)義與實(shí)現(xiàn)有關(guān),所以建議大家都用sigaction

不過(guò)我看了下Android Bionic下的實(shí)現(xiàn),分別包含BSD以及SYSV的版本,但也都變成了sigaction,所以一般在C庫(kù)中就保證了這一點(diǎn),也就無(wú)所謂了。

 

static __sighandler_t
_signal(
int  signum, __sighandler_t  handler, int  flags)
{
    
struct sigaction  sa;
    __sighandler_t    result 
= SIG_ERR;

    sigemptyset( 
&sa.sa_mask );

    sa.sa_handler 
= handler;
    sa.sa_flags   
= flags;

    
if ( !sigaction( signum, &sa, &sa ) )
        result 
= (__sighandler_t) sa.sa_handler;

    
return result;
}



__sighandler_t bsd_signal(
int signum, __sighandler_t handler)
{
  
return _signal(signum, handler, SA_RESTART);
}


__sighandler_t sysv_signal(
int signum, __sighandler_t handler)
{
  
return _signal(signum, handler, SA_RESETHAND);
}



exec函數(shù)會(huì)講信號(hào)處理方式還原為系統(tǒng)默認(rèn),這個(gè)應(yīng)該毫無(wú)疑問(wèn),exec后地址空間就不一樣了,保留之前的捕捉函數(shù)也是無(wú)意義的;在這之前是有意義的,我的意思是說(shuō)fork后exec之前。

后面有列一大堆是否可以重入的函數(shù),不太想記,然后講到了malloc
我想不能重入的函數(shù)無(wú)外乎兩種類型
one: 自己有maintain一些全局or static變量 --> malloc 維護(hù)分配內(nèi)存時(shí)static heap linklist
two: 函數(shù)參數(shù)里面有引用之類的,會(huì)影響調(diào)用者的情況。

然后我看到這個(gè)立馬就想到了線程安全,malloc是線程安全的麼?
去看了下Bionic的實(shí)現(xiàn),然后就又看到下面這個(gè)名字 Dong Lea,馬上就會(huì)想到Java Concurrent庫(kù),看到這個(gè)你直接就會(huì)有想法,Bionic里面這個(gè)malloc肯定是線程安全的;
有時(shí)候被這些東西搞的很累,比較煩他,而且Bionic里面用的malloc的那個(gè)實(shí)現(xiàn)版本(aka dlmalloc)我又沒(méi)看懂:(,只好去網(wǎng)上search了一下,有個(gè)人做過(guò)實(shí)驗(yàn),有些結(jié)論還是可以參考的
http://www.360doc.com/content/12/0420/23/168576_205320609.shtml

總的來(lái)說(shuō),這個(gè)東西取決于C庫(kù)實(shí)現(xiàn),我想Bionic和Glibc都應(yīng)該一樣會(huì)支持兩種不同的版本,然后編譯的時(shí)候就可以確定是否有線程相關(guān)操作,然后在link的時(shí)候link過(guò)來(lái)不同的版本

線程安全和信號(hào)安全是兩個(gè)概念

如果在線程安全的malloc中,信號(hào)處理函數(shù)中發(fā)生重入,那么應(yīng)該是會(huì)發(fā)生dead lock
如果是非線程安全中,那么應(yīng)該是所謂的 undefined behavior.

前面還有一個(gè)概率忘記寫
早期的Unix系統(tǒng),如果系統(tǒng)在執(zhí)行一個(gè)低速系統(tǒng)調(diào)用(基本可以總結(jié)為blocking IO:包括IPC,F(xiàn)ile IO,ioctl),那么如果捕捉到信號(hào),那么系統(tǒng)就會(huì)中斷這個(gè)system call -->EINTR, 這在當(dāng)時(shí)是有理由的,而且理由看起來(lái)也合理,但是由于user有時(shí)候并不知道某些系統(tǒng)調(diào)用是否是低速系統(tǒng)調(diào)用,BSD引進(jìn)了自動(dòng)重啟的功能,linux follow這種規(guī)則

然后有幾個(gè)版本的signal函數(shù)
signal默認(rèn)自動(dòng)重啟
_signal(signum, handler, SA_RESTART) 這是由于在sigaction中的flag為SA_RESTART,上面sysV的實(shí)現(xiàn)我不想多寫了。
sigaction也就是可選的了。

但一般我印象中好多 read/write都是會(huì)自己判斷返回值以及errno 是否為 EINTR,然后retry,因?yàn)樯厦孢@種方式依賴系統(tǒng)實(shí)現(xiàn),需要將所有的signal都設(shè)定為SA_RESTART,那么如果有某個(gè)信號(hào)發(fā)生的時(shí)候,被他INTR的系統(tǒng)調(diào)用才會(huì)自動(dòng)重啟,不知道默認(rèn)signal在注冊(cè)處理行為的時(shí)候是不是如此,感覺(jué)不太好用。

alarm函數(shù)
對(duì)于很多像alarm這種函數(shù),在設(shè)計(jì)時(shí)or使用時(shí)均應(yīng)該考慮臨界值的問(wèn)題
alarm的唯一性,是by process的,process單例
所以如果alarm兩次,那么第一次會(huì)被覆蓋,怎么處理第一次未完成的情況->返回值會(huì)帶回來(lái)剩余的時(shí)間
怎么取消設(shè)定的alarm
傳入值為0 means cancel
總之了,這個(gè)東西設(shè)計(jì)的時(shí)候還蠻完善的

后面還有一堆有關(guān)signal的標(biāo)準(zhǔn)函數(shù),這里也就不一一列舉

作業(yè):
1.去掉for(;;), 那捕捉到SIGUSR1就返回了,pause()只要捕捉到信號(hào),等信號(hào)處理完時(shí)自己就會(huì)返回并帶回EINTR。不曉得為啥有這種題目...
2.實(shí)現(xiàn)sig2str,這有點(diǎn)無(wú)聊了.:(不寫
3.畫runtime stack的樣子。
4.IO操作的超時(shí)最好不要采用alarm的方式,各種原子問(wèn)題,select/poll是最好的選擇。
后面有好一些是要寫代碼的...
我略想,然后就不太愿意寫了.
PS:有些東西略難...我也是有點(diǎn)想不清楚的樣子,打算慢慢搞。

后面決定把標(biāo)題改一下,順利看完書吧...Orz.

posted @ 2013-06-02 21:52 Torres 閱讀(279) | 評(píng)論 (0)編輯 收藏

第九章 進(jìn)程關(guān)系
也是首先記錄基本概念

關(guān)于linux終端的概念,這個(gè)最好仔細(xì)想一下,因?yàn)橹髸?huì)有偽終端的內(nèi)容,如果不想的具體一些,那么會(huì)比較難有感覺(jué)。下面有個(gè)鏈接,我覺(jué)得寫的還可以。
http://blog.csdn.net/smstong/article/details/8760331

我寫下我的理解,肯定有不太對(duì)的地方,但只能到此。

廣義的講: 終端就是用來(lái)和User交互的設(shè)備,鍵盤,顯示器,打印機(jī)...
這里一般講的是狹義的終端:
一般有兩種: 字符啞終端; 圖形終端
字符啞終端,就是自己沒(méi)有處理能力,只能輸入輸出的;
圖形終端其實(shí)他有處理能力,例如framebuffer...
linux中,真正能稱得上是終端的其實(shí)物理組件就是鍵盤+顯示器,或者稱這兩個(gè)東西叫做控制臺(tái)
但是linux虛擬出來(lái)了6個(gè)終端設(shè)備
tty1 ~ tty6
而/dev/tty0是一個(gè)symbol link指向當(dāng)前終端,/dev/console則可以為/dev/tty0提供緩沖(這個(gè)我不確定)

tty7是圖形終端
圖形終端中,會(huì)用軟件類似rxvt虛擬出更多的偽終端,類似我們看到的/dev/pts/0...

偽終端是一種虛擬終端
但是有時(shí)候所謂的虛擬終端還是能對(duì)應(yīng)上物理設(shè)備

對(duì)于偽終端pts和ptm
我自己感覺(jué)就像PIPE,只不過(guò)輸入輸出時(shí)有終端行規(guī)程來(lái)控制格式。

后面有講會(huì)話,會(huì)話由進(jìn)程組構(gòu)成,不過(guò)我想不清楚運(yùn)用場(chǎng)景...

這一章有一些沒(méi)看懂,進(jìn)程組,會(huì)話,控制終端...因?yàn)椴辉趺从茫确胚@里,以后再翻。
沒(méi)有作業(yè)...
posted @ 2013-05-31 19:50 Torres 閱讀(268) | 評(píng)論 (0)編輯 收藏

記錄一些基本概念
PID == 0的是Swapper進(jìn)程(調(diào)度進(jìn)程),這個(gè)Process的RO應(yīng)該是pre load的時(shí)候都放到內(nèi)存里面了,不run任何磁盤上的code,屬于系統(tǒng)進(jìn)程。
PID == 1的是init進(jìn)程。這個(gè)是一個(gè)以root運(yùn)行的用戶進(jìn)程
fork,我有看到一個(gè)詞fork hope...我覺(jué)得不錯(cuò),我也希望我能fork出hope...
fork的返回狀態(tài)是有原因的,APUE的解釋還不錯(cuò)吧
fork 在 父進(jìn)程中返回子進(jìn)程的 PID,因?yàn)闆](méi)有任何API能知道 child process id,而且child可能會(huì)有很多。。。
fork 在 子進(jìn)程中返回0,是因?yàn)?是swapper進(jìn)程,所以沒(méi)關(guān)系;也沒(méi)必要返回父進(jìn)程ID,可以通過(guò)getppid()得到。
fork后,子進(jìn)程父進(jìn)程共享正文 RO
RW ZI Heap Stack是父進(jìn)程的副本,副本意味著copy
linux已經(jīng)有很多使用COW了(copy-on-write),這個(gè)東西應(yīng)該是要借助MMU
因?yàn)榛緁ork后會(huì)跟exec,那樣子所有的東西又都需要馬上被替換掉
都設(shè)置為Read Only,無(wú)論是parent還是child只要一個(gè)試圖修改,那么就copy,單位應(yīng)該是MMU的Page
linux有提供clone,這個(gè)東西可以由調(diào)用者控制什么東西是要和父進(jìn)程共享的,我還沒(méi)看到pthread的實(shí)現(xiàn),猜想應(yīng)該就是通過(guò)這東西實(shí)現(xiàn)資源共享的。
tips:{
對(duì)于字符串求長(zhǎng)度,sizeof會(huì)計(jì)算null,這也正常,因?yàn)閟izeof()是算memory被分配的大小;strlen計(jì)算的是有效字符的長(zhǎng)度,不包含null。
如果字符串是常量,sizeof能在編譯時(shí)被優(yōu)化,也變成常量,strlen則是函數(shù)調(diào)用不能被優(yōu)化。
}
vfork
具體實(shí)現(xiàn)我沒(méi)有具體去想,這個(gè)東西也沒(méi)有什么特別的,跟fork不一樣的是,他必須接上exec or exit,否則會(huì)run在父進(jìn)程空間而不做任何復(fù)制
所以會(huì)有同步,在子進(jìn)程還未call exec or exit前,父進(jìn)程會(huì)被suspend。要不然父進(jìn)程受的影響便是未知的。
另外這里也有討論exit函數(shù)在做清理工作時(shí)對(duì)流的處理
應(yīng)該是說(shuō) 一般exit不會(huì)去做關(guān)閉流的動(dòng)作,而把這件事情留給kernel,只是flush stream就好,否則類似遇到vfork這種會(huì)在父進(jìn)程空間做事情的,
如果call exit把流給關(guān)閉了,會(huì)有一些不預(yù)期的結(jié)果。
父進(jìn)程提前終止,那么子進(jìn)程的父進(jìn)程都會(huì)被置為1--> init process.
init process會(huì)為每個(gè)子進(jìn)程無(wú)論是自己fork出來(lái)的還是因?yàn)楦高M(jìn)程提前終止而領(lǐng)養(yǎng)的做wait獲取終止?fàn)顟B(tài),避免zombie進(jìn)程
處理方式應(yīng)該是收到SIGCHLD后call wait(null)
wait系列的函數(shù)還蠻多的...
對(duì)于不想使自己的子進(jìn)程處于zombie狀態(tài)的,自己有還想做一些事情的且不想關(guān)心子進(jìn)程結(jié)束狀態(tài)的可以fork兩次,exit第一次出來(lái)的進(jìn)程,wait他,然后第二次fork出來(lái)的
進(jìn)程的父進(jìn)程就變成init了,不過(guò)需要使用這種技巧的Case我沒(méi)想到。。。

關(guān)于exec系列函數(shù),我想最好還是記住,因?yàn)榻?jīng)常會(huì)用,不能每次用都去查吧。
一共6個(gè),exec能給的最多就這三個(gè)參數(shù),要run的程序路徑;要給run的程序的參數(shù);要給run的程序的環(huán)境變量
其中只有execve是 system call,其他都是庫(kù)函數(shù)。
execve(const char* name, char* const argv[], char* const envp[])

這里又有講三個(gè)用戶ID的用法
實(shí)際用戶ID;有效用戶ID;保存的設(shè)置用戶ID

這里有講一個(gè)Case,應(yīng)該算經(jīng)典Case,也只有類似Case才能讓你明白保存的設(shè)置用戶ID的用途

man程序?yàn)樵O(shè)置用戶ID/設(shè)置組ID,就會(huì)在兩種權(quán)限中切換。
運(yùn)行man的時(shí)候
real user id == login id
effective user id == man
saved effective user id == man

在man訪問(wèn)完他自己擁有的那些file后,想運(yùn)行一些命令call setuid(getuid())因?yàn)槲覀儾皇莚oot
real user id == login id
effective user id == login id
saved effective user id == man
這個(gè)時(shí)候運(yùn)行命令就是我們login時(shí)候的user的權(quán)限

完了后面想設(shè)置回去的時(shí)候再call setuid(euid),這里euid就是man,之前可以保存在man自己的Context中
這個(gè)時(shí)候euid == saved effective user id,所以effective user id又被設(shè)置為man,這種情況下如果沒(méi)有saved effective user id那用戶權(quán)限就回不去了。
real user id == login id
effective user id == man
saved effective user id == man

所以注意setuid 如果是root用戶,那么三個(gè)ID都會(huì)被設(shè)置成目標(biāo)ID,如果不是
則effective user id可能會(huì)改變 當(dāng)目標(biāo)ID為 real user id or saved effective user id的時(shí)候,從設(shè)計(jì)的角度仔細(xì)想,這樣子也合理。

system的實(shí)現(xiàn)
execel shell,而不是直接去fork/execel本身要system的程序,那樣會(huì)變得異常復(fù)雜,你需要解析cmdString,分離出程序和參數(shù),帶入和shell一樣的環(huán)境變量,找到目的程序,execute他,然后帶回結(jié)果。
一般會(huì)去call system的程序不要使用set-user-id,這樣容易導(dǎo)致安全泄露。容易讓system出來(lái)的那個(gè)process權(quán)限擴(kuò)大,不易控制。

貼一個(gè)system的實(shí)現(xiàn),非常nice
#include <sys/types.h>
#include 
<signal.h>
#include 
<stdlib.h>
#include 
<unistd.h>
#include 
<paths.h>
#include 
<sys/wait.h>

extern char **environ;

int
system(
const char *command)
{
  pid_t pid;
    sig_t intsave, quitsave;
    sigset_t mask, omask;
    
int pstat;
    
char *argp[] = {"sh""-c", NULL, NULL};

    
if (!command)        /* just checking */
        
return(1);

    argp[
2= (char *)command;

    sigemptyset(
&mask);
    sigaddset(
&mask, SIGCHLD);
    sigprocmask(SIG_BLOCK, 
&mask, &omask);
    
switch (pid = vfork()) {
    
case -1:            /* error */
        sigprocmask(SIG_SETMASK, 
&omask, NULL);
        
return(-1);
    
case 0:                /* child */
        sigprocmask(SIG_SETMASK, 
&omask, NULL);
        execve(_PATH_BSHELL, argp, environ);
    _exit(
127);
  }


    intsave 
= (sig_t)  bsd_signal(SIGINT, SIG_IGN);
    quitsave 
= (sig_t) bsd_signal(SIGQUIT, SIG_IGN);
    pid 
= waitpid(pid, (int *)&pstat, 0);
    sigprocmask(SIG_SETMASK, 
&omask, NULL);
    (
void)bsd_signal(SIGINT, intsave);
    (
void)bsd_signal(SIGQUIT, quitsave);
    
return (pid == -1 ? -1 : pstat);
}

這里使用了vfork和execve,他的實(shí)現(xiàn)細(xì)節(jié)以及可移植性做的還是蠻完美的。

作業(yè):
8.1 如果并非如此那么exit就沒(méi)有關(guān)閉stdout,這在之前也講過(guò)一般的C庫(kù)都不會(huì)這么多此一舉,一般他們只會(huì)去flush stream不會(huì)close
所以如果想達(dá)到那種效果,可以自己去顯示去fclose

8.2
這個(gè)有點(diǎn)復(fù)雜啦,vfork后理論上應(yīng)該立即調(diào)用exec才對(duì),如果不去exec or exit,那么子進(jìn)程就run在父進(jìn)程的數(shù)據(jù)空間,如果子進(jìn)程從另外一個(gè)函數(shù)返回,不call exit,那么就會(huì)一直等到main函數(shù)退出,父進(jìn)程才會(huì)執(zhí)行,但是這個(gè)時(shí)候stack里面已經(jīng)沒(méi)有任何東西了,我不確定會(huì)發(fā)生什么事情

8.5
不提供返回保存的有效用戶ID的函數(shù)

8.6 8.7稍后貼上source code.


posted @ 2013-05-31 00:25 Torres 閱讀(216) | 評(píng)論 (0)編輯 收藏

第六章不看,直接到第七章

所謂的啟動(dòng)例程用C寫起來(lái)一般是這樣,但一般情況這個(gè)會(huì)是匯編來(lái)寫
exit(main(argc, argv))

exit函數(shù)
_exit和_Exit立即進(jìn)入kernel,exit則需要做完必要的清理工作,先run注冊(cè)過(guò)的終止處理程序,然后清理沒(méi)有關(guān)閉的流。
exit _Exit是ISO C標(biāo)準(zhǔn),_exit則是Posix,所以頭文件不一樣
前面是stdlib.h 后面是unistd.h

關(guān)于malloc,calloc,realloc
malloc分配的空間初始值不確定;calloc會(huì)幫忙清零;realloc可以動(dòng)態(tài)分配合適的大小空間,如果不夠會(huì)有copy原始數(shù)據(jù)的動(dòng)作,所以對(duì)realloc之前的memory記錄指針不是好方法,因?yàn)閞ealloc完就可能失效。

這三個(gè)都是去call sbrk來(lái)擴(kuò)大縮小heap
但一般只是擴(kuò)大,在free的時(shí)候 malloc 會(huì)把擴(kuò)大的 memory cache起來(lái)(virual memory)

setjmp, longjmp
跳轉(zhuǎn)函數(shù),支持跨越函數(shù)幀的跳轉(zhuǎn),goto是函數(shù)內(nèi)跳轉(zhuǎn)
注意: longjmp到setjmp的點(diǎn)后,一般情況 全局/局部/局部靜態(tài)變量都沒(méi)辦法恢復(fù)到setjmp時(shí)的值;register/volatile變量在編譯后(cc -O)會(huì)放到寄存器中,這些都能實(shí)現(xiàn)回滾。這也就說(shuō)明setjmp的時(shí)候,jmp_buf并沒(méi)有保存這些值。

7.1 如果不調(diào)用return 和 exit 函數(shù)返回值用了printf的返回值,Orz...我沒(méi)想清楚怎么做的。

7.2 取決于printf面對(duì)的設(shè)備類型,行緩沖的話,碰到換行就會(huì)被輸出,如果是全緩沖,那么則要么需要call fflush,要么等stream被close。

7.3 不用參數(shù)傳遞,不用全局變量,Orz...要傳遞argc, argv -->NO

7.4 通常null就是0,一般指針為 null 的時(shí)候意味著空指針引用,一般0都不會(huì)去放數(shù)據(jù),RO的起始位置在一個(gè)更高的地址,0會(huì)是無(wú)效的,用來(lái)指引程序出錯(cuò)。

7.5
typedef void func(void);
int atexit(func* fp);
或者
typedef void (*func)(void);
int atexit(func fp);
我喜歡下面那種,因?yàn)楹瘮?shù)本身就是個(gè)pointer。

7.6 calloc會(huì)將分配的空間清零,ISO C不保證0值和空指針相等

7.7 size輸出的是靜態(tài)的RO,RW,ZI,像Heap,Stack是runtime才有的。

7.8 理論上不帶debug信息的.out文件應(yīng)該大小都是等于RO+RW+ZI的。

7.9 這就是.a和.so的區(qū)別了,link的時(shí)候靜態(tài)庫(kù)會(huì)被直接copy過(guò)來(lái),所以會(huì)變大很多。

7.10 變量作用域的故事。
posted @ 2013-05-28 20:00 Torres 閱讀(204) | 評(píng)論 (0)編輯 收藏

     摘要: 這一章會(huì)讓你想起一些塵封的記憶上大學(xué)的時(shí)候,教C語(yǔ)言的老師會(huì)教大家文件IO,那個(gè)時(shí)候講的都是標(biāo)準(zhǔn)輸入輸出,都是C庫(kù)的實(shí)現(xiàn),和第三章Unbuffered IO要區(qū)別開(kāi)來(lái),目的之前講過(guò)減少System Call的調(diào)用次數(shù),提高PerformanceJava上也有類似的實(shí)現(xiàn),只不過(guò)Java的實(shí)現(xiàn)會(huì)更加Common一些,類似BufferedInputStream/BufferedOutputStream,...  閱讀全文
posted @ 2013-05-28 19:24 Torres 閱讀(312) | 評(píng)論 (0)編輯 收藏

這里要稍微寫一些linux下復(fù)雜的權(quán)限管理,應(yīng)該只是一小部分知識(shí),還有一些關(guān)于cap的東西以后再看。

與process關(guān)聯(lián)的ID
誰(shuí)execute這個(gè)process的人稱為real user id,應(yīng)該就是登陸時(shí)使用的user
real group id也一樣.
一般在一個(gè)登陸Session中,這兩個(gè)值都不會(huì)改變,但是超級(jí)用戶進(jìn)程可以改變
像Android中每個(gè)APK有獨(dú)自的User id,然后類似rild之類的native deamon process就會(huì)嘗試改變自己的user id為類似radio之類的東西。

在運(yùn)行中檢查權(quán)限所使用的id稱為有效用戶id,effective user id
然后還有有效組ID
附加組ID

一般有效用戶ID == 真實(shí)用戶ID, 有效組ID == 真實(shí)組ID
但是如果在可執(zhí)行文件的st_mode中有設(shè)置 set-user-ID/set-group-ID
那在執(zhí)行這個(gè)文件的時(shí)候 有效用戶ID和有效組ID會(huì)變成文件的owner

一般有設(shè)置set-user-ID/set-group-ID的程序都會(huì)獲得額外的權(quán)限

關(guān)于文件權(quán)限,有一些容易引起誤解的,也還有一些我自己也沒(méi)有理解清楚的

文件權(quán)限比較好理解,O_TRUNC需要文件具有寫權(quán)限。

文件夾的寫權(quán)限,應(yīng)該就是類似能不能在其中create/update/delete文件和文件夾
文件夾的讀權(quán)限,應(yīng)該就是讀文件夾里面的文件/文件夾列表,通常我們 ls 當(dāng)前文件夾就必須具有讀權(quán)限
文件夾的執(zhí)行權(quán)限,這個(gè)東西聽(tīng)說(shuō)又叫搜索位,通常我們cd XXX,就必須在當(dāng)前文件夾下搜索XXX是否存在,然后就是當(dāng)我們?cè)L問(wèn)類似這種很長(zhǎng)路徑的文件/aaa/bbb/ccc/dd.txt
對(duì)aaa,bbb,ccc必須具有執(zhí)行權(quán)限,就是搜索

一般如果具有讀權(quán)限就應(yīng)該就可以搜索,如果這之前有區(qū)別,就是應(yīng)該是搜索的范圍大于能讀到的內(nèi)容。
也就是之后需要知道一個(gè)Folder本身里面有寫什么內(nèi)容,我目前知道的是一定有當(dāng)前的文件列表--> TODO: 看情景分析 or 去看下 ls 的實(shí)現(xiàn),ls里面有比較多參數(shù)有些是需要x的有些是需要r的,例如 ls -l如果沒(méi)有執(zhí)行權(quán)限就只能拿到 name ,能知道是folder還是file,除此之外其他的東西都拿不到,文件本身的權(quán)限,user,group

-->linux目錄中記錄的應(yīng)該是只有兩個(gè)東西 inode & name.這與文件系統(tǒng)的實(shí)現(xiàn)有關(guān)。

另外增刪文件都需要WX權(quán)限

另外內(nèi)核對(duì)文件操作權(quán)限的判斷順序...如果是owner就會(huì)看owner權(quán)限,group權(quán)限就不會(huì)看了,如果在group中則others也就不看了,這個(gè)邏輯上正常,而且可以想一下kernel的實(shí)現(xiàn),一定是if-else的判斷出結(jié)果后直接返回。


這里就要提如果新建文件/文件夾的時(shí)候文件夾的owner user id和group id是誰(shuí)
user id會(huì)是process 有效用戶ID
group id可以有兩種選擇父folder的group id;or process有效組ID
linux中根據(jù)文件系統(tǒng)的不同有的可以在mount fs的時(shí)候選擇

文件長(zhǎng)度: st_size
lseek到文件末尾之后的空間,會(huì)照成文件空洞
文件長(zhǎng)度會(huì)+空洞的大小,但是這些空洞未必會(huì)占用磁盤空間。
du 命令可以看磁盤空間大小

符號(hào)鏈接和所謂的硬鏈接完全是兩回事,建議細(xì)讀4.14,不要去上網(wǎng)search類似我這種自己記錄給自己看的blog...:(

這章內(nèi)容比較多而且雜,主要是stat中的每個(gè)參數(shù)的意義,需要思考想清楚的東西也比較多,有比較多關(guān)于文件操作的System Call

作業(yè)開(kāi)始:
之后只寫有點(diǎn)意義的題目,無(wú)意義的也沒(méi)什么可以寫的pass
4.1 stat和lstat的區(qū)別,stat基本不會(huì)關(guān)心是否是S_IFLNK(符號(hào)鏈接),應(yīng)該是看到S_IFLNK會(huì)往下去找真正的File,然后拿到屬性值
而lstat旁道S_IFLNK則會(huì)直接返回。第一次看到這樣的函數(shù)設(shè)計(jì)怪怪的,我的初始感覺(jué)是反的。我覺(jué)得stat是一個(gè)common的設(shè)計(jì),不會(huì)針對(duì)不同的File類型來(lái)做一些區(qū)別。Orz...最后不是。

4.2 umask 777意味著rwxrwxrwx全部變沒(méi)掉,但是這也沒(méi)關(guān)系,不知道出題人的意思

4.5 目錄和符號(hào)鏈接的長(zhǎng)度不可能為0,目錄創(chuàng)建出來(lái)就會(huì)包含. 和 ..而且 . 的inode指向自己本身會(huì)占磁盤空間,符號(hào)鏈接肯定也是不能為0的,其中有存指向的鏈接path

4.6 這個(gè)有點(diǎn)意思,后面貼代碼

4.7 這個(gè)看了下答案,沒(méi)看懂>為什么kernel默認(rèn)的創(chuàng)建文件賦予的權(quán)限“可能會(huì),也可能不會(huì)”受unmask的值的影響?

4.8 du df的區(qū)別...

4.9 ~ 后面的題大概看了下,不想寫了,好多細(xì)節(jié)...

 


 

posted @ 2013-05-27 16:42 Torres 閱讀(265) | 評(píng)論 (0)編輯 收藏

看了下第一次作業(yè),那個(gè)排版和高亮背景有點(diǎn)瞎,這次能好點(diǎn)就不錯(cuò)了.

第二章是關(guān)于POSIX以及XSI的一些標(biāo)準(zhǔn)和limitation,就不做作業(yè)了,直接到第三章,沒(méi)有睡午覺(jué),有些困,不要寫錯(cuò)...

先貼一些比較好的思路
(1)
在AF_LOCAL(Posix: AF_UNIX)的Socket編程中,對(duì)Client端來(lái)說(shuō)需要connect
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen)
這里需要給serv_addr一個(gè)length,這個(gè)length是一個(gè)真實(shí)的地址長(zhǎng)度。
當(dāng)socket為AF_UNIX的Socket時(shí) socket address為sockaddr_un里面存放著地址類型以及socket path,我們需要知道sockaddr_un中真正存放數(shù)據(jù)的長(zhǎng)度。

函數(shù)offsetof是不錯(cuò)的選擇
這個(gè)function的實(shí)現(xiàn)看起來(lái)可以學(xué)習(xí),我先抄一段貼下面

#define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER )

宏功能:獲得一個(gè)結(jié)構(gòu)體變量成員在此結(jié)構(gòu)體中的偏移量。

1. ( (TYPE *)0 ) 將零轉(zhuǎn)型為TYPE類型指針;
2. ((TYPE *)0)->MEMBER 訪問(wèn)結(jié)構(gòu)中的數(shù)據(jù)成員;

3. &( ( (TYPE *)0 )->MEMBER )取出數(shù)據(jù)成員的地址,即相對(duì)于0的偏移量,要的就這個(gè);
4.(size_t)(&(((TYPE*)0)->MEMBER))結(jié)果轉(zhuǎn)換類型,size_t應(yīng)該最終為unsigned int類型。
此宏的巧妙之處在于將 0 轉(zhuǎn)換成(TYPE*),這樣結(jié)構(gòu)體中成員的地址即為在此結(jié)構(gòu)體中的偏移量。

這里主要還是利用了 '->' 這個(gè)符號(hào)的功能,編譯器解析這個(gè)符號(hào)的時(shí)候應(yīng)該就是通過(guò)偏移量取值,所以這里先定義好取值規(guī)則(TYPE*)然后利用偏移量取值反向再取地址,再然后因?yàn)槠鹗嫉刂肥?,取回的地址就變成了真正的偏移量。
還有一個(gè)知道m(xù)ember求整體的Ptr的Marco應(yīng)該也蠻好用,括號(hào)比較多,我沒(méi)頭去看具體實(shí)現(xiàn)了...

 

#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr – offsetof(type,member) );})

宏功能:從結(jié)構(gòu)體(type)某成員變量(member)指針(ptr)來(lái)求出該結(jié)構(gòu)體(type)的首指針。

(2)
有討論一些東西,沒(méi)有搞清楚,貼在這里以后有心力了再看,在看Rild,然后pthread在做線程同步的時(shí)候需要先lock,也許大家都是這樣做,也一直這樣做,但是突然就想到之前Java的時(shí)候討論過(guò)的東西為什么wait/notify 一定要在一個(gè)synchronize block中,當(dāng)然你可以講,如果不會(huì)有exception拋出,你也還可以講wait本來(lái)就會(huì)先釋放lock然后把當(dāng)前的thread放到Object waiting thread pool中然后掛起當(dāng)前線程,實(shí)現(xiàn)就是這樣子,但是從設(shè)計(jì)來(lái)講,原因我沒(méi)有想的特別清楚,如果不去lock,是不是因?yàn)閣ait/notify本身有很多操作沒(méi)辦法做multi thread...

然后就是關(guān)于select的一些輔助的Marco,類似FD_SET,F(xiàn)D_ZERO之類的,簡(jiǎn)單講就是一個(gè)long數(shù)組,每個(gè)元素的每個(gè)bit代表一個(gè)fd,然后做set,clear之類的動(dòng)作,最大不能超過(guò)那個(gè)1024

今天先到這里,寫個(gè)序,明天正文...Orz

(3)
繼續(xù)討論東西
有人為了在C++中作出類似Java可以自動(dòng)釋放內(nèi)存的東西,也就是所謂的智能指針,Android有幫忙實(shí)現(xiàn)strong pointer/weak pointer,他的大概意思就是用一個(gè)第三者來(lái)看住當(dāng)前new出來(lái)的object,采用的方式一般是引用計(jì)數(shù),這種方式也許在Design Pattern中有專門的名字。一般利用編譯器對(duì)這個(gè)第三者做自動(dòng)釋放的時(shí)候去檢查計(jì)數(shù)器,看是否能釋放他看管的object,C++里面編譯器會(huì)自動(dòng)去call ~Constructor就例如局部變量。

有一些復(fù)雜的Case,就算是交叉引用了,Strong Pointer/Weak Pointer就是為了解決交叉引用的問(wèn)題,從語(yǔ)義上講父Object引用子Object,用SP,反過(guò)來(lái)用WP
只要SP為0,就可以釋放Memory

今天大家有講Java的WeakReference,SoftReference,我就含糊了,我以為這東西和上面一樣。
Java的WeakReference,SoftReference跟上面講的完全不一樣,由于沒(méi)分清楚,混淆了。JVM就會(huì)處理引用交叉的問(wèn)題,JVM處理引用交叉可以使用類似有向圖,如果呈環(huán)狀而且環(huán)中的任意點(diǎn)都沒(méi)辦法到GC Roots(VM stack中的Reference/static reference/JNI reference),那么就都GC掉應(yīng)該也沒(méi)問(wèn)題。
所以Java中的所謂的WeakReference/SoftReference跟SP/WP不一樣,Java中普通賦值的reference都是強(qiáng)可及的Strong Reference。
WeakReference只是用來(lái)關(guān)心下那些將要被回收卻沒(méi)有回收的Object,一旦被GC Thread掃到就會(huì)被釋放。
而SoftReference是JVM對(duì)Object的Cache,只要內(nèi)存足夠不會(huì)被釋放。

貼一個(gè)講Android SP/WP的鏈接,之前有印象看過(guò)。
http://blog.csdn.net/luoshengyang/article/details/6786239


(4)
Keep Thinking...
某人讓我寫一個(gè)處理字符串的函數(shù),30min, 我怎么也不能讓他失望的,是吧~無(wú)論11點(diǎn)多的時(shí)候我頭有多暈,我很開(kāi)心的在那里寫,很直白的講,我自己用的本是個(gè)游戲機(jī),沒(méi)啥好環(huán)境。用C寫了個(gè),然后跑出來(lái)的結(jié)果出乎預(yù)料,我想可能是長(zhǎng)時(shí)間指手畫腳慣了~仔細(xì)想了下一些文件操作

然后仔細(xì)看了一下二進(jìn)制文件和文本文件,這也是糾正之前的一些疑惑。
基本概念是:
我自己感覺(jué)文本文件和二進(jìn)制文件沒(méi)啥區(qū)別,文本文件就是有明確普通解碼方式的顯示字符的文件,可以理解說(shuō)文本文件也是一種二進(jìn)制文件,只不過(guò)這個(gè)二進(jìn)制文件的解碼方式是固定好的(ASCII/UTF-8之類)

然后就是讀寫文本文件時(shí)在Windows上有一些轉(zhuǎn)換,寫的時(shí)候會(huì)在\n前面自動(dòng)加上\r,讀的時(shí)候反過(guò)來(lái),Linux則沒(méi)有區(qū)別。

有一件事情是要明確知道的
像printf之類的東西,幫助我們輸出的,其實(shí)我們往printf里面塞的是 int 值,這是一個(gè)二進(jìn)制的值,但是我們從stdout中看到的則已經(jīng)變成了字符,一定有人幫忙做了轉(zhuǎn)換.
我原本以為printf會(huì)是檢查目標(biāo)文件,如果目標(biāo)文件為二進(jìn)制則直接輸出原本的值,如果目標(biāo)為文本文件就輸出對(duì)應(yīng)值的字符

這種理解是錯(cuò)的,不同的輸入輸出函數(shù)帶來(lái)了不同的結(jié)果
類似printf這種格式話輸出函數(shù),會(huì)把輸出內(nèi)容按照字符以默認(rèn)的編碼方式encode放入文件,所以那個(gè)文件里面寫的值已經(jīng)是真實(shí)值的一種顯示方式(字符)了
然后scanf這種函數(shù)在讀的之后,也會(huì)把字符轉(zhuǎn)換為對(duì)應(yīng)的值,一般ASCII就直接 -0x30了

像read/write這種function他們不會(huì)做任何轉(zhuǎn)換,直接就把值放進(jìn)去了,所以你用文本文件打開(kāi)的時(shí)候,按照默認(rèn)的decode方式來(lái)解析他的時(shí)候會(huì)發(fā)現(xiàn)是亂碼。

這個(gè)東西與文本文件二進(jìn)制文件沒(méi)有關(guān)系。
不知道是不是我自己很弱,現(xiàn)在才知道這個(gè)東西。

作業(yè)終于開(kāi)始:
3.1 這章主要講文件Unbuffered IO,這里的unbuffered是指userspace沒(méi)有buffer來(lái)緩沖,kernel一定還是有緩沖的,一般userspace的緩沖做在標(biāo)準(zhǔn)C lib中
另外像Android這種架構(gòu)Java layer會(huì)做緩沖(BufferedInputStream/BufferedOutputStream, 默認(rèn)是8K)
Java layer的緩沖可以減少 JNI 調(diào)用次數(shù)
C lib緩沖可以減少 system call 的次數(shù)

3.2 自己實(shí)現(xiàn)dup2
不能用fcntl那就只能用dup了
這里是想要讓做題的人知道,kernel對(duì)進(jìn)程文件描述符的分配原則是 "取最小的沒(méi)有使用的"
所以用dup實(shí)現(xiàn)Mydup(fd1, fd2)
1.fd2 < fd1: close fd2; dup(fd1)得到的值就是fd2
2.fd2 = fd1: 直接return fd1
3.fd2 > fd1: 那就只能無(wú)限dup了,直到返回值為fd2,然后close掉 fd1 ~ fd2-1
這里需要對(duì)文件描述符表,文件表,inode之類的概念熟悉。

3.3
這里主要是要說(shuō)文件表項(xiàng)的分配,每次open kernel都會(huì)分配新的文件表項(xiàng),dup的作用就是拿到另外一個(gè)文件描述符指向相同的文件表項(xiàng),但是每支文件只有同一份
inode。
fd1,fd2會(huì)指向同一份文件表項(xiàng)
fd1,fd2,fd3的文件表項(xiàng)會(huì)指向同一份inode
fcntl 作用于fd1,F(xiàn)_SETFD用來(lái)設(shè)定 close_on_exec 標(biāo)志這個(gè)東西是在文件描述符中的,所以只會(huì)影響fd1
如果是F_SETFL則會(huì)影響fd1, fd2文件狀態(tài)標(biāo)志都放在文件表中。

3.4
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
if (fd > 2) close(fd);
這個(gè)東西的作用應(yīng)該就是重定向標(biāo)準(zhǔn)輸入輸出和錯(cuò)誤輸出到fd.

3.5
沒(méi)什么重要的,shell命令一定是從左往右解析執(zhí)行的

3.6
雖然是append方式打開(kāi),這個(gè)東西是針對(duì)寫,lseek可以自己選定讀取位置。
write函數(shù)是否是會(huì)每次先lseek到文件尾部?然后再寫,這樣之前l(fā)seek的設(shè)置就無(wú)效了
小東西驗(yàn)證。

后面貼下代碼
append會(huì)影響每次write,只要是O_APPEND打開(kāi),每次write都會(huì)添加到文件末尾。

Done

posted @ 2013-05-22 22:58 Torres 閱讀(165) | 評(píng)論 (0)編輯 收藏

記性不好,所以作業(yè)做起來(lái)--從第一章開(kāi)始,期望有始有終
1.1 對(duì)根目錄(/)來(lái)說(shuō),沒(méi)有parent,所以'.' or '..'就沒(méi)有區(qū)別了,可以用ls -li來(lái)看inode,/.. 代表他本身
1.2 多實(shí)例,Process ID遞增,分配Process ID的具體方式-->//TODO:去查情景分析
1.3 const char* 放到perror中不想讓perror自己改,其實(shí)我覺(jué)得strerror也可以const int
1.4 errno related
比較早之前errno是by process的,這并不合適,Linux支持多線程存取errno
extern int* __errno_location(void)
#define errno (* __errno_location())
Macro為一個(gè)函數(shù),使得__errno_location有機(jī)會(huì)返回一個(gè)TSL的variable
這里APUE有Wrap一些簡(jiǎn)單的打error log的函數(shù),由于errno是一個(gè)by thread variable,所以必須先保存
要不然后面print之類的system call同樣有可能出現(xiàn)error然后覆蓋掉errno
有看到變參函數(shù),糾正了我自己長(zhǎng)期的一個(gè)誤區(qū),之前一直以為變參函數(shù)只能用在特定的Case下才有用,例如scanf/printf之類的輸入輸出函數(shù)
因?yàn)樗麄兌加袔ь愃苀ormat的參數(shù),能夠通過(guò)%d,%c之類的東西找到后面的變參的個(gè)數(shù)以及類型,今天有去看一下va_list/va_arg/va_end的實(shí)現(xiàn),會(huì)發(fā)現(xiàn)變參函數(shù)是,本來(lái)也應(yīng)該是一種更加common的設(shè)計(jì)
這里順帶提一下function call的大體流程,這里需要比較多的背景知識(shí),類似函數(shù)調(diào)用約定,程序內(nèi)存分布...
找來(lái)一張圖比較好看Linux process userspace的布局,印象中windows也一樣只不過(guò)windows默認(rèn)是2G/2G,linux是大家都知道的1G/3G


userspace從0~0xC0000000,kernel space(0xC0000000~0xffffffff)的樣子跟這個(gè)差別比較大,那邊分vmalloc/kmalloc還有類似high memory的概念,這里主要貼出userspace,可以看一下RO,RW,ZI,Heap的位置,順便提一下整個(gè)linux virtual memory其實(shí)都可以分為兩部分: anonymous memory/mapped memory,像RO就是mapped memory,RW開(kāi)始是mapped memory,只要有人改變初始值,那就會(huì)變成anonymous memory,ZI,Heap,Stack這些都是anonymous memory,與函數(shù)調(diào)用相關(guān)的主要是stack -->stack的地址是向低地址增長(zhǎng)的,棧底在高地址,棧頂在低地址
然后介紹兩種常用的函數(shù)調(diào)用約定,一種是C語(yǔ)言默認(rèn)的函數(shù)調(diào)用約定 __cdecl; 另外一種是PASCAL默認(rèn)的調(diào)用約定 __stdcall
這兩種都是從右到左將參數(shù)壓棧,然后再push ebp; mov ebp, esp; 再然后壓入函數(shù)局部變量,不一樣的是cdecl是由caller function將函數(shù)參數(shù)出棧,stdcall是由callee function將函數(shù)出棧。
這兩種方式各有好處,慨括講一下cdecl的話每個(gè)調(diào)用者都必須去做pop stack的動(dòng)作,code size會(huì)變大;stdcall則在callee function本身來(lái)看會(huì)比較單純
但是這種類型比較難從編譯器的角度來(lái)支持變參函數(shù)。
變參函數(shù)都是cdecl
可以玩一下這樣的函數(shù)...

 

 1#include <cstdlib>
 2#include <iostream>
 3
 4using namespace std;
 5
 6void foo(int cnt, )
 7{
 8    cout << "start foo" << endl; 
 9    int* p = &cnt;
10    for(int i = 0; i < cnt; i++){        
11        cout << *(++p) << endl;
12    }

13    cout << "end foo" << endl;
14}

15
16int main(int argc, char *argv[])
17{
18    int a = 1, b = 3, c = 5, d = 7;
19    void (*functest)(int cnt, );
20    functest = foo;
21    functest(4, a, b, c, d);
22    system("PAUSE");
23    return EXIT_SUCCESS;
24}


加一點(diǎn)對(duì)va_list/va_arg/va_end的說(shuō)法,其實(shí)他的實(shí)現(xiàn)蠻靈活的

///stdarg.h
#define va_start _crt_va_start
#define va_arg _crt_va_arg
#define va_end _crt_va_end  

///vadefs.h
#define _ADDRESSOF(v)   ( &reinterpret_cast<const char &>(v) )
typedef 
char *  va_list;
#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap)      ( ap = (va_list)0 )

貼一段網(wǎng)上看到的應(yīng)該是i386的實(shí)現(xiàn),類似這種變參函數(shù)會(huì)直接去用指針操作stack來(lái)找參數(shù)的,因?yàn)樯婕暗綄?duì)其的問(wèn)題一定是by platform的,不太好移植
像_INTSIZEOF(n) 就是int對(duì)其
大概講下意思 va_list 就是 char*,是一個(gè)用來(lái)找參數(shù)ptr的游標(biāo)
va_start(ap, v)  通過(guò)第一個(gè)固定參數(shù)v 初始化 ap
va_arg(ap, t)  這里就給Type t了,所以并不一定需要類似printf的 format 來(lái)找,caller和callee可以有一些其他的約定方式,這個(gè)返回當(dāng)前類型為t的參數(shù)的值,并且將ap指向下個(gè)參數(shù),因?yàn)橹癮p被初始化的時(shí)候其實(shí)他并不知道他指向的參數(shù)的類型。
va_end(ap) 清掉ap
多說(shuō)一句,printf系列的函數(shù)還蠻多的,fprintf/sprintf/vprintf/svnprintf...帶f的是面向FILE streaming的,s是針對(duì)char buffer的,v則是說(shuō)參數(shù)是帶va_list的,n則是具體指size
1.5/1.6討論32位表示時(shí)間溢出的,不想寫答案,時(shí)間比較晚,第一章作業(yè)寫完。

 

記錄一些關(guān)于系統(tǒng)總線與CPU“位數(shù)"的基本概念

通常所說(shuō)的CPU的位數(shù),32位or64位CPU,指的是ALU(算術(shù)邏輯單元)的寬度,也就是這個(gè)ALU處理數(shù)據(jù)的基本單元的寬度
所以數(shù)據(jù)總線基本會(huì)和ALU寬度相同(有例外,這個(gè)我沒(méi)想清楚工作原理) -->應(yīng)該是可以新加一些Module來(lái)做轉(zhuǎn)換。
而地址總線則是CPU尋址的能力,一個(gè)是怎么去尋址,一個(gè)是尋到地址后,地址中內(nèi)容的寬度(當(dāng)然這個(gè)寬度跟地址類型(byte,short,int)有關(guān),但送給CPU的時(shí)候一般是單位次數(shù)送數(shù)據(jù)總線的寬度的數(shù)據(jù)),地址總線決定CPU能訪問(wèn)的Memory的范圍。

8086是16位ALU 20位數(shù)據(jù)總線尋址1M
每次CPU送出的地址都是16位,然后加上段寄存器作為最高4位

 

posted @ 2013-05-21 23:25 Torres 閱讀(219) | 評(píng)論 (0)編輯 收藏

僅列出標(biāo)題
共7頁(yè): 1 2 3 4 5 6 7 
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            一区二区三区欧美在线观看| 亚洲大黄网站| 亚洲国产精品成人综合色在线婷婷 | 午夜在线一区| 精品91视频| 麻豆精品精华液| 一区二区三区高清不卡| 老司机久久99久久精品播放免费 | 91久久精品网| 欧美日韩天天操| 午夜视黄欧洲亚洲| 久久精品国产精品| 日韩视频三区| 亚洲综合精品| 亚洲国产精品福利| 亚洲视频中文字幕| 一区二区三区自拍| 99热免费精品| 韩国久久久久| 日韩视频在线观看国产| 国产日韩欧美视频| 亚洲七七久久综合桃花剧情介绍| 国产精品国产自产拍高清av王其 | 91久久精品国产91久久| 亚洲精品亚洲人成人网| 国产精品网站在线| 亚洲国产日韩欧美| 国产亚洲欧美另类中文| 亚洲成色精品| 国产日韩亚洲欧美综合| 亚洲欧洲日产国产综合网| 国产亚洲精品久久久久动| 亚洲国产成人精品视频| 国产日韩精品一区二区| 亚洲精品影视| 亚洲国产精品悠悠久久琪琪| 在线视频亚洲| 亚洲激情在线观看| 午夜精品免费| 亚洲在线视频| 欧美精品激情在线| 麻豆精品精品国产自在97香蕉| 欧美日韩高清免费| 欧美va天堂va视频va在线| 国产精品主播| 亚洲小说欧美另类社区| 一本一本久久a久久精品综合妖精 一本一本久久a久久精品综合麻豆 | 亚洲精品久久久久久久久久久久 | 久久精品99| 亚洲女人天堂成人av在线| 女人色偷偷aa久久天堂| 久久久久九九九九| 国产欧美日韩在线视频| 亚洲一级二级在线| 亚洲欧美日韩精品久久久久| 欧美高清视频www夜色资源网| 老巨人导航500精品| 国产小视频国产精品| 亚洲一区中文| 亚洲欧美美女| 国产精品国产三级国产专区53 | 久久久噜噜噜久久狠狠50岁| 久久疯狂做爰流白浆xx| 国产精品欧美精品| 亚洲私人影吧| 欧美一区二区免费| 国产亚洲精品久久久久动| 欧美一区二区三区四区在线 | 久久九九精品99国产精品| 国产欧美日韩激情| 久久精品国产精品亚洲综合| 久久夜色精品国产噜噜av| 亚洲高清不卡av| 欧美国产免费| 中文网丁香综合网| 久久精品五月婷婷| 亚洲国产成人精品久久| 久久成人精品电影| 欧美成人精品高清在线播放| 亚洲国产色一区| 欧美日韩国产一区二区| 亚洲视频综合在线| 久久性天堂网| 亚洲人成人一区二区三区| 欧美成人高清视频| 国产精品99久久久久久人| 久久久99精品免费观看不卡| 亚洲黄色尤物视频| 欧美日韩午夜剧场| 久久成人羞羞网站| 亚洲国产精品久久久久秋霞影院| 日韩视频不卡| 国产日韩成人精品| 欧美成人精品1314www| 一本色道久久88综合亚洲精品ⅰ| 美日韩精品免费观看视频| 亚洲精品一区二区三区樱花| 欧美一区二区视频观看视频| 亚洲成色777777在线观看影院| 亚洲精品女人| 亚洲综合精品自拍| 在线欧美一区| 国产精品一二三视频| 久久躁狠狠躁夜夜爽| 亚洲素人一区二区| 亚洲国产精品黑人久久久| 欧美一区二区视频观看视频| 亚洲国产综合在线看不卡| 国产伦精品一区二区| 欧美二区在线| 欧美中文字幕在线视频| 亚洲美女在线一区| 欧美大片在线观看| 久久精品免费电影| 亚洲免费一级电影| 亚洲精品美女在线观看播放| 国产自产女人91一区在线观看| 欧美日韩亚洲视频| 美日韩精品视频免费看| 欧美在线免费视频| 亚洲在线免费| 一区二区三区欧美亚洲| 亚洲激情精品| 欧美二区在线看| 久久久久久999| 欧美一激情一区二区三区| 9l国产精品久久久久麻豆| 亚洲国产欧美精品| 在线观看国产日韩| 樱花yy私人影院亚洲| 国产在线观看精品一区二区三区 | 久久久久久久波多野高潮日日| 一本到12不卡视频在线dvd| 欧美黑人一区二区三区| 久久色在线观看| 久久激情中文| 久久成人这里只有精品| 欧美一区二区视频免费观看| 亚洲欧美综合| 亚洲在线观看视频| 亚洲男女自偷自拍| 午夜免费在线观看精品视频| 99精品视频免费| 亚洲美女区一区| 99精品热视频只有精品10| 99国产精品久久久久久久成人热| 亚洲啪啪91| 亚洲精品久久嫩草网站秘色| 亚洲精品久久久久久久久| 亚洲精品综合精品自拍| 日韩午夜电影| 日韩网站在线观看| 亚洲天堂黄色| 欧美一区二区三区久久精品茉莉花 | 国产香蕉97碰碰久久人人| 国产亚洲一二三区| 精品999日本| 亚洲人成毛片在线播放| 一片黄亚洲嫩模| 亚洲男人第一av网站| 久久九九精品99国产精品| 另类春色校园亚洲| 亚洲精品国产精品国自产观看| 亚洲人妖在线| 亚洲欧美日韩专区| 久热精品视频| 欧美色欧美亚洲另类七区| 国产美女扒开尿口久久久| 黑人中文字幕一区二区三区| 亚洲第一偷拍| 亚洲已满18点击进入久久| 久久er精品视频| 亚洲高清一二三区| 亚洲欧美国产77777| 亚洲电影av在线| 一本久久综合亚洲鲁鲁| 久久av一区| 欧美日韩欧美一区二区| 国产欧美精品一区二区色综合| 好吊一区二区三区| 亚洲无亚洲人成网站77777| 久久精品欧洲| 亚洲精品国产拍免费91在线| 亚洲欧美一区二区激情| 欧美mv日韩mv国产网站app| 国产精品青草久久| 亚洲美女视频网| 久久久在线视频| 在线视频一区观看| 欧美国产日韩xxxxx| 国产一区二区按摩在线观看| 日韩亚洲欧美高清| 免费观看一级特黄欧美大片| 一区二区三区av| 欧美丰满高潮xxxx喷水动漫| 国产在线拍偷自揄拍精品| 亚洲欧美清纯在线制服| 亚洲人永久免费| 免费欧美视频| 尤物yw午夜国产精品视频|