導航:首頁 > 操作系統 > linuxsizeof

linuxsizeof

發布時間:2023-07-11 22:58:32

linux 之mutex 源碼分析

 mutex相關的函數並不是linux kernel實現的,而是glibc實現的,源碼位於nptl目錄下。

http://ftp.gnu.org/pub/gnu/glibc/glibc-2.3.5.tar.gz

首先說數據結構:

typedef union

{

  struct

  {

    int __lock;

    unsigned int __count;

    int __owner;

    unsigned int __nusers;

    /* KIND must stay at this position in the structure to maintain

       binary compatibility.  */

    int __kind;

    int __spins;

  } __data;

  char __size[__SIZEOF_PTHREAD_MUTEX_T];

  long int __align;

} pthread_mutex_t;

 int __lock;  資源競爭引用計數

 int __kind; 鎖類型,init 函數中mutexattr 參數傳遞,該參數可以為NULL,一般為 PTHREAD_MUTEX_NORMAL

結構體其他元素暫時不了解,以後更新。

/*nptl/pthread_mutex_init.c*/

int

__pthread_mutex_init (mutex, mutexattr)

     pthread_mutex_t *mutex;

     const pthread_mutexattr_t *mutexattr;

{

  const struct pthread_mutexattr *imutexattr;

  assert (sizeof (pthread_mutex_t) <= __SIZEOF_PTHREAD_MUTEX_T);

  imutexattr = (const struct pthread_mutexattr *) mutexattr ?: &default_attr;

  /* Clear the whole variable.  */

  memset (mutex, '\0', __SIZEOF_PTHREAD_MUTEX_T);

  /* Copy the values from the attribute.  */

  mutex->__data.__kind = imutexattr->mutexkind & ~0x80000000;

  /* Default values: mutex not used yet.  */

  // mutex->__count = 0;        already done by memset

  // mutex->__owner = 0;        already done by memset

  // mutex->__nusers = 0;        already done by memset

  // mutex->__spins = 0;        already done by memset

  return 0;

}

init函數就比較簡單了,將mutex結構體清零,設置結構體中__kind屬性。

/*nptl/pthread_mutex_lock.c*/

int

__pthread_mutex_lock (mutex)

     pthread_mutex_t *mutex;

{

  assert (sizeof (mutex->__size) >= sizeof (mutex->__data));

  pid_t id = THREAD_GETMEM (THREAD_SELF, tid);

  switch (__builtin_expect (mutex->__data.__kind, PTHREAD_MUTEX_TIMED_NP))

    {

     …

    default:

      /* Correct code cannot set any other type.  */

    case PTHREAD_MUTEX_TIMED_NP:

    simple:

      /* Normal mutex.  */

      LLL_MUTEX_LOCK (mutex->__data.__lock);

      break;

  …

  }

  /* Record the ownership.  */

  assert (mutex->__data.__owner == 0);

  mutex->__data.__owner = id;

#ifndef NO_INCR

  ++mutex->__data.__nusers;

#endif

  return 0;

}

該函數主要是調用LLL_MUTEX_LOCK, 省略部分為根據mutex結構體__kind屬性不同值做些處理。

宏定義函數LLL_MUTEX_LOCK最終調用,將結構體mutex的__lock屬性作為參數傳遞進來

#define __lll_mutex_lock(futex)                                                \

  ((void) ({                                                                \

    int *__futex = (futex);                                                \

    if (atomic_compare_and_exchange_bool_acq (__futex, 1, 0) != 0)        \

      __lll_lock_wait (__futex);                                        \

  }))

atomic_compare_and_exchange_bool_acq (__futex, 1, 0)宏定義為:

#define atomic_compare_and_exchange_bool_acq(mem, newval, oldval) \

  ({ __typeof (mem) __gmemp = (mem);                                      \

     __typeof (*mem) __gnewval = (newval);                              \

      \

     *__gmemp == (oldval) ? (*__gmemp = __gnewval, 0) : 1; })

這個宏實現的功能是:

如果mem的值等於oldval,則把newval賦值給mem,放回0,否則不做任何處理,返回1.

由此可以看出,當mutex鎖限制的資源沒有競爭時,__lock 屬性被置為1,並返回0,不會調用__lll_lock_wait (__futex); 當存在競爭時,再次調用lock函數,該宏不做任何處理,返回1,調用__lll_lock_wait (__futex);

void

__lll_lock_wait (int *futex)

{

  do

    {

      int oldval = atomic_compare_and_exchange_val_acq (futex, 2, 1);

      if (oldval != 0)

lll_futex_wait (futex, 2);

    }

  while (atomic_compare_and_exchange_bool_acq (futex, 2, 0) != 0);

}

