UNIX再學習 -- 文件I/O

分類:IT技術 時間:2017-03-24

在 UNIX/linux 系統中,一切皆文件,這句話想必都有聽過。對於文件的操作幾乎適用於所有的設備,這也就看出了文件操作的重要性了。在C語言再學習部分有講過標準I/O文件操作,參看:C語言再學習 -- 文件 下面我們來講解下系統文件I/O的。

一、文件描述符

1、文件描述符簡介

首先從文件描述符開始講起。因為,對於內核而言,所有打開的文件都是通過文件描述符引用的。那麽文件描述符到底是什麽?

文件描述符(file descriptor)通常是一個小的非負整數,內核用以標識一個特定進程正在訪問的文件。當打開一個現有文件或創建一個新文件時,內核向進程返回一個文件描述符。當讀、寫一個文件時,使用 open 或 create 返回的文件描述符標識該文件,將其作為參數傳送給 read 或 write。

2、標準輸入、標準輸出和標準錯誤

按照慣例,UNIX 系統 shell 把文件描述符 0 與進程的標準輸入(stdin)關聯,文件描述符 1標準輸出(stdout)關聯,文件描述符 2標準錯誤(stderr)關聯。這是各種 shell 以及很多應用程序使用的慣例,與 UNIX 內核無關。盡管如此,如果不遵循這種慣例,很多 UNIX 系統應用程序就不能正常工作。

這部分講 shell 編程重定向時,正好講到了,參看UNIX再學習 -- shell編程

POSIX 定義了 STDIN_FILENO、STDOUT_FILENO 和 STDERR_FILENO 來代替 0、1、2。這三個符號常量的定義位於頭文件 <unistd.h>

查看 /usr/include/unistd.h
/* Standard file descriptors.  */
#define STDIN_FILENO    0   /* Standard input.  */
#define STDOUT_FILENO   1   /* Standard output.  */
#define STDERR_FILENO   2   /* Standard error output.  */
文件描述符的有效範圍是 0 到 OPEN_MAX。一般來說,每個進程最多可以打開 64 個文件(0 — 63)。對於 FreeBSD 8.0、Linux 3.2.0、Mac OS X 10.6.8 以及 Solaris 10 來說,文件描述符的變化範圍幾乎是無限的,它只受系統配置的存儲器總量、整型的字長以及系統管理員所配置的軟限制和硬限制的約束。

OPEN_MAX:每個進程最大打開文件數。查看如下:

查看 man sysconf
PEN_MAX - _SC_OPEN_MAX
              The maximum number of files that a process can have open at any time.  Must not be less than _POSIX_OPEN_MAX (20).
或參看:man sysconf()
#include <unistd.h>
#include <stdio.h>
int main (void)
{
    printf("_SC_ARG_MAX: %ld\n", sysconf(_SC_ARG_MAX));
    printf("_SC_CHILD_MAX: %ld\n", sysconf(_SC_CHILD_MAX));
    printf("_SC_CLK_TCK: %ld\n", sysconf(_SC_CLK_TCK));
    printf("_SC_NGROUPS_MAX: %ld\n", sysconf(_SC_NGROUPS_MAX));
    printf("_SC_OPEN_MAX: %ld\n", sysconf(_SC_OPEN_MAX));
    printf("_SC_JOB_CONTROL: %ld\n", sysconf(_SC_JOB_CONTROL));
    printf("_SC_SAVED_IDS: %ld\n", sysconf(_SC_SAVED_IDS));
    printf("_SC_version: %ld\n", sysconf(_SC_VERSION));
	return 0;
}
輸出結果:
_SC_ARG_MAX: 2097152
_SC_CHILD_MAX: 7892
_SC_CLK_TCK: 100
_SC_NGROUPS_MAX: 65536
_SC_OPEN_MAX: 1024
_SC_JOB_CONTROL: 1
_SC_SAVED_IDS: 1
_SC_VERSION: 200809
可得,在 ubuntu 12.04 下測試結果為:_SC_OPEN_MAX =1024 

而在 /apue.3e/exercises/openmax.c 也是有獲取系統的 _SC_OPEN_MAX 的程序,可自行查看。

3、最大文件描述符數

