一)概述 .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(打开文件的总数)一致. |
>>更多交流,请到 ChinaUnix【Linux系统管理论坛】:http://linux.chinaunix.net/bbs/forum-2-1.html |
linux进程与它的文件描述符、/proc/self 表示当前进程目录、lsof
最新推荐文章于 2024-05-16 08:00:53 发布