atomic_compare_and_exchange_val_acq (futex, 2, 1); 宏定義:

/* The only basic operation needed is compare and exchange.  */

#define atomic_compare_and_exchange_val_acq(mem, newval, oldval) \

  ({ __typeof (mem) __gmemp = (mem);                                      \

     __typeof (*mem) __gret = *__gmemp;                                      \

     __typeof (*mem) __gnewval = (newval);                              \

      \

     if (__gret == (oldval))                                              \

       *__gmemp = __gnewval;                                              \

     __gret; })

這個宏實現的功能是,當mem等於oldval時,將mem置為newval,始終返回mem原始值。

此時,futex等於1,futex將被置為2,並且返回1. 進而調用

lll_futex_wait (futex, 2);

#define lll_futex_timed_wait(ftx, val, timespec)                        \

({                                                                        \

   DO_INLINE_SYSCALL(futex, 4, (long) (ftx), FUTEX_WAIT, (int) (val),        \

     (long) (timespec));                                \

   _r10 == -1 ? -_retval : _retval;                                        \

})

該宏對於不同的平台架構會用不同的實現,採用匯編語言實現系統調用。不過確定的是調用了Linux kernel的futex系統調用。

futex在linux kernel的實現位於:kernel/futex.c

SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,

struct timespec __user *, utime, u32 __user *, uaddr2,

u32, val3)

{

struct timespec ts;

ktime_t t, *tp = NULL;

u32 val2 = 0;

int cmd = op & FUTEX_CMD_MASK;

if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI ||

      cmd == FUTEX_WAIT_BITSET ||

      cmd == FUTEX_WAIT_REQUEUE_PI)) {

if (_from_user(&ts, utime, sizeof(ts)) != 0)

return -EFAULT;

if (!timespec_valid(&ts))

return -EINVAL;

t = timespec_to_ktime(ts);

if (cmd == FUTEX_WAIT)

t = ktime_add_safe(ktime_get(), t);

tp = &t;

}

/*

 * requeue parameter in 'utime' if cmd == FUTEX_*_REQUEUE_*.

 * number of waiters to wake in 'utime' if cmd == FUTEX_WAKE_OP.

 */

if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE ||

    cmd == FUTEX_CMP_REQUEUE_PI || cmd == FUTEX_WAKE_OP)

val2 = (u32) (unsigned long) utime;

return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);

}

futex具有六個形參,pthread_mutex_lock最終只關注了前四個。futex函數對參數進行判斷和轉化之後,直接調用do_futex。

long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,

u32 __user *uaddr2, u32 val2, u32 val3)

{

int clockrt, ret = -ENOSYS;

int cmd = op & FUTEX_CMD_MASK;

int fshared = 0;

if (!(op & FUTEX_PRIVATE_FLAG))

fshared = 1;

clockrt = op & FUTEX_CLOCK_REALTIME;

if (clockrt && cmd != FUTEX_WAIT_BITSET && cmd != FUTEX_WAIT_REQUEUE_PI)

return -ENOSYS;

switch (cmd) {

case FUTEX_WAIT:

val3 = FUTEX_BITSET_MATCH_ANY;

case FUTEX_WAIT_BITSET:

ret = futex_wait(uaddr, fshared, val, timeout, val3, clockrt);

break;

         …

default:

ret = -ENOSYS;

}

return ret;

}

省略部分為對其他cmd的處理,pthread_mutex_lock函數最終傳入的cmd參數為FUTEX_WAIT,所以在此只關注此分之,分析futex_wait函數的實現。

static int futex_wait(u32 __user *uaddr, int fshared,

      u32 val, ktime_t *abs_time, u32 bitset, int clockrt)

{

struct hrtimer_sleeper timeout, *to = NULL;

struct restart_block *restart;

struct futex_hash_bucket *hb;

struct futex_q q;

int ret;

           … … //delete parameters check and convertion

retry:

/* Prepare to wait on uaddr. */

ret = futex_wait_setup(uaddr, val, fshared, &q, &hb);

if (ret)

goto out;

/* queue_me and wait for wakeup, timeout, or a signal. */

futex_wait_queue_me(hb, &q, to);

… … //other handlers

return ret;

}

futex_wait_setup 將線程放進休眠隊列中,

futex_wait_queue_me(hb, &q, to);將本線程休眠,等待喚醒。

喚醒後,__lll_lock_wait函數中的while (atomic_compare_and_exchange_bool_acq (futex, 2, 0) != 0); 語句將被執行,由於此時futex在pthread_mutex_unlock中置為0,所以atomic_compare_and_exchange_bool_acq (futex, 2, 0)語句將futex置為2,返回0. 退出循環,訪問用戶控制項的臨界資源。