通過上面可知,默認的 Linux 進程的最大打開文件描述符數是 1024。但是在某些情況下這點文件描述符數是遠遠不夠的,可根據需要進行更改。

第一種方法:通過 ulimit 命令修改  

參看:ulimit 命令    ulimit 用於限制 shell 啟動進程所占用的資源,支持以下各種類型的限制:所創建的內核文件的大小、進程數據塊的大小、Shell 進程創建文件的大小、內存鎖住的大小、常駐內存集的大小、打開文件描述符的數量、分配堆棧的最大大小、CPU 時間、單個用戶的最大線程數、Shell 進程所能使用的最大虛擬內存。同時,它支持硬資源和軟資源的限制。 作為臨時限制,ulimit 可以作用於通過使用其命令登錄的 shell 會話,在會話終止時便結束限制,並不影響於其他 shell 會話。而對於長期的固定限制,ulimit 命令語句又可以被添加到由登錄 shell 讀取的文件中,作用於特定的 shell 用戶。選項:
選項 [options]	含義	例子
-H	設置硬資源限制,一旦設置不能增加。	      ulimit – Hs 64;限制硬資源,線程棧大小為 64K。
-S	設置軟資源限制,設置後可以增加,但是不能超過硬資源設置。	        ulimit – Sn 32;限制軟資源,32 個文件描述符。
-a	顯示當前所有的 limit 信息。	           ulimit – a;顯示當前所有的 limit 信息。
-c	最大的 core 文件的大小, 以 blocks 為單位。      	ulimit – c unlimited; 對生成的 core 文件的大小不進行限制。
-d	進程最大的數據段的大小,以 Kbytes 為單位。     	ulimit -d unlimited;對進程的數據段大小不進行限制。
-f	進程可以創建文件的最大值,以 blocks 為單位。     	ulimit – f 2048;限制進程可以創建的最大文件大小為 2048 blocks。
-l	最大可加鎖內存大小,以 Kbytes 為單位。       	ulimit – l 32;限制最大可加鎖內存大小為 32 Kbytes。
-m	最大內存大小,以 Kbytes 為單位。       	ulimit – m unlimited;對最大內存不進行限制。
-n	可以打開最大文件描述符的數量。	       ulimit – n 128;限制最大可以使用 128 個文件描述符。
-p	管道緩沖區的大小,以 Kbytes 為單位。	      ulimit – p 512;限制管道緩沖區的大小為 512 Kbytes。
-s	線程棧大小,以 Kbytes 為單位。      	ulimit – s 512;限制線程棧的大小為 512 Kbytes。
-t	最大的 CPU 占用時間,以秒為單位。     	ulimit – t unlimited;對最大的 CPU 占用時間不進行限制。
-u	用戶最大可用的進程數。     	ulimit – u 64;限制用戶最多可以使用 64 個進程。
-v	進程最大可用的虛擬內存,以 Kbytes 為單位。    	ulimit – v 200000;限制最大可用的虛擬內存為 200000 Kbytes。

英文信息,可 man bash 查看
   ulimit [-HSTabcdefilmnpqrstuvx [limit]]
              Provides  control  over  the resources available to the shell and to processes started by it, on systems that allow such con‐
              trol.  The -H and -S options specify that the hard or soft limit is set for the given  resource.   A  hard  limit  cannot  be
              increased  by a non-root user once it is set; a soft limit may be increased up to the value of the hard limit.  If neither -H
              nor -S is specified, both the soft and hard limits are set.  The value of limit can be a number in the unit specified for the
              resource  or  one  of  the  special values hard, soft, or unlimited, which stand for the current hard limit, the current soft
              limit, and no limit, respectively.  If limit is omitted, the current value of the soft limit  of  the  resource  is  printed,
              unless  the  -H  option  is  given.  When more than one resource is specified, the limit name and unit are printed before the
              value.  Other options are interpreted as follows:
              -a     All current limits are reported
              -b     The maximum socket buffer size
              -c     The maximum size of core files created
              -d     The maximum size of a process's data segment
              -e     The maximum scheduling priority ("nice")
              -f     The maximum size of files written by the shell and its children
              -i     The maximum number of pending signals
              -l     The maximum size that may be locked into memory
              -m     The maximum resident set size (many systems do not honor this limit)
              -n     The maximum number of open file descriptors (most systems do not allow this value to be set)
              -p     The pipe size in 512-byte blocks (this may not be set)
              -q     The maximum number of bytes in POSIX message queues
              -r     The maximum real-time scheduling priority
              -s     The maximum stack size
              -t     The maximum amount of cpu time in seconds
              -u     The maximum number of processes available to a single user
              -v     The maximum amount of virtual memory available to the shell and, on some systems, to its children
              -x     The maximum number of file locks
              -T     The maximum number of threads

              If limit is given, it is the new value of the specified resource (the -a option is display only).  If  no  option  is  given,
              then  -f  is  assumed.   Values  are  in  1024-byte  increments, except for -t, which is in seconds, -p, which is in units of
              512-byte blocks, and -T, -b, -n, and -u, which are unscaled values.  The return status is 0 unless an invalid option or argu‐
              ment is supplied, or an error occurs while setting a new limit.
