linux中C語言 創建臨時文件tmpnam函數 tmpfile函數的分析

臨時文件
通常,程序需要以文件的形式使用臨時存儲。這也許是存儲計算的中間結果,或者是在實際操作之前所做的文件拷貝備份。例如,一個數據程序在刪除記錄時會使用臨時文件。文件會收集需要保存的數據庫實體,然後在操作結束時,臨時文件會成爲新的數據庫而原始的會被刪除。
臨時文件的大量使用隱藏了他的一個缺點。我們必須小心來確保程序會選擇一個唯一的名字來使用臨時文件。如果不是這樣,因爲Linux是一個多任務系統,也許會有另一個程序選擇了相同的名字,而這兩個彼此之間會相互影響。
一個唯一的臨時文件名可以由tmpnam函數生成:
#include char *tmpnam(char *s);
tmpnam
函數會返回一個與現存的文件不同的可用的文件名。如果字符串不爲null,文件名也就會被寫入其中。後續的tmpnam函數調用會覆蓋返回值所用的表態存
儲區,所以如果tmpnam函數被調用多次,實際上是使用一個字符串參數。這個字符串假定至少爲L_tmpnam字符長。一個程序中,tmpnam至多可
以被調用TMP_MAX次,而且每次都會生成一個不同的文件名。
如果臨時文件被立即使用,那麼我們可以同時使用tmpfile函數來對其命名並且打開。這一點很重要,因爲另一個程序會使用tmpnam返回的相同的名字創建一個文件。tmpfile函數避免這種情況的發生:
#include FILE *tmpfile(void);
tmpfile函數會返回一個指向唯一的臨時文件的流指針。這個文件會爲讀和寫打開,而且在所有到文件的引用被關閉以後,這個文件會被自動刪除。
如果發生錯誤,tmpfile會返回一個空指針,並且設置errno變量。
試驗--tmpnam與tmpfile
下面讓我們實際的看一下這兩個函數的使用:
#include
int main()
{
    char tmpname[L_tmpnam];
    char *filename;
    FILE *tmpfp;
    filename = tmpnam(tmpname);
    printf(“Temporary file name is: %s\n”, filename);
    tmpfp = tmpfile();
    if(tmpfp)
        printf(“Opened a temporary file OK\n”);
    else
        perror(“tmpfile”);
    exit(0);
}
當我們編譯運行程序tmpnam.c時,我們可以看到由tmpnam生成的唯一的文件名:
$ ./tmpnam
Temporary file name is: /tmp/file2S64zc
Opened a temporary file OK
工作原理

個程序調用tmpnam生成一個唯一文件名的臨時文件。如果我們要使用這個臨時文件,我們可以立即打開,從而來減小另一個程序會使用同一個文件名打開這個
文件的風險。tmpfile調用會同時創建並打開一個臨時文件,從而避免了這種風險。實際上,當編譯一個使用這個函數的程序時,GNU
C編譯器也會給出一個使用tmpnam的警告。
老版本Unix系統還有另一個使用mktemp與mkstemp函數來生成臨時文件名的方法。這些與被Linux系統支持,並且與tmpnam相似,所不同的是我們可以爲臨時文件名指定一個模板,這樣我們就可以更好的控制其位置與名字:
#include
char *mktemp(char *template);
int mkstemp(char *template);
mktemp函數由指定的模板生成一個唯一的文件名。template必須以6個x開始。mktemp函數使用唯一可用的文件名字符來替換這些x字符。他返回一個指向所生成的字符串的指針,如果不可以生成一個唯一的文件名,則會返回一個null指針。
mkstemp函數在創建與打開臨時文件方面與tmpfile相類似。文件名由與mktemp相同的方式生成的,但是返回的結果是一個打開的,底層文件描述符。
通常,我們應使用創建與打開函數tmpfile與mkstemp,而不是tmpnam與mktemp。
用戶信息

有了Linux程序,除了init之外,都是由其他程序或用戶啓動的。我們將會在第11章中瞭解更多的如何運行程序,或進程,交互等內容。用戶通常由一個
負責命令的shell來啓動程序。我們已經看到,程序可以通過檢測其環境變量以及讀取系統時鐘來確定其環境信息。一個程序也可以查看正使用他的用戶信息。
當一個用戶登陸進Linux系統時,他就有一個用戶名與密碼。如果這些通過驗證,系統就會用戶提供一個shell。通常,用戶具有一個稱之爲UID的唯一的用戶標識。Linux運行的所有程序都是運行在用戶的行爲以及與其相關的UID上的。

