国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 操作系統 > 正文

關于getpw系列函數返回的靜態區域

2024-06-28 13:21:09
字體:
來源:轉載
供稿:網友
關于getpw系列函數返回的靜態區域

  首先說一下什么是getpw系列函數,它主要是指這些函數:

  

  這些函數根據一個用戶名(getpwnam和getpwnam_r兩個函數)或者一個用戶ID(getpwuid和getpwuid_r)來獲取這個用戶在/etc/passwd中相應的條目信息,并把這些信息存放在一個struct passwd的結構體里面,然后再把這個結構體的指針返回。問題就出在這個存儲用戶信息的結構體上面,它是由getpw函數在程序中自定義的一塊靜態存儲區,而且每調用一次getpw函數,這個靜態存儲區就會被重寫一次。比如下面這段代碼:

 1 #include<errno.h> 2 #include<pwd.h> 3 #include<string.h> 4 #include<stdlib.h> 5 #include<stdarg.h> 6 #include<stdio.h> 7 #include<sys/types.h> 8  9 #define BUFSIZE 51210 11 void err_exit(char *fmt,...);12 13 int main(int argc,char *argv[])14 {15     16     struct passwd *mistr;17     struct passwd *rootstr;18 19     if(NULL == (mistr=getpwnam("michael")))20     err_exit("[getpwnam]<mi>:");21     if(NULL == (rootstr=getpwnam("root")))22     err_exit("[getpwnam]<root>:");23 24     PRintf("name = %s/n",mistr->pw_name);25     printf("name = %s/n",rootstr->pw_name);26 27     return 0;28 }

  我想要獲取michael用戶和root用戶的信息,然后我很異想天開得創建了兩個指針,一個指向michael用戶的信息,一個指向root用戶的信息,以為這樣就能分別保存兩個用戶的信息了,然后發現程序的運行結果如下圖:

  

  發現兩次打印的信息,都是root,說明后面調用的getpwnam函數重寫了struct passwd這個結構體,把前面調用getpwnam函數時寫入的michael用戶的信息給覆蓋了。

  這個就是getpwnam_r和getpwuid_r函數出現的原因,為了防止多線程程序因為競爭而產生錯誤。關于這兩個函數getpwnam的文檔中是這樣描述的:

  

  這兩個函數可以將用戶信息存放在用戶定義的struct passwd結構體中,而不一定非得要放在函數定義的,可能被重寫的靜態區域中,這樣多線程程序運行的時候就可以避免競爭了。

  接下來再來說我的一個新的發現,就是getpw函數調用的時候是有鎖的,也就是說同一個進程中,如果一個getpw函數正在運行,另一個getpw函數是無法運行的。

  這個發現是我在運行《APUE》的程序清單10-2的時候發現的,《APUE》上面的代碼如下(不是完全相同,自己重寫了一下^_^):

 1 /** 2  *  可重入函數的概念就是可以在被調用了一半的時候被打斷,然后再來從新調用,這里的getpwnam就是一個不可重入函數 3  * ,因為getpwnam函數會修改一個靜態變量來保存讀出的用戶信息,如果調用了一半再被重新調用,則有可能會讓原來的 4  * 信息被覆蓋掉 5  * 6  * 現在getpwname函數可能有鎖,信號處理函數再來調用這個函數會卡死 7  */ 8  9 #include<errno.h>10 #include<pwd.h>11 #include<signal.h>12 #include<string.h>13 #include<stdlib.h>14 #include<stdarg.h>15 #include<stdio.h>16 #include<sys/types.h>17 #include<unistd.h>18 19 #define BUFSIZE 51220 21 void err_exit(char *fmt,...);22 int err_dump(char *fmt,...);23 int err_ret(char *fmt,...);24 25 /**26  *  本函數用來處理SIGALRM信號27  */28 void sig_alrm(int signo)29 {30     struct passwd *rootstr;31 32     printf("In signal SIGALRM handler/n");33 34     if(NULL == (rootstr=getpwnam("root")))35     err_exit("[getpwnam]<root>:");36     else37     printf("pw_name = %s/n",rootstr->pw_name);38 39     alarm(1);40 }41 42 int main(int argc,char *argv[])43 {44     45     struct passwd *mistr;46     signal(SIGALRM,sig_alrm);47 48     /**49      *    alarm函數用來在1秒鐘之后發送一個SIGALRM信號給本程序50      */51     alarm(1);52 53     for(;;)54     {55     if(NULL == (mistr=getpwnam("michael")))56         err_exit("[getpwnam]<mi>:");57     if(strcmp(mistr->pw_name,"michael"))58         printf("結果錯誤,讀出的用戶名是:%s/n",mistr->pw_name);59     }60 61     return 0;62 }

  這個程序的流程是主函數調用getpwname獲取michael用戶的信息,同時通過alarm函數給自己發送SIGALRM信號,在SIGALRM的信號處理函數中,我們再來調用getpwnam函數獲取root用戶的信息,作者想要的結果是主函數不斷讀取michael用戶的信息,但是SIGALRM信號處理函數獲取了root用戶的信息,所以最后主函數最后打印就有可能是root用戶的信息,此時主函數就打印讀取出錯。

  但是在程序運行的過程中我的電腦上卻會發生死鎖的情況,運行結果如下圖所示:

  

  到此時程序就卡死了,不會再繼續下去了。

  后來我又用gdb把這個程序調試了一下,首先在32行那里增加一個斷點,然后再用si命令一步一步地執行匯編指令,執行的過程如下:

  

  si命令在調試時是會進入函數內部的,所以我在進入getpwnam函數之后又一步一步地深入,最后我發現我的函數卡在了__lll_lock_wait_private()這個地方,函數的調用棧如下:

  

  前4行表示接受到了信號之后的處理過程,最后函數卡在了__lll_lock_wait_private()這個函數這里。我google了一下這個函數,在這里找到了它的實現。這個有點慚愧,看了半天愣是沒看懂那個實現代碼是是什么意思,不過我在那里看到了那個文件是一個線程庫,所以我猜getpwnam函數的執行過程應該是這樣的:

  在getpwnam函數內部,具體還是要調用getpwnam_r函數來實現的(從上面函數棧截圖的5~8行可以看出來),在getpwnam函數內部調用getpwnam_r函數的時候,先要上一個線程鎖,調用完成之后,再來解鎖這個線程鎖,這樣來確保getpwnam函數每次只會被一個線程調用。

  而在上面的代碼中,主函數中的getpwnam函數沒有解鎖,就被暫停運行了,而信號處理函數(sig_alrm)函數中的getpwnam再來調用的時候,就會等待主函數中的getpwnam解鎖,而主函數又是暫停的,這樣就會產生死鎖了!

  OK,這就是我的分析了, 后面那個線程鎖那里我感覺還不是太了解,講的不是太好,還望見諒,如果哪位關于這部分有更深的了解,還望不吝賜教!


上一篇:curl命令詳解

下一篇:光標操作快捷鍵

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 沐川县| 盐津县| 大丰市| 泽库县| 托克逊县| 唐山市| 云南省| 临桂县| 湖南省| 宽甸| 临高县| 西乌珠穆沁旗| 集贤县| 古田县| 桂阳县| 天津市| 平顺县| 延庆县| 夹江县| 石阡县| 玉山县| 南京市| 绥棱县| 邢台县| 开远市| 乐清市| 合水县| 淄博市| 咸丰县| 拉孜县| 平谷区| 湖南省| 成武县| 电白县| 龙口市| 嘉义县| 商河县| 拜城县| 齐齐哈尔市| 大石桥市| 米泉市|