實例:

顯示當前最大打開文件描述符數

# ulimit -n
1024

修改當前用戶環境下的最大打開文件描述符數 (臨時更改)

設置當前用戶環境下的最大打開文件描述符數為 65536
# ulimit -HSn 65536

查看:
# ulimit -n
65536

將指令添加到腳本中 (永久更改)

添加到 單用戶目錄下:/etc/bash.bashrc 或 ~/.bashrc

echo "ulimit -HSn 65536" >> ~/.bashrc
或者
echo "ulimit -HSn 65536" >> /etc/bash.bashrc
我使用的是 root 超級用戶登錄,而非 non-root 登錄的,所以放在 /etc/profile 等針對所有用戶的不起作用。

還有網上說的寫到 rc.local 我也沒有實現。

第二種方法:修改 limits.conf 文件 (永久更改)

/etc/security/limits.conf 文件最後加入如下兩行:

* soft nofile 65536
* hard nofile 65536
 其中 * 代表所有用戶nofile 是代表最大文件打開數用 non-root 登錄,通過 ulimit -n 查看是否生效。

告訴你個不幸的消息,很遺憾我的沒有生效。將 * 改為 root 則可以生效,因為我用的不是 non-root 登錄的。

查看最大文件描述符數上限

當然,這個最大文件描述符數也是有上限的,比如設置一個很大的數,會提示如下錯誤:
# ulimit -n 100000000
bash: ulimit: open files: 無法修改 limit 值: 不允許的操作
而最大文件描述符數的上限值是在 /proc/sys/fs/nr_open 設置的,默認為 1048576你也可以修改它,比如執行:
 echo 2000000 > /proc/sys/fs/nr_open

4、混淆的概念

在我看到的很多文章裏,有不少將 /proc/sys/fs/file-max 看作了 最大文件描述符數的上限值,其實是不對的。那麽到底 file-max 和 nr_open 有什麽區別呢?查看 linux/Documentation/sysctl/fs.txt,可看到關於 file-max 和 nr_open 的解釋
file-max & file-nr:

The kernel allocates file handles dynamically, but as yet it
doesn't free them again.

The value in file-max denotes the maximum number of file-
handles that the Linux kernel will allocate. When you get lots
of error messages about running out of file handles, you might
want to increase this limit.

Historically, the three values in file-nr denoted the number of
allocated file handles, the number of allocated but unused file
handles, and the maximum number of file handles. Linux 2.6 always
reports 0 as the number of free file handles -- this is not an
error, it just means that the number of allocated file handles
exactly matches the number of used file handles.

Attempts to allocate more file descriptors than file-max are
reported with printk, look for "VFS: file-max limit <number>
reached".
nr_open:

This denotes the maximum number of file-handles a process can
allocate. Default value is 1024*1024 (1048576) which should be
enough for most machines. Actual limit depends on RLIMIT_NOFILE
resource limit.
翻譯一下:關於 file-max內核可以動態的分配文件句柄,但到目前為止是不會釋放它們的。
file-max 的值表示Linux內核分配的最大文件句柄數如果你看到了很多關於打開文件數已經達到了最大值的錯誤信息,你可以試著增加該值的限制。在kernel 2.6之前的版本中,file-nr 中的值由三部分組成,分別為:1.已經分配的文件句柄數,2.已經分配但沒有使用的文件句柄數,3.最大文件句柄數。但在 kernel 2.6 版本中第二項的值總為 0,這並不是一個錯誤,它實際上意味著已經分配的文件句柄無一浪費的都已經被使用了。
關於 nr_opennr_open 的值表示一個進程可以分配的最大文件句柄數。 默認值為1024 * 1024(1048576),應該足夠用於大多數機器。 實際限制取決於RLIMIT_NOFILE資源限制。

