linux進程與它的檔案描述符

來源:互聯網
上載者:User

一)概述
.open系統調用返回的檔案描述符是非負整型.
.每一個進程都有自己的檔案描述符集合.
.當建立進程時,通常有3個開啟檔案描述符(0,1,2),0代表標準輸入,1代表標準輸出,2代表標準錯誤,它們統稱為標準IO.
.當多個描述符指向同一個檔案,每個檔案描述符仍保持他獨特的效能.
.由於檔案描述符在一個進程中是特有的,因此不能在多個進程中間實現共用,而唯一的例外是在父/子進程之間,當一個進程調用fork時,調用fork時開啟的所有檔案在子進程和父進程中仍然是開啟的,而且子進程寫入檔案描述符會影響到父進程的同一檔案描述符,反之亦然.

 

二)父/子進程對檔案描述符的分支使用樣本

來源程式如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/times.h>
#include <sys/stat.h>
#include <sys/wait.h>

void writestr(int fd, char *buf)
{
        int r = write(fd, buf, strlen(buf));
        if (r == -1)
                perror(buf);
}

void busywait(void)
{
        clock_t t1 = times(NULL);
        while (times(NULL) - t1 < 2);
}

int main (int argc, char *argv[])
{
        int fd = open("thefile.txt",
                        O_CREAT | O_TRUNC | O_RDWR,
                        S_IRWXU | S_IRWXG | S_IRWXO);
        assert(fd != -1);
        writestr(fd, "This is the parent.\n");
        pid_t pid = fork();

        busywait();
        if(pid == 0){
                writestr(fd,"Child write\n");
        }
        else{
                writestr(fd, "Hi it's me. Im back.\n");

                int status;
                waitpid(pid, &status, 0);
        }
        close(fd);
        return 0;
}

編譯fork-file.c
gcc fork-file.c -o fork-file

執行fork-file,同時查看thefile.txt,如下:
./fork-file && cat thefile.txt
This is the parent.
Child write
Hi it's me. Im back.

./fork-file && cat thefile.txt
This is the parent.
Hi it's me. Im back.
Child write

注:
1)我們發現每次運行結果的順序都不相同,有時父進程會先調用write系統調用,有時子進程會先調用write.
2)子進程繼承了父進程的檔案描述符,如本例子open系統調用得到的檔案描述符是3,在子進程和父進程中的檔案描述符都是3.

 

三)檔案描述符與/proc

子目錄/proc/self本身就是當前運行進程ID的符號連結.

用ls -ld查看/proc/self目錄的符號連結,發現每次都不一樣,說明我們每次用ls命令時的進程ID都是不同的.
ls -ld /proc/self
lrwxrwxrwx 1 root root 64 2010-10-10 06:25 /proc/self -> 30525

我們查看/proc/self/fd目錄下的檔案描述符,如下:
ls -l /proc/self/fd
total 0
lrwx------ 1 root root 64 2010-10-10 12:16 0 -> /dev/pts/1
lrwx------ 1 root root 64 2010-10-10 12:16 1 -> /dev/pts/1
lrwx------ 1 root root 64 2010-10-10 12:16 2 -> /dev/pts/1
lr-x------ 1 root root 64 2010-10-10 12:16 3 -> /proc/30578/fd

我們看到了3個標準的IO描述符,它們都被軟錠接到了/dev/pts/1,/dev/pts/1是我們通過ssh開啟第2個終端,如果是第1個終端,那將是/dev/pts/0.
如果我們通過ipmi的串口登入,這裡應該是/dev/ttySx,而如果是本地登入那應該是/dev/ttyx,如果是單使用者登入那將是/dev/console.
/dev/pts/x是虛擬終端
/dev/ttySx是串列控制端
/dev/ttyx是控制台
/dev/console是單使用者控制台

檔案描述符3,這個描述符是ls進程開啟/proc/self/fd(也就是/proc/30578/fd)所得到的檔案描述符.

