(1)如果数据并不存在,则读文件可能会使调用者永远阻塞(例如读管道、终端设备和网络设备)。
(2)如果数据不能立即被接受,则写这些同样的文件也会使调用者永远阻塞;
(3)在某些条件发生之前,打开文件会被阻塞(例如以只写方式打开一个FIFO,那么在没有其他进程已用读方式打开该FIFO时);
(4)对已经加上强制性锁的文件进行读、写;
(5)某些ioctl操作;
(6)某些进程间通信函数;
非阻塞I/O调用open、read和write等I/O操作函数使上述的慢速系统调用在不能立即完成的情况下,立即出错返回。
(1)如果是调用open以获得该描述符,则可指定O_NONBLOCK标志;
(2)对于已经打开的一个描述符,则可调用fcntl打开O_NONBLOCK文件状态标志(注意:设置文件状态标志的方法)。
功能:当第一个进程正在读或修改文件的某个部分时,使用记录锁可以阻止其他进程修改同一文件区。记录锁锁定的是文件中的一个区域(也可能是整个文件)。
#include
int fcntl(int fd, int cmd, .../*struct flock *flockptr*/)
struct flock
{
short int l_type; /* Type of lock: F_RDLCK, F_WRLCK, or F_UNLCK. */
short int l_whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_start; /* offset in bytes, relative to l_whence */
off_t l_len; /* length in bytes; 0 means lock to EOF. */
pid_t l_pid; /* Process holding the lock. */
};
#include "apue.h"
#include
pid_t
lock_test(int fd, int type, off_t offset, int whence, off_t len)
{
struct flock lock;
lock.l_type = type; /* F_RDLCK or F_WRLCK */
lock.l_start = offset; /* byte offset, relative to l_whence */
lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
lock.l_len = len; /* #bytes (0 means to EOF) */
if (fcntl(fd, F_GETLK, &lock) < 0)
err_sys("fcntl error");
if (lock.l_type == F_UNLCK)
return(0); /* false, region isn't locked by another proc */
return(lock.l_pid); /* true, return pid of lock owner */
}
进程不能用lock_test来测试自己是否在文件的某一部分持有一把锁,F_GETLK命令的定义说明,返回信息指示是否有现有的锁阻止调用进程设置它自己的锁。因为 F_SETLK 和 F_SETLKW 总是替换调用进程现有的锁(若已存在),所以调用进程决不会阻塞在自己持有的锁上,所以F_GETLK 命令决不会报告调用进程自己持有的锁。
(1)锁与进程和文件相关联:当一个进程终止时,它所建立的锁全部释放。当文件描述符关闭时,则该文件描述符上由某个进程设置的锁也会释放
(2)由fork产生的子进程不继承父进程所设置的锁
(3)在执行exec后,新程序可以继承原执行程序的锁,但如果一个文件描述符设置了执行时关闭标志,那么当作为exec的一部分关闭该文件描述符时,将释放响应文件的所有锁
概述:将我们感兴趣的描述符列表传给一个函数,该函数直到这些描述符中的一个已经准备好I/O时才返回。
select 和 poll 函数基本用法点击这里
概述:利用这种技术,进程告诉内核,当描述符准备好进行I/O时,用一个信号通知它。
struct aiocb
{
int aio_fildes; /* file desriptor */
off_t aio_offset; /* file offset for I/O */
volatile void *aio_buf; /* buffers for I/O */
size_t aio_nbytes; /* numbers of bytes to transfer */
int aio_reqprio; /* priority */
struct sigevent aio_sigevent; /* signal information*/
int aio_lio_opcode; /* operation for list I/O*/
sigevent 事件
struct sigevent {
int sigev_notify; /* notify type*/
int sigev_signo; /* signal number */
union sigval sigev_value; /* notify argument */
void (*sigev_notrify_function)(union sigval); /* notify function */
pthread_attr_t *sigev_notify_attributes; /* notify attrs */
};
#include
ssize_t readv(int filedes,const struct iovec *iov,int iovcnt);
ssize_t writev(int filedes,const struct iovec *iov,int iovcnt);
参数:filedes 文件描述符
iov 指向iovec结构数组的一个指针。
iovcnt 数组元素的个数
返回值:若成功则返回已读、写的字节数,若出错则返回-1
struct iovec {
void *iov_base; /* 起始地址 */
size_t iov_len; /* 需要传输的字节数 */
};
readv() 系统调用从文件描述符 fd 关联的文件里读取数据到 iovcnt 个由 iov 结果描述的缓存区里。(分散读)
writev() 系统调用把 iovcnt 个由 iov 结构描述的缓存区数据写入文件描述符 fd 关联的文件里。(聚合写)
#include
void *mmap (void *addr, size_t len, int prot, int flag, int fd, __off_t off);
addr:指定映射区的起始地址,0表示由系统选择,该函数返回该映射区的起始地址
fd:被映射文件的描述符
len:映射的字节数
off:映射字节在文件中的起始偏移量
prot:映射存储区的保护请求,对指定映射区的保护请求不能超过文件open模式访问权限
prot | 说明 |
---|---|
PORT_READ | 可读 |
PORT_WRITE | 可写 |
PORT_EXEC | 可执行 |
PORT_NONE | 不可访问 |
flag:影响映射存储区的属性
(1)MAP_SHARED:存储操作会修改映射的文件
(2)MAP_PRIVATE:对映射区的存储操作会创建该映射文件的一私有副本,不会影响原文件
#include
int mprotect(void *addr, size_t len, int prot);
地址参数addr必须是系统页长的整数倍
如果修改的页时通过MAP_SHARED标志映射到地址空间的,那么修改不会立即写会到文件中,何时写会由内核的守护进程决定,如果只修改了一页中的一个字节,整个页都会被写会
可以调用msync将该页冲洗到被映射的文件中
#include
int msync(void *addr, size_t len, int flags);
进程终止时,会自动解除存储映射区的映射,直接调用munman函数也可以解除映射区,
关闭映射存储区时使用的文件描述符并不解除映射区
#include
int mumap(void *addr, size_t len);
调用munmap并不会使映射区的内容写会到磁盘上
#include
#include
#include
#include
#include
#include
#include
#include
#include
void sys_err(const char *str)
{
perror(str);
exit(1);
}
int main(int argc, char *argv[])
{
int N = 5; //默认为5个
if (argc < 3 || argc > 4)
sys_err("please enter like this: ./a.out file_src file_dst [process_number]");
if (argc == 4)
N = atoi(argv[3]); //获取输入的进程个数
int fd_src = open(argv[1], O_RDONLY);
if (fd_src < 0)
sys_err("open source file error");
int fd_dst = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0644);
if (fd_dst < 0)
sys_err("open dst file error");
struct stat sbuf;
int ret = fstat(fd_src, &sbuf);
if (ret < 0)
sys_err("read file information error");
int flen = sbuf.st_size;
if (flen < N)
N = flen;
ret = ftruncate(fd_dst, flen);
if (ret < 0)
sys_err("ftruncate error");
char *mp_src = (char *)mmap(NULL, flen, PROT_READ, MAP_SHARED, fd_src, 0);
if (mp_src == MAP_FAILED)
perror("mmap error");
close(fd_src);
char *mp_dst = (char *)mmap(NULL, flen, PROT_READ | PROT_WRITE, MAP_SHARED, fd_dst, 0);
if (mp_dst == MAP_FAILED)
perror("mmap error");
close(fd_dst);
int num = flen / N;
int remainder = flen % num; //均分后剩下的部分
pid_t pid;
int i;
for(i = 0; i < N; ++i) {
printf("create %dth proc\n",i);
pid = fork();
if (pid == 0)
break;
}
if (i == N) {
for(int j = 0; j < N; ++j)
wait(NULL);
}
else if (i == N-1) {
memcpy(mp_dst + i*num, mp_src + i*num, num + remainder);
}
else {
memcpy(mp_dst + i*num, mp_src + i*num, num);
}
munmap(mp_src, flen);
munmap(mp_dst, flen);
return 0;
}