(1)概念解析

先說 file_max

前面的翻譯,我們已經知道 file-max 的值表示Linux內核分配的最大文件句柄數讓我們再查看一下更多關於它的內容: man proc
 /proc/sys/fs/file-max
              This  file  defines  a system-wide limit on the number of open files for all processes.  (See also setrlimit(2), which can be
              used by a process to set the per-process limit, RLIMIT_NOFILE, on the number of files it may open.)  If you get lots of error
              messages about running out of file handles, try increasing this value:

              echo 100000 > /proc/sys/fs/file-max

              The kernel constant NR_OPEN imposes an upper limit on the value that may be placed in file-max.

              If  you  increase  /proc/sys/fs/file-max,  be  sure  to  increase  /proc/sys/fs/inode-max  to  3-4  times  the  new  value of
              /proc/sys/fs/file-max, or you will run out of inodes.
翻譯一下:該文件定義了所有進程的打開文件數量的系統範圍限制。 (另見setrlimit(2),可以是被一個進程用來設置每個進程的限制,RLIMIT_NOFILE,對它可能打開的文件數。)如果你收到很多錯誤關於運行文件句柄的消息,嘗試增加此值。
內核常量 NR_OPEN 對可以放在 file-max中 的值施加上限。如果增加 /proc/sys/fs/file-max,請確保將 /pro /sys/fs/inode-max 增加到新值的 3-4 倍,否則 /proc/sys/fs/file-max,否則將用盡inode。比 file-max 中的值大 3-4 倍,因為 stdin,stdout 和網絡套接字也需要一個 inode 來處理它們。

意思是,修改file-max時也要按其值的 3-4 倍來修改 inode-max。而file-max 的值我們可以使用 cat 查看:
# cat /proc/sys/fs/file-max
100987
而有種說法是 file-max 一般為內存大小(KB)的 10% 來計算,如果使用 shell,可以這樣計算:
# grep MemTotal /proc/meminfo | awk '{printf("%d\n",$2/10)}'
102479
經比較,可能由於有各種其他原因導致 file-max 沒有設置為內存的 10%。
再有關於 file-nr 中的值由三部分組成,分別為:1.已經分配的文件句柄數,2.已經分配但沒有使用的文件句柄數,3.最大文件句柄數。第二項的值總為 0,這並不是一個錯誤,它實際上意味著已經分配的文件句柄無一浪費的都已經被使用了。
# cat /proc/sys/fs/file-nr
6464	0	100987

而,file-max 的值也有兩種方式修改第一種:臨時更改
echo 1000000 > /proc/sys/fs/file-max
第二種:永久更改修改 /etc/sysctl.conf 文件,末尾增加 fs.file-max = 1000000

註意幾句話,file-max 的值表示Linux內核分配的最大文件句柄數file-max 該文件定義了所有進程的打開文件數量的系統範圍限制。(系統級)
ulimit 用於限制 shell 啟動進程所占用的資源。 (進程級)
nr_open 的值表示一個進程可以分配的最大文件句柄數。 (進程級)這也間接說明了,nr_open 是 ulimit -HSn 的上限值

(2)系統級 和 進程級 的區別

首先,系統級

我們剛才有查看 file-nr:
# cat /proc/sys/fs/file-nr
6464	0	100987
file-nr 中的值由三部分組成,分別為:1.已經分配的文件句柄數,2.已經分配但沒有使用的文件句柄數,3.最大文件句柄數。也就是說,系統文件描述符的數量目前被使用 6464.例如,查看 proc 目錄

使用 ps 指令查看當前終端啟動的進程使用 ps -aux 表示顯示所有包含其他使用者的進程
使用 ps -aux | more 表示將ps -aux的結果進行分屏顯示
# ps
  PID TTY          TIME CMD
 2401 pts/1    00:00:00 bash
 3054 pts/1    00:00:00 ps