同樣我們將ls -l /proc/self/fd的資訊輸出的一個檔案中,如下:
ls -l /proc/self/fd > /tmp/foo.txt && cat /tmp/foo.txt
total 0
lrwx------ 1 root root 64 2010-10-10 12:22 0 -> /dev/pts/1
l-wx------ 1 root root 64 2010-10-10 12:22 1 -> /tmp/foo.txt
lrwx------ 1 root root 64 2010-10-10 12:22 2 -> /dev/pts/1
lr-x------ 1 root root 64 2010-10-10 12:22 3 -> /proc/31005/fd
這時標準輸出的檔案描述符不再是/dev/pts/1了,而是/tmp/foo.txt

再如我們將/dev/null做為標準輸入,如下所示:
ls -l /proc/self/fd > /tmp/foo.txt < /dev/null && cat /tmp/foo.txt
total 0
lr-x------ 1 root root 64 2010-10-10 12:30 0 -> /dev/null
l-wx------ 1 root root 64 2010-10-10 12:30 1 -> /tmp/foo.txt
lrwx------ 1 root root 64 2010-10-10 12:30 2 -> /dev/pts/1
lr-x------ 1 root root 64 2010-10-10 12:30 3 -> /proc/31998/fd
此時標準輸入的檔案描述符也不再是/dev/pts/1了,而是/dev/null

最後我們將/dev/null做為標準錯誤輸出,如下所示:
ls -l /proc/self/fd > /tmp/foo.txt 2< /dev/null && cat /tmp/foo.txt
total 0
lrwx------ 1 root root 64 2010-10-10 12:37 0 -> /dev/pts/1
l-wx------ 1 root root 64 2010-10-10 12:37 1 -> /tmp/foo.txt
lr-x------ 1 root root 64 2010-10-10 12:37 2 -> /dev/null
lr-x------ 1 root root 64 2010-10-10 12:37 3 -> /proc/32435/fd
此時標準錯誤輸出的檔案描述符也不再是/dev/pts/1了,而是/dev/null

 

四)檔案描述符與lsof命令

用lsof命令可以查看系統中所有進程的所有開啟檔案,前提是你有相應的許可權.

我們首先查看當前bash的PID
echo $$
3174

用lsof查看開啟的檔案.
lsof -p 3174
COMMAND  PID USER   FD   TYPE DEVICE    SIZE   NODE NAME
bash    3174 root  cwd    DIR    8,1    4096  32577 /root
bash    3174 root  rtd    DIR    8,1    4096      2 /
bash    3174 root  txt    REG    8,1  797784 309511 /bin/bash
bash    3174 root  mem    REG    8,1   47520 147727 /lib/libnss_files-2.7.so
bash    3174 root  mem    REG    8,1   43472 147716 /lib/libnss_nis-2.7.so
bash    3174 root  mem    REG    8,1   88968 147720 /lib/libnsl-2.7.so
bash    3174 root  mem    REG    8,1   31536 147722 /lib/libnss_compat-2.7.so
bash    3174 root  mem    REG    8,1 4636768 782032 /usr/lib/locale/locale-archive
bash    3174 root  mem    REG    8,1 1375536 147730 /lib/libc-2.7.so
bash    3174 root  mem    REG    8,1   14616 147734 /lib/libdl-2.7.so
bash    3174 root  mem    REG    8,1  256288 147710 /lib/libncurses.so.5.7
bash    3174 root  mem    REG    8,1  119288 147733 /lib/ld-2.7.so
bash    3174 root  mem    REG    8,1   25700 767172 /usr/lib/gconv/gconv-modules.cache
bash    3174 root    0u   CHR  136,1              3 /dev/pts/1
bash    3174 root    1u   CHR  136,1              3 /dev/pts/1
bash    3174 root    2u   CHR  136,1              3 /dev/pts/1
bash    3174 root  255u   CHR  136,1              3 /dev/pts/1

在/proc中查看開啟的檔案
ls -l /proc/3174/fd
total 0
lrwx------ 1 root root 64 2010-10-10 13:13 0 -> /dev/pts/1
lrwx------ 1 root root 64 2010-10-10 13:13 1 -> /dev/pts/1
lrwx------ 1 root root 64 2010-10-10 13:13 2 -> /dev/pts/1
lrwx------ 1 root root 64 2010-10-10 13:15 255 -> /dev/pts/1

