《unix环境高级编程》 读书笔记 (1)

简介: 目录: http://blog.csdn.net/alex_my/article/details/39346381 File I/O 1 file descriptor // 查看当前session中的fd数量限制  ulimit -n // 修改当前session中的fd数量限制 ulimit -n your_need 可以在一个终端


File I/O


1 file descriptor

// 查看当前session中的fd数量限制 
ulimit -n

// 修改当前session中的fd数量限制
ulimit -n your_need

可以在一个终端使用以上命令,另外再打开一个进行查看。

在程序中,可以使用系统函数进行修改
#include <sys/resource.h>

struct rlimit
{
     rlim_t rlim_cur;
     rlim_t rlim_max;
}

int getrlimit(int resource, struct rlimit* rlim);
int setrlimit(int resource, const struct rlimit* rlim);

ext:

resource可选择的有:

RLIMIT_CORE: 设置core文件的大小,0表示禁止创建core文件。一个程序崩溃的时候,会在指定目录生成一个core文件,主要是用于调试。

RLIMIT_CPU: 设置每秒内的cpu Time,如果超过这个限制,则会发送SIGXCPU信号。

RLIMIT_DATA: 设置进程中data segment大小,单位为bytes。如果超过这个限制,malloc()会失败。具体关于data segment 参考这里: http://blog.csdn.net/fatshaw/article/details/6294557

RLIMIT_FSIZE: 设置进程内文件最大长度,如果超过这个长度,则产生SIGXFSZ信号。如果线程阻塞,或者进程抓取或者忽略了这个信号,则会从底部减少文件大小并设置EFBIG错误。

RLIMIT_NOFILE: 设置文件描述符大小,比最大文件描述符大一,如果超过了这个限制,则申请文件描述符失败,并且设置EMFILE错误。

RLIMIT_STACK: 设置进程最大堆栈,单位为bytes。

RLIMIT_AS: 设置进程可用内存,单位为bytes。如果超过这个限制,malloc()和mmap()将会失败,并设置ENOMEM错误。


2 lseek function

off_t lseek(int fildes, off_t offset, int whence)    移动读/写偏移量 

whence可选择的有:

SEEK_SET: 文件偏移量设置为 offset

SEEK_CUR: 文件偏移量设置为 当前偏移量 + offset

SEEK_END: 文件偏移量设置为 文件大小 + offset

获取当前偏移量的方法:

offset value = lseek(fd, 0, SEEK_CUR);

当操作的文件为 pipe, FIFO, socket时,返回-1,并设置错误为ESPIPE。

其它错误可以man 3 lseek查阅

程序用例:test2.1.cc

#include <stdio.h>
#include <unistd.h>

int main(int argc, char** argv)
{
     if(lseek(STDIN_FILENO, 0, SEEK_CUR) == -1)
          printf("Seek Failed\n");
     else
          printf("Seek OK\n");

     return 0;
}

g++ -o test2.1 test2.1.cc

./test2.1
输出:Seek Failed。原因:参数1 fildes必须是已经打开的文件描述符.

./test2.1 < /etc/passwd
输出:Seek OK

另外,关于main函数列表上的两个参数,可以这么记:

int main(int arg-count, char** arg-variable);

第一个是参数数量,第二个是参数变量集合



3 File Sharing -- close-on-exec

当使用fork创建子线程后,子线程获得父进程的数据空间,堆和栈的副本,也包含文件描述符,共享共同的打开文件标记,当前的偏移量等。

当在子进程中调用exec执行另一个程序时,替换了当前进程的正文,数据,堆和栈,即原先的子进程中的文件描述符丢失了(并未关闭),无法再关闭这些在子进程中不再使用到的文件描述符。而文件描述符是系统珍贵资源,数量有限。

一种办法是在执行exec之前先把这些文件描述符关闭,但在复杂的系统中,这是一件比较麻烦的事情。

另一种办法是打开文件的时候就指定好,当子进程执行exec的时候,这个文件关闭。即所谓的close-on-exec。

// 通过fcntl实现
int flags = fcntl(fd, F_GETFD);
flags |= FD_CLOEXEC;
fcntl(fd, F_SETFD, flags);

// 在创建或打开文件的时候指定
int fd = open("text.log", O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC);