# ps -aux | more
Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME command
root         1  0.0  0.1   3612  2024 ?        Ss   10:53   0:01 /sbin/init
root         2  0.0  0.0      0     0 ?        S    10:53   0:00 [kthreadd]
root         3  0.0  0.0      0     0 ?        S    10:53   0:00 [ksoftirqd/0]
root         6  0.0  0.0      0     0 ?        S    10:53   0:00 [migration/0]
root         7  0.0  0.0      0     0 ?        S    10:53   0:01 [watchdog/0]
root         8  0.0  0.0      0     0 ?        S<   10:53   0:00 [cpuset]
root         9  0.0  0.0      0     0 ?        S<   10:53   0:00 [khelper]
root        10  0.0  0.0      0     0 ?        S    10:53   0:00 [kdevtmpfs]
.....
那我們就看看當前終端啟動的進程 2401lsof 命令可以查出某個進程打開的文件數目,wc -l 只顯示列數lsof使用,參看:lsof 命令
# lsof | grep '2401' | wc -l
20

意思是有20個文件被打開
查詢進程使用的文件描述符數目
# ls -l /proc/2401/fd/ | wc -l
5

意思是文件描述符只有5個
所以說,一個文件即使被打開,也可能沒有文件描述符,比如當前工作目錄,內存映射文件和可執行文本文件。
以上的 fd 就是系統文件描述符。

再者,進程級

比如,使用open 和 close 函數查看 fd 的值:
//open函數和close函數的使用
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{
	
//	while (1){
		int fd = open("a.txt",O_WRONLY|O_CREAT|O_TRUNC,0644);
		if(-1 == fd)
		{
			perror("open"),exit(-1);
		}


		printf ("fd = %d\n", fd);
#if 1		
		//2.關閉文件
		int res = close(fd);
		if(-1 == res)
		{
			perror("close"),exit(-1);
		}

#endif 
//	}
	return 0;
}
輸出結果:
fd = 3
該程序,除去標準輸入(0)、標準輸出(1)、標準錯誤(2),文件描述符從 3 開始將上面的程序改為循環,其結果為:
忽略....
fd = 1019
fd = 1020
fd = 1021
fd = 1022
fd = 1023
open: Too many open files
可以看到當 fd = 1023 再往後,出現錯誤 open: Too many open files,而這個 1024,不就是在 Ubuntu 12.04 下測試結果為:_SC_OPEN_MAX =1024 即:
# ulimit -n
1024
這裏的 fd 是進程級的文件描述符。

5、文件描述符和文件指針

(1)文件描述符和文件指針比較

參看:文件句柄(file handles) & 文件描述符(file descriptors)
文件描述符(fd):在linux系統中打開文件就會獲得文件描述符,它是個很小的正整數。每個進程在PCB(Process Control Block)中保存著一份文件描述符表,文件描述符就是這個表的索引,每個表項都有一個指向已打開文件的指針。文件指針(FILE*):C 語言中使用文件指針做為 I/O的句柄。文件指針指向進程用戶區中的一個被稱為 FILE 結構的數據結構。FILE 結構包括一個緩沖區和一個文件描述符。而文件描述符是文件描述符表的一個索引,因此從某種意義上說文件指針就是句柄的句柄(在Windows系統上,文件描述符被稱作文件句柄)。
通過 /usr/include/libio.h 查看 C 語言中 _IO_FILE 結構體的定義: 
struct _IO_FILE {
  int _flags;		/* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags

  /* The following pointers correspond to the C++ streambuf protocol. */
  /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
  char* _IO_read_ptr;	/* Current read pointer */
  char* _IO_read_end;	/* End of get area. */
  char* _IO_read_base;	/* Start of putback+get area. */
  char* _IO_write_base;	/* Start of put area. */
  char* _IO_write_ptr;	/* Current put pointer. */
  char* _IO_write_end;	/* End of put area. */
  char* _IO_buf_base;	/* Start of reserve area. */
  char* _IO_buf_end;	/* End of reserve area. */
  /* The following fields are used to support backing up and undo. */
  char *_IO_save_base; /* Pointer to start of non-current get area. */
  char *_IO_backup_base;  /* Pointer to first valid character of backup area */
  char *_IO_save_end; /* Pointer to end of non-current get area. */