我們發現lsof不只是顯示檔案描述符,在lsof輸出的最後四個檔案是該進程的開啟檔案的檔案描述符,0u代表的檔案描述符為0,而255u代表的檔案描述符為255,u的意思為可讀可寫.
除了這四個檔案描述符外,在FD一欄還有cwd,rtd,txt,mem等幾種類型,它們的意義如下:
cwd代表目前的目錄,這裡是/root
rtd代表根目錄,這裡是/
txt代表執行的程式,這裡是/bin/bash
mem代表映射到記憶體的檔案,這裡是/lib/libc-2.7.so等動態連結程式庫

TYPE一欄表示檔案/目錄的類型,DIR代表目錄,REG代表普通檔案,CHR代表字元裝置.

我們在偽終端1,執行vi -d /etc/hosts /etc/mtab

在偽終端2,用lsof查看開啟的檔案.

lsof -p 6195
COMMAND  PID USER   FD   TYPE DEVICE    SIZE   NODE NAME
vi      6195 root  cwd    DIR    0,3       0      1 /proc
vi      6195 root  rtd    DIR    8,1    4096      2 /
vi      6195 root  txt    REG    8,1 1699024 686572 /usr/bin/vim.basic
vi      6195 root  mem    REG    8,1   47520 147727 /lib/libnss_files-2.7.so
vi      6195 root  mem    REG    8,1   43472 147716 /lib/libnss_nis-2.7.so
vi      6195 root  mem    REG    8,1   88968 147720 /lib/libnsl-2.7.so
vi      6195 root  mem    REG    8,1   31536 147722 /lib/libnss_compat-2.7.so
vi      6195 root  mem    REG    8,1 4636768 782032 /usr/lib/locale/locale-archive
vi      6195 root  mem    REG    8,1   17424 147652 /lib/libattr.so.1.1.0
vi      6195 root  mem    REG    8,1   14616 147734 /lib/libdl-2.7.so
vi      6195 root  mem    REG    8,1 1375536 147730 /lib/libc-2.7.so
vi      6195 root  mem    REG    8,1   23616 767588 /usr/lib/libgpm.so.2.0.0
vi      6195 root  mem    REG    8,1   29360 147707 /lib/libacl.so.1.1.0
vi      6195 root  mem    REG    8,1  109464 146602 /lib/libselinux.so.1
vi      6195 root  mem    REG    8,1  256288 147710 /lib/libncurses.so.5.7
vi      6195 root  mem    REG    8,1  119288 147733 /lib/ld-2.7.so
vi      6195 root    0u   CHR  136,0              2 /dev/pts/0
vi      6195 root    1u   CHR  136,0              2 /dev/pts/0
vi      6195 root    2u   CHR  136,0              2 /dev/pts/0
vi      6195 root    4u   REG    8,1   12288 480777 /etc/.hosts.swp
vi      6195 root    5u   REG    8,1   12288 482077 /etc/.mtab.swp

我們看到vi建立了臨時檔案/etc/.hosts.swp和/etc/.mtab.swp,同時它的執行程式是/usr/bin/vim.basic,應該是確實是vi的二進位程式.
cwd是/proc,說明偽終端1下的目前的目錄是/proc。
其實我們可以通過w確認當前系統的偽終端,再通過lsof|grep 'pts/0'查看使用該偽終端的PID,最後用lsof -p PID查看該使用者在做什麼.
如果有具體的使用者名稱,可以直接用lsof -u username來查看該使用者當前載入的檔案.

 

五)檔案描述符的限制

每個進程允許開啟檔案的實際數目是由核心決定的,但是可以使用sysconf函數在運行時進行查看.如下:

vi process_limit.c

原始碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

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

        process = sysconf(_SC_OPEN_MAX);
        printf("current process total:%lu\n", process);
        return 0;
}

編譯連結:
gcc process_limit.c -o process_limit
./process_limit
current process total:819200

進程可以開啟檔案的總數為819200,與ulimit -n(開啟檔案的總數)一致.

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.