/*nptl/pthread_mutex_unlock.c*/

int

internal_function attribute_hidden

__pthread_mutex_unlock_usercnt (mutex, decr)

     pthread_mutex_t *mutex;

     int decr;

{

  switch (__builtin_expect (mutex->__data.__kind, PTHREAD_MUTEX_TIMED_NP))

    {

   … …

    default:

      /* Correct code cannot set any other type.  */

    case PTHREAD_MUTEX_TIMED_NP:

    case PTHREAD_MUTEX_ADAPTIVE_NP:

      /* Normal mutex.  Nothing special to do.  */

      break;

    }

  /* Always reset the owner field.  */

  mutex->__data.__owner = 0;

  if (decr)

    /* One less user.  */

    --mutex->__data.__nusers;

  /* Unlock.  */

  lll_mutex_unlock (mutex->__data.__lock);

  return 0;

}

省略部分是針對不同的__kind屬性值做的一些處理,最終調用 lll_mutex_unlock。

該宏函數最終的定義為:

#define __lll_mutex_unlock(futex)                        \

  ((void) ({                                                \

    int *__futex = (futex);                                \

    int __val = atomic_exchange_rel (__futex, 0);        \

\

    if (__builtin_expect (__val > 1, 0))                \

      lll_futex_wake (__futex, 1);                        \

  }))

atomic_exchange_rel (__futex, 0);宏為:

#define atomic_exchange_rel(mem, value) \

  (__sync_synchronize (), __sync_lock_test_and_set (mem, value))

實現功能為:將mem設置為value,返回原始mem值。

__builtin_expect (__val > 1, 0) 是編譯器優化語句,告訴編譯器期望值,也就是大多數情況下__val > 1 ?是0,其邏輯判斷依然為if(__val > 1)為真的話執行 lll_futex_wake。

現在分析,在資源沒有被競爭的情況下,__futex 為1,那麼返回值__val則為1,那麼 lll_futex_wake (__futex, 1);        不會被執行,不產生系統調用。 當資源產生競爭的情況時,根據對pthread_mutex_lock 函數的分析,__futex為2, __val則為2,執行 lll_futex_wake (__futex, 1); 從而喚醒等在臨界資源的線程。

lll_futex_wake (__futex, 1); 最終會調動同一個系統調用,即futex, 只是傳遞的cmd參數為FUTEX_WAKE。

在linux kernel的futex實現中,調用

static int futex_wake(u32 __user *uaddr, int fshared, int nr_wake, u32 bitset)

{

struct futex_hash_bucket *hb;

struct futex_q *this, *next;

struct plist_head *head;

union futex_key key = FUTEX_KEY_INIT;

int ret;

if (!bitset)

return -EINVAL;

ret = get_futex_key(uaddr, fshared, &key);

if (unlikely(ret != 0))

goto out;

hb = hash_futex(&key);

spin_lock(&hb->lock);

head = &hb->chain;

plist_for_each_entry_safe(this, next, head, list) {

if (match_futex (&this->key, &key)) {

if (this->pi_state || this->rt_waiter) {

ret = -EINVAL;

break;

}

/* Check if one of the bits is set in both bitsets */

if (!(this->bitset & bitset))

continue;

wake_futex(this);

if (++ret >= nr_wake)

break;

}

}

spin_unlock(&hb->lock);

put_futex_key(fshared, &key);

out:

return ret;

}

該函數遍歷在該mutex上休眠的所有線程,調用wake_futex進行喚醒,

static void wake_futex(struct futex_q *q)

{

struct task_struct *p = q->task;

/*

 * We set q->lock_ptr = NULL _before_ we wake up the task. If

 * a non futex wake up happens on another CPU then the task

 * might exit and p would dereference a non existing task

 * struct. Prevent this by holding a reference on p across the

 * wake up.

 */

get_task_struct(p);

plist_del(&q->list, &q->list.plist);

/*

 * The waiting task can free the futex_q as soon as

 * q->lock_ptr = NULL is written, without taking any locks. A

 * memory barrier is required here to prevent the following

 * store to lock_ptr from getting ahead of the plist_del.

 */

smp_wmb();

q->lock_ptr = NULL;

wake_up_state(p, TASK_NORMAL);

put_task_struct(p);

}

wake_up_state(p, TASK_NORMAL);  的實現位於kernel/sched.c中,屬於linux進程調度的技術。

❷ Linux管道實驗題

<pre t="code" l="cpp">#include <unistd.h>
#include <stdio.h>