  struct _IO_marker *_markers;

  struct _IO_FILE *_chain;

  int _fileno;
#if 0
  int _blksize;
#else
  int _flags2;
#endif
  _IO_off_t _old_offset; /* This used to be _offset but it's too small.  */

#define __HAVE_COLUMN /* temporary */
  /* 1+column number of pbase(); 0 is unknown. */
  unsigned short _cur_column;
  signed char _vtable_offset;
  char _shortbuf[1];

  /*  char* _save_gptr;  char* _save_egptr; */

  _IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};
我們可以通過 /usr/include/stdio.h  C  語言中 FILE 結構體的定義:
/* Define outside of namespace so the C++ is happy.  */
struct _IO_FILE;

__BEGIN_NAMESPACE_STD
/* The opaque type of streams.  This is the definition used elsewhere.  */
typedef struct _IO_FILE FILE;
__END_NAMESPACE_STD
#if defined __USE_LARGEFILE64 || defined __USE_SVID || defined __USE_POSIX \
    || defined __USE_BSD || defined __USE_ISOC99 || defined __USE_XOPEN \
    || defined __USE_POSIX2
__USING_NAMESPACE_STD(FILE)
#endif

# define __FILE_defined	1
#endif /* FILE not defined.  */
#undef	__need_FILE


#if !defined ____FILE_defined && defined __need___FILE

/* The opaque type of streams.  This is the definition used elsewhere.  */
typedef struct _IO_FILE __FILE;
在這個_IO_FILE結構體中的“int _fileno”就是fd,即文件描述符。舉例驗證:
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>

int main(){
        char buf[50] = {"file descriptor demo"};
        FILE *myfile;

        myfile = fopen("test","w+");
        if(!myfile){
                printf("error: openfile failed!\n");
        }
        printf("The openfile's descriptor is %d\n", myfile->_fileno);
        if(write(myfile->_fileno,buf,50)<0){
                perror("error: write file failed!\n");
                exit(1);
        }else{
                printf("writefile successed!\n");
        }

        exit(0);
}
輸出結果:
The openfile's descriptor is 3
writefile successed!

查看 test
cat test 
file descriptor demoroot

(2)文件描述符和文件指針相互轉換

文件指針轉文件描述符:

參看:百度百科--fileno函數 filenoint _fileno( FILE *stream );
 fileno 用來取得參數stream指定的文件流所使用的文件描述符。
實例:
#include <stdio.h>
int main( void )
{
	printf( "The file descriptor for stdin is %d\n", fileno( stdin ) );
	printf( "The file descriptor for stdout is %d\n", fileno( stdout ) );
	printf( "The file descriptor for stderr is %d\n", fileno( stderr ) );
	return 0;
}
輸出結果:
The file descriptor for stdin is 0
The file descriptor for stdout is 1
The file descriptor for stderr is 2
#include <stdio.h>
int main(void)
{
	FILE *fp;
	int fd;
	fp = fopen("/etc/passwd", "r");
	fd = fileno(fp);
	printf("fd = %d\n", fd);
	fclose(fp);
	return 0;
}
輸出結果:
fd = 3

文件描述符轉文件指針:

參看:百度百科--fdopen函數 fdopen
FILE* fdopen(int fd, const char* type);
fdopen 取一個現存的文件描述符,並使一個標準的I / O流與該描述符相結合。此函數常用於由創建管道和網絡通信通道函數獲得的描述符。因為這些特殊類型的文件不能用標準I/O fopen函數打開,首先必須先調用設備專用函數以獲得一個文件描述符,然後用fdopen使一個標準I/O流與該描述符相結合。
實例:標準輸出文件描述符為1
#include<stdio.h>
int main (void)
{
	FILE * fp = fdopen (1, "w+");
	fprintf (fp, "%s\n", "hello!");
	fclose (fp);
	return 0;
}
輸出結果:
hello!


Tags: 應用程序 include create C語言 Linux

文章來源:


ads
ads

相關文章
ads

相關文章

ad