高级IO函数

1. 高级 I/O 函数

1.1 pipe

#include <unistd.h>
/*
成功返回0,失败返回-1设置error
*/
int pipe( int fd[2] );

(1)fd[0] 只能从管道读,fd[1] 只能写,默认情况下这一对文件描述符都是阻塞的(读空管道 或 写满管道 会被阻塞);

(2)写端 fd[1] 引用计数减为 0,即没有进程往管道写入数据,对 fd[0] 的 read 操作将返回 0(EOF);同理,读端 fd[0] 引用计数减为 0,对 fd[1] 的 write 操作将失败,并引发 SIGPIPE 信号;

(3)管道大小默认为 65536 字节,可以使用 fcntl 函数修改;

创建双向管道;

#include <sys/types.h>
#include <sys/socket.h>
/*
成功返回0,失败返回-1设置error
*/
int socketpair( int domain, int type, int protocol, int fd[2] );

domain 只能设置为 AF_UNIX 的协议族,即 PF_UNIX;

1.2 dup 和 dup2

创建新的文件描述符,与 file_descriptor 指向相同的文件、管道或网络连接;

#include <unistd.h>
/*
失败返回-1设置error
*/
int dup( int file_descriptor );
int dup2( int file_descriptor_one, int file_descriptor_two );

dup返回的文件描述符总是取当前系统可用的最小整数值(一般方式是关闭标准输入或输出,其返回的就是之前关闭标准输入或输出的值),dup2返回第一个不小于 file_descriptor_two 的整数值;

注意:dupdup2 创建的新文件描述符不会继承原本文件描述符的属性;

1.3 readv 和 writev

不同内存位置上的数据,不需要拼接到一块再进行写入

#include <sys/uio.h>
/*
成功时返回读出/写入 fd 的字节数
失败返回-1设置error
*/
ssize_t readv( int fd, const struct iovec* vector, int count );
ssize_t writev( int fd, const struct iovec* vector, int count );

count 是 vector 数组的长度

1.4 sendfile

在两个文件描述符之间直接传递数据(完全在内核中操作),称为零拷贝(没有内核缓冲区和用户缓冲区之间的数据拷贝)

#include <sys/sendfile.h>
/*
成功返回传输的字节数
失败返回-1设置error
*/
ssize_t sendfile( int out_fd, int in_fd, off_t* offset, size_t count );

offset 指定从读入文件流的哪个位置开始读,如果为空,使用默认的起始位置;
count 指定在文件描述符 in_fd 和 out_fd 之间传输的字节数;

注意:in_fd 必须指向真实的文件(不能是管道和 socket),即必须支持 mmap 函数;out_fd必须是一个 socket;

1.5 mmap 和 munmap

mmap 申请一段内存空间,可作为进程间通信的共享内存,也可将文件直接映射到其中;
munmap 释放这段内存;

#include <sys/mman.h>
/*
成功返回指向目标内存的指针
失败返回 MAP_FAILED((void*)-1)并设置errno
*/
void* mmap( void *start, size_t length, int prot, int flags, int fd,
			off_t offset );
/*
成功返回 0
失败返回-1设置error
*/
int munmap( void *start, size_t length );

start 允许用户指定某个特定地址作为这段内存的起始地址,如果被设置为 NULL,系统自动分配;
length 指明需要内存的长度;
prot 设置访问权限,取值为 PROT_READ(可读)、PROT_WRITE(可写)、PROT_EXEC(可执行)、PROT_NONE(不能被访问) 这几个值的按位或;

flags 控制内存段内容被修改后程序的行为,是下表某些值的按位或(MAP_SHARED 和 MAP_PRIVATE 是互斥的);
在这里插入图片描述
fd 是被映射文件的文件描述符,一般通过 open获得;
offset 设置从文件的何处开始映射;

1.6 splice

用于在两个文件描述符之间移动数据,也是零拷贝;