//警告: 該程序未做錯誤驗證, 未關閉管道(由系統自動關閉)

int main()
{
int p2c[2]; // 該管道父進程寫,子進程讀
int c2p[2]; // 該管道子進程寫,父進程讀

// 創建2條管道
pipe(p2c);
pipe(c2p);

int pid = fork();
int fd_read, fd_write; // 這兩個描述符用於保存某進程讀端和寫端
int pid_my; // 保存某進程自身的pid
int pid_other; // 另一進程的pid,通過

if ( pid == 0 ) { // 子進程
fd_read = p2c[0];
fd_write= c2p[1];
// 通過getpid取得自身pid,寫到管道里
pid_my = getpid();

write(fd_write, pid_my, sizeof(int));
// 從另一管道讀取另一進程的pid
read(fd_read, pid_other, sizeof(int));
// 列印讀取到的pid
printf("Recive pid : %d\n", pid_other);
} else { // p
fd_read = c2p[0];
fd_write= p2c[1];
pid_my = getpid();
// 由於子進程是先寫自身pid,父進程最好先讀取子進程的pid
read(fd_read, pid_other, sizeof(int));
write(fd_write, pid_my, sizeof(int));

printf("Recive pid : %d\n", pid_other);
}

return 0;
}

❸ linux環境下,c語言怎麼讀取WEB伺服器的80埠上頁面的內容

已知url ,host, port;

int s, size;
struct sockaddr_in sin;
struct hostent* phe;
char cmd[256];
char msg_hdr[1000];
char* p;

//准備http中GET 方法的請求。
sprintf(cmd,"GET %s\r\nHTTP/1.1\r\nHost:%s", url, host);
//創建socket
if((s=socket(PF_INET,SOCK_STREAM,0))<0)
return -1;
//取得遠程主機的IP地址,失敗函數返回-1
if((phe = gethostbyname(host)) == NULL)
return -1;

memset(&sin,0,sizeof(sin));
memcpy(&sin.sin_addr,phe->h_addr,sizeof(struct in_addr));
sin.sin_family=AF_INET;
sin.sin_port=htons(pms->port);

//跟遠程機器建立連接,失敗函數返回-1
if(connect(s,(struct sockaddr*)&sin,sizeof(sin))==-1)
return -1;
//發送GET請求
if(write(s,cmd,strlen(cmd))<0)
return 0;

//從鏈接描述符(連接管道)中讀取傳送過來的數據
if(read(s, msg_hdr, 300)<0)
error;
close(s);

//讀到該文件的大小
if((p=strstr(msg_hdr,"Content-Length"))||(p=strstr(msg_hdr,"Content-length:")))
p+=16;
else
error;
//返回大小
size = atoi(p);

sprintf(cmd,"GET %s HTTP/1.1\r\nHost: %s\r\nAccept: */*\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nConnection: close\r\nRange: bytes0-%d\r\n\r\n", url, host, size);
//創建套介面
if((s=socket(AF_INET,SOCK_STREAM,0))<0)
return 0;
//取得遠程主機的IP地址,失敗返回0
if((phe = gethostbyname(host)) == NULL)
return 0;
memset(&sin,0,sizeof(sin));
memcpy(&sin.sin_addr,phe->h_addr,sizeof(struct in_addr));
sin.sin_family=AF_INET;
sin.sin_port=htons(port);
//建立連接
if(connect(s,(struct sockaddr*)&sin,sizeof(sin))==-1)
return 0;
//發送讀取請求
if(write(s,cmd,strlen(cmd))<0)
error;
read(s, buf, BUFSIZE)..............

閱讀全文

與linuxsizeof相關的資料

熱點內容
單片機中三位數碼管原件 瀏覽:140
pdf可以刪除其中一頁 瀏覽:216
清dns緩存的命令 瀏覽:103
免費pdf在線轉換 瀏覽:768
堆貨演算法 瀏覽:878
vsc編譯vc程序 瀏覽:197
centos55命令 瀏覽:709
美國干編程有什麼條件 瀏覽:505
阿里雲伺服器遠程鏈接 瀏覽:249
墨鏡慧眼怎麼下載廠商的app 瀏覽:61
iphone加密專線 瀏覽:491
aes產生加密文件 瀏覽:415
編程實現藍牙通信 瀏覽:769
怎麼恢復掉簽的app 瀏覽:848
伺服器部署ip地址 瀏覽:323
涉密場所周邊安全防護距離演算法 瀏覽:674
安卓fpse模擬器怎麼設置加速 瀏覽:948
建行app怎麼生成電子簽章 瀏覽:510
獲取當前時間javadate 瀏覽:75
帶密碼的wifi如何加密 瀏覽:239