們可以設置一個程序的運行,使其看起來就像是另一個用戶啓動的。當一個程序具有其UID的權限集合時,他的運行看起來就像是可執行文件的擁有者啓動的。當
執行su命令時,程序的運行看起來就像是超級用戶啓動的。然後他會驗證用戶的訪問權限,將其UID改變爲目標用戶的UID,然後執行目標用戶的登陸
shell。這會允許一個程序的運行看起來就像是另一個不同的用戶啓動的,而這通常爲系統管理員用來執行維護任務。
因爲UID是用戶標識的關鍵,我們就從這裏開始討論。
UID有其自己的類型--uid_t--在sys/types.h中進行了定義。他通常是一個小整數。一些是由系統預先定義的;另一個些是當有新用戶要添加到系統時,由系統管理員創建的。通常,用戶的UID值大於100。
#include #include uid_t getuid(void);char *getlogin(void);
getuid函數會返回與程序相關的UID值。這通常是啓動程序的用戶的UID值。
getlogin函數會返回與當前用戶相關聯的登陸名。
系統文件/etc/passwd包含一個處理用戶帳戶的數據庫。每個用戶一行,其中包含用戶名,加密密碼,用戶標識UID,組標識GID,全名,主目錄,以及默認的shell。下面是其中的一個例子:
neil:zBqxfqedfpk:500:100:Neil Matthew:/home/neil:/bin/bash

果我們要編寫一個程序來確定啓動程序的用戶UID,我們會對其進行擴展來查看密碼文件以查找用戶的登陸名與全名。我們並不推薦這樣做,因爲現在的類
Unix系統都由使用簡單的密碼文件遷移到加強的系統安全。許多系統,包括Linux,有一個選項可以使用影子密碼(shadwo
password)文件,其中根本就不包含任何有用的加密密碼信息(這通常存放在用戶不可讀取的/etc/shadow中)。正是因爲這個原因,系統定義
了一系列函數來爲用戶信息提供標準而高效的程序接口:
#include
#include
struct passwd *getpwuid(uid_t uid);
struct passwd *getpwnam(const char *name);
密碼數據庫結構,passwd,定義在pwd.h中,包含下列成員:
成員        描述
char *pw_name    用戶登陸名
uid_t pw_uid    UID值
gid_t pw_gid    GID值
char *pw_dir    用戶主目錄
char *pw_gecos    用戶全名
char *pw_shell    用戶默認shell
一些Unix系統會使用一個不同的名字來表示用戶全名域:在一些系統上例如Linux爲pw_gecos,而在另一個系統上爲pw_comment。這就意味着我們並不推薦使用這個域。
getpwuid與getpwnam函數都會返回一個指向對應於一個用戶的密碼結構指針。對於getpwuid函數,用戶是由UID標識的,而對於getpwnam函數,用戶是由登陸來標識的。如果出錯,他們都會返回一個空指針並且設置errno變量。
試驗--用戶信息
這裏有一個程序,user.c,這會由密碼數據庫中得到一些用戶信息:
#include
#include
#include
#include
int main()
{
    uid_t uid;
    gid_t gid;
    struct passwd *pw;
    uid = getuid();
    gid = getgid();
    printf(“User is %s\n”, getlogin());
    printf(“User IDs: uid=%d, gid=%d\n”, uid, gid);
    pw = getpwuid(uid);
    printf(“UID passwd entry:\n name=%s, uid=%d, gid=%d, home=%s, shell=%s\n”,
        pw->pw_name, pw->pw_uid, pw->pw_gid, pw->pw_dir, pw->pw_shell);
    pw = getpwnam(“root”);
    printf(“root passwd entry:\n”);
    printf(“name=%s, uid=%d, gid=%d, home=%s, shell=%s\n”,
        pw->pw_name, pw->pw_uid, pw->pw_gid, pw->pw_dir, pw->pw_shell);
    exit(0);
}
其程序輸出如下所示,也許在不同的系統上其輸出結果會略有不同:
$ ./user
User is neil
User IDs: uid=500, gid=100
UID passwd entry:
name=neil, uid=500, gid=100, home=/home/neil, shell=/bin/bash
root passwd entry:
name=root, uid=0, gid=0, home=/root, shell=/bin/bash
工作原理
這個程序會調用getuid函數來得到當前用戶的UID值。這個UID會用在getpwuid函數中來得到詳細的密碼文件信息。作爲一個對照,我們演示瞭如何在getpwnam函數中指定用戶名root來得到用戶信息。
要遍歷密碼文件信息,我們可以使用getpwent函數。這會取得連續的文件實體:
#include
#include
void endpwent(void);
struct passwd *getpwent(void);
void setpwent(void);
getpwent
函數會依次返回每個用戶信息。當到達文件結尾時,他會返回一個空指針。一旦搜索完畢所有的有效實體,我們可以使用endpwent函數來結束處理過程。
setpwent函數可以在密碼文件中重新設置指針使其指向文件的開頭,這樣當下次調用getpwent函數時可以重新開始遍歷。這些函數的操作與我們在
第3章所討論的目錄遍歷函數opendir,readdir,closedir相類似。
用戶與組標識可由其他的一個通常不用的函數來得到:
#include
#include
uid_t geteuid(void);
gid_t getgid(void);
gid_t getegid(void);
int setuid(uid_t uid);
int setgid(gid_t gid);
我們可以查看系統手冊頁來得到關於組標識與有效用戶標識的更爲詳細的內容,儘管我們會發現我們根本就不需要來操作這些函數。
               
               
               

本文來自ChinaUnix博客,如果查看原文請點:http://blog.chinaunix.net/u/19185/showart_600127.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章