具体查看man 2 open或者man 2 creat



4 Atomic Operations -- O_APPEND

有这样一个例子:

程序A和程序B对同一个文件进行写操作。
程序A先写入1500字节,程序B打开,调用lseek,得到文件末尾为1500, 写入100字节。
程序A执行,此时,程序A的file table entry记录文件末尾为1500,写入100字节。此时,覆盖了程序B写入的内容。

解决方案:

// 每次write的时候,都调用lseek,获取文件末尾偏移量。

// 在创建或者打开的时候设置参数O_APPEND, 设置此标记后,每次调用write,文件偏移量都会更新到文件末尾。


5 open a file

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

flags可以选择的值:

必选其一:O_RDONLY, O_WRONLY, O_RDWR

可选:
     O_CLOEXEC   : 查看本文 "3 File Sharing -- close-on-exec"
     O_CREAT     : 如果文件不存在,则创建
     O_DIRECT    : 直接进行文件IO,系统尽量不进行缓存。具体可参考以下链接,查看本文"8 fcntl function"中的
                   内容。
     O_DIRECTORY : 如果path所指文件并非是一个目录,则会打开失败 
     O_EXCL      : 如果O_CREAT和O_EXCL同时设置,如果文件不存在,则会创建文件,如果存在,则会打开失败。
                   如果O_CREAT和O_EXCL同时设置,且欲打开的文件为符号连接,则会打开失败。
     O_NOCTTY    : 如果欲打开的文件为终端机设备,则不会将该终端机当成进程控制终端机。
     O_NOFOLLOW  : 如果path所指文件是一个符号链接,则会打开失败
     O_TRUNC     : 如果文件存在且以可写方式打开时,会令文件长度为0,且数据会丢失。
     O_TTY_INIT  : 恢复终端默认属性。
     O_APPEND    : 读写文件会从文件末尾开始。
     O_SYNC      : 同步方式,任何写操作会阻塞进程直到写入到底层硬件中。
     O_ASYNC     : 
     O_LARGEFILE : 用于打开超大文件,具体说明需另外查找。
     O_NOATIME   : 读文件不更新最后访问时间。
     O_NONBLOCK  : 非阻塞方式
     O_PATH      : 

mode在创建新文件的时候启作用,设置文件属性。
mode可以选择的值:
     位于读书笔记(2)中的第五点:"5 改变文件的操作权限"

程序示例:
// test2.2.cc

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>

int main(int argc, char* argv[])
{
     // 以读写的形式打开一个文件,如果不存在,则创建     
     int fd = ("test2.2.logc", O_RDWR | O_CREAT);
     if(fd == -1)
          printf("file open failed, error[%d].\n", errno);
     else
          printf("file open success, fd[%d].\n", fd);

     return 0;
}


6 creat and close a file

#include <fcntl.h>

int creat(const char *pathname, mode_t mode);

mode可选值如果open中的mode可选值,同样参考:
位于读书笔记(2)中的第五点:"5 改变文件的操作权限"

操作creat相同于如下语句:

open(pathname, O_RDWR | O_CREAT | O_TRUNC, mode);

注意的是,调用creat是以只写方式打开所创建的文件,如果要读文件,必须先close,然后open.

或者使用上述形式的open函数打开一个文件。

#include <unistd.h>

int close(int fd);

当一个进程终止时,内核自动关闭它所有打开的文件。



7 Atomic Operations -- pthread, pwrite

对一个文件指定位置进行写入或者读取,有两个步骤:

-1: 调用lseek,设置偏移量
-2: 调用read或者write操作。

两步操作在过程中可能被打断,得到的不是需要的结果。

而pthread/pwrite可以解决这个问题:

ssize_t pread(int fd, void *buf, size_t count, off_t offset);

ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset); 

使用pthread/pwrite并不会改变当前文件偏移量。

当打开文件时指定了参数O_APPEND时,pwrite总是添加到文件末尾,而不会按照参数offset指定位置。 


8 dup, dup2, dup3

int dup(int oldfd);
int dup2(int oldfd, int newfd);
int dup3(int oldfd, int newfd, int flags);

这些系统调用将会创建一个文件描述符的副本。

dup将返回一个未使用的最小的文件描述符