#include <fcntl.h>
/*
成功返回移动字节的数量,可能返回0
失败返回 -1 并设置errno
*/
ssize_t splice( int fd_in, loff_t* off_in, int fd_out, loff_t* off_out, 
				size_t len, unsigned int flags );

(1)fd_in 是待输入数据的文件描述符;如果其为管道,off_in 必须为 NULL;否则,off_in 表示从输入数据流何处开始读数据,此时 off_in 若被设置为 NULL,表示从当前偏移位置读入;

fd_out / off_out 与之类似,用于输出数据流;

fd_in 和 fd_out 必须至少有一个是管道;

len 指定移动数据的长度

(2)flags 控制数据如何移动;被设置为下表某些值的按位或;
在这里插入图片描述
在这里插入图片描述

1.7 tee

两个管道之间复制数据,也是零拷贝操作,不消耗数据,管道上的数据仍可用于后续的读操作;

#include <fcntl.h>
/*
成功返回在两个fd之间复制的数量,可能返回0
失败返回 -1 并设置errno
*/
ssize_t tee( int fd_in, int fd_out, size_t len, unsigned int flags );

fd_in 和 fd_out 必须都是管道;flags 与 splice 相同

1.8 fcntl(file control)

#include <fcntl.h>
/*
失败返回 -1 并设置errno
*/
int fcntl( int fd, int cmd, ... );

cmd 指定执行何种类型的操作;常用操作和参数如下所示;
在这里插入图片描述
SIGIO 和 SIGURG 必须与某个文件描述符关联后才能使用,关联的方法为:使用 fcntl 为文件描述符指定宿主进程或进程组。宿主进程或进程组将捕获这两个信号;
被关联的文件描述符可读或可写时,系统触发 SIGIO 信号;其上有带外数据可读时,触发 SIGURG 信号;
使用 SIGIO 时,还需要利用 fcntl设置其 O_ASYNC 标志;
在这里插入图片描述
非阻塞 fd 会立即返回 -1,errno 设置为 EAGAIN 或 EWOULDBLOCK 表示数据被读取完毕;

2. Linux 服务器程序规范

2.1 日志

2.1.1 Linux 系统日志

(1)Linux 提供一个守护进程来处理系统日志 —— rsyslogd;

其能接收用户日志和内核日志;

用户进程调用 syslog生成系统日志,将日志输出到 /dev/log 中(AF_UNIX 类型),rsyslogd 监听该文件获得用户进程输出;

内核日志由 printk等打印到内核的环状缓冲(ring buffer)中,该缓冲直接映射到 /proc/kmsg 文件中,rsyslogd 读取该文件获得内核日志;

(2)输出,默认情况下,调试信息会保持到 /var/log/debug 中,普通信息到 /var/log/messages,内核消息到 /var/log/kern.log;

rsyslogd 的主配置文件是 /etc/rsyslog.conf,可以配置内核日志输入路径、包含哪些子配置文件等;

2.1.1 syslog 函数

(1)

#include <syslog.h>
void syslog( int priority, const char* message, ... );

priority 是设施值(默认为 LOG_USER)和日志级别的按位或;
日志级别如下所示:

#define LOG_EMEGE		0	/* 系统不可用 */
#define LOG_ALERT		1	/* 报警,需要立即采取动作 */
#define LOG_CRIT		2	/*非常严重情况 */
#define LOG_ERR			3	/* 错误 */
#define LOG_WARNING		4	/* 警告 */
#define LOG_NOTICE		5	/* 通知 */		
#define LOG_INFO		6	/* 信息 */
#define LOG_DEBUG		7	/* 调试 */

(2)改变 syslog 的默认输出方式,进一步结构化日志内容

void openlog( const char* ident, int logopt, int facility );

ident 指定的字符串将被添加到日志消息的日期和时间之后,通常被设定为程序的名字;
logopt 对后续的 syslog 调用的行为进行配置,可取下列值的按位或:

#define LOG_PID			0x01	/* 日志信息中包含程序PID */
#define LOG_CONS		0x02	/* 如果消息不能记录到日志文件,则打印到终端 */
#define LOG_ODELAY		0x04	/* 延迟打开日志功能直到第一次调用 syslog  */
#define LOG_NDELAY		0x08	/* 不延迟打开日志功能 */

facility 可用来修改 syslog 中的默认设施值;

(3)设置 syslog 日志掩码,日志级别大于日志掩码的日志信息被系统忽略;

/* 返回先前的日志掩码值 */
int setlogmask( int maskpri );

(4)关闭日志功能

void closelog();

2.2 用户信息

2.2.1 UID、EUID、GID 和 EGID
#include <sys/types.h>
#include <unistd.h>
uid_t getuid();				/* 获取真实用户ID */
uid_t geteuid();			/* 获取有效用户ID */
gid_t getgid();				/* 获取真实组ID */
gid_t getegid();			/* 获取有效组ID */
int setuid( uid_t uid );
int seteuid( uid_t uid );
int setgid( gid_t gid );
int setegid( gid_t gid );

一个进程拥有两个用户 ID:UID 和 EUID;EUID 存在的目的是方便资源访问:使得运行程序的用户拥有该程序的有效用户的权限,比如 su;

文件如果由 set-user-id(s) 标志,运行该文件时,其有效用户就是该程序的所有者;

root 的 UID 为 0;有效用户为 root 的进程称为特权进程;

2.2.2 切换用户

从 root 身份启动进程切换到普通用户身份运行,调用 setgidsetgid 实现;

2.3 进程间关系

2.3.1 进程组

每个进程都隶属于一个进程组,除了 PID 外,还有 PGID;

#include <unistd.h>
/*
成功返回进程 PID 所属进程组的 PGID
失败返回-1并设置errno
*/
pid_t getpgid( pid_t pid );
/*
成功返回 0
失败返回-1并设置errno
*/
int setpgid( pid_t pid, pid_t pgid );

(1)每个进程组都有一个首领进程,其 PID 和 PGID 相同;进程组将一直存在,直到其中所有进程都退出或加入其他进程组;

(2)setpgid 中 pid 为 0,表示设置当前进程的 PGID 为 pgid;如果 pgid 为 0,则使用 pid 作为 PGID;

(3)一个进程只能设置自己或其子进程的 PGID,当子进程调用 exec系列函数后,不能在父进程对它设置 PGID;

2.3.2 会话

有关联的进程组将形成一个会话,下面函数用于创建一个会话;

#include <unistd.h>
/*
成功返回新的进程组的 PGID
失败返回-1并设置errno
*/
pid_t setsid( void );
pid_t getsid( pid_t pid );

不能由进程组的首领进程创建会话,会产生错误;对非首领进程,调用 setsid,会使得调用进程成为会话的首领,会新建一个进程组,调用进程成为该组首领,调用进程将甩开终端;

会话ID:会话首领所在的进程组的 PGID;

2.4 改变工作目录和根目录

#include <unistd.h>
/*获取和改变进程工作目录*/
char* getcwd( char* buf, size_t size );
int chdir( const char* path );
/*改变进程根目录*/
int chroot( const char* path );

(1)buf 指向的内存用于存储进程当前工作目录的绝对路径,其大小由 size 指定;

如果当前工作目录的绝对路径长度(加上最后一个’\0’)大于 size,返回 NULL,并设置 errno 为 ERANGE;

(2)chdirchroot 成功返回 0,失败返回 -1 并设置 error;

chroot不改变进程当前的工作目录,调用其之后,仍需要使用 chdir(”/“)来将工作目录切换至新的根目录,只有特权进程才能改变根目录;

2.5 进程后台化

#include <unistd.h>
/*成功返回 0, 失败返回-1并设置errno*/
int daemon( int nochdir, int noclose );

nochdir 为 0,工作目录被设置为 “/”,否则为当前工作目录;
noclose 为 0 时,标准输入、输出、错误输出都被重定向到 /dev/null 文件,否则,继续用原先设备;

参考

《Linux 高性能服务器》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值