dup2将返回一个指定的文件描述符。如果oldfd指定了文件描述标记(close-on-exec),newfd并不会拥有。
需要重新使用fcntl指定。

如果newfd已经存在,则会先被关闭。
如果oldfd和newfd数值相同,且oldfd是有效的文件描述符,则dup2不做任何事情,返回newfd。
如果oldfd和newfd数值相同,且oldfd是无效的文件描述符,则调用失败, 不会调用newfd关闭的步骤。

dup3效果与dup2相同,但可以通过指定flags为O_CLOEXEC,使得newfd具有close-on-exec功能。

如果oldfd和newfd数值相同,则调用失败,并设置错误为INVAL。


9 fcntl function

#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ );

可以改变一个已经打开的文件的属性 

其中, cmd 可选择的有:

F_DUPFD: 返回一个大于等于参数3且最小未使用的文件描述符,与fd共享一个文件表项,如果fd有文件描述符标志(目前只有一个,FD_CLOEXEC),则此标志会被清除。可以查看dup2相关。

F_DUPFD_CLOEXEC: (since Linux 2.6.24) 效果等同于F_DUPFD,但会给返回的文件描述符添加close-on-exce标志。

F_GETFD: 返回fd的文件描述符标志。目前仅有一个标志:FD_CLOEXEC

F_SETFD: 将参数3设置为fd的文件描述符标志。

F_GETFL: 返回fd的文件状态标志,即open时候的flags标记。
               
               O_RDONLY, O_WDONLY, O_RDWR, O_EXCL, O_SEARCH, 
               O_APPEND, O_NONBLOCK, O_SYNC, O_DSYNC, O_RSYNC, O_FSYNC, O_ASYNC

F_SETFL: 设置fd的文件状态标志,可设置的值有(Linux下, 本机为CentOS7.0, Mac和FreeBSD有不同的选项):

               O_APPEND, 每次write的时候都将添加到文件的末尾,效果等同于open的时候添加O_APPEND标记。

               O_NONBLOCK, 非阻塞IO,如果read没有数据可读,或者write操作阻塞,则返回-1并且设置错误为 
                         EAGAIN。
           
               O_ASYNC, 当IO可用时,允许发送SIGIO信号到进程组。

               O_DIRECT, 直接进行文件IO,系统尽量不进行缓存。具体可参考以下链接:
                                        
               
               O_NOATIME, 读文件不改变最后访问时间。

另外还有F_GETOWN, F_SETOWN, F_GETOWN_EX等等,具体查看fcntl(2)


10 ioctl function

ioctl 是设备驱动程序中对设备的IO通道进行管理的函数。
不同的设备驱动都会定义自己的ioctl指令。
后边讲到套接字的时候再具体详述。

再详细点的讲述可以看这里:




参考:




相关文章
|
10月前
|
Unix Linux C语言
计算机操作系统实验一 Unix/Linux编程开发环境
计算机操作系统实验一 Unix/Linux编程开发环境
96 0
|
6月前
|
Unix Shell Python
unix高级编程-fork和execve
unix高级编程-fork和execve
27 0
|
6月前
|
Ubuntu Unix Shell
unix高级编程-fork之后父子进程共享文件
unix高级编程-fork之后父子进程共享文件
34 0
|
6月前
|
Unix Linux
unix高级编程-僵尸进程和孤儿进程
unix高级编程-僵尸进程和孤儿进程
38 0
|
12月前
|
Unix Linux Shell
Unix/Linux环境使用(基础篇)(五)
Unix/Linux环境使用(基础篇)(五)
|
12月前
|
网络协议 安全 Ubuntu
Unix/Linux环境使用(基础篇)(四)
Unix/Linux环境使用(基础篇)(四)
|
12月前
|
Ubuntu Unix Linux
Unix/Linux环境使用(基础篇)(三)
Unix/Linux环境使用(基础篇)(三)
|
12月前
|
安全 Unix Linux
Unix/Linux环境使用(基础篇)(二)
Unix/Linux环境使用(基础篇)(二)
|
12月前
|
Ubuntu 安全 NoSQL
Unix/Linux环境使用(基础篇)(一)
Unix/Linux环境使用(基础篇)(一)
|
存储 Unix Shell
UNIX读书笔记---UNIX基础知识
UNIX读书笔记---UNIX基础知识

相关实验场景

更多