文件锁和IO多路复用

文件锁和IO多路复用

一直没用过fcntl/select/poll/epoll,今天便花了点时间看看,主要简短的记录几个例子。

1.fcntl

#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
fd:文件描述符
lock:结构为flock,设置记录锁的具体状态
struct flock
{
	short l_type;//F_RDLCK/F_WRLCK/F_UNLCK 读取锁/写入锁/解锁
	off_t l_start;//类似于fseek里面的相对偏移量
	short l_whence;//类似于fseek里面的偏移起点
	off_t l_len;//加锁区域的长度
	pid_t l_pid;//给当前区域加锁的进程ID
};
F_DUPFD:复制文件描述符
F_GETFD:获得fd的close-on-exec标志,若标志未设置,则文件经过exec()之后,仍然保持打开状态
F_SETFD:设置close-on-exec标志,由参数arg的FD_CLOEXEC位决定
F_GETFL:得到open设置的标志
F_SETFL:改变open设置的标志
F_GETLK:应该是获取lock状态的
F_SETLK:设置lock状态
F_SETLKW:阻塞设置lock状态
成功,返回0,-1则为出错

lock_set.c

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
int lock_set(int fd,int type)
{
	struct flock old_lock,lock;
	lock.l_whence = SEEK_SET;
	lock.l_start = 0;
	lock.l_len = 0;
	lock.l_type = type;
	lock.l_pid = -1;
	fcntl(fd,F_GETLK,&lock);
	if(lock.l_type!=F_UNLCK)
	{
		if(lock.l_type==F_RDLCK)
		{
			printf("read lock already set by %d\n",lock.l_pid);
		}
		else if(lock.l_type==F_WRLCK)
		{
			printf("write lock already set by %d\n",lock.l_pid);
		}
	}
	lock.l_type=type;
	if((fcntl(fd,F_SETLKW,&lock))<0)
	{
		printf("lock failed:type = %d\n",lock.l_type);
		return 1;
	}
	switch(lock.l_type)
	{
		case F_RDLCK:
		{
			printf("read lock set by %d\n",getpid());
		}
		break;
		case F_WRLCK:
		{
			printf("write lock set by %d\n",getpid());
			break;
		}
		case F_UNLCK:
		{
			printf("release lock by %d\n",getpid());
		}
		break;
	}
	return 0;
}

write.c

#include<sys/file.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>

int main()
{
	int fd;
	fd = open("hello",O_RDWR|O_CREAT,0644);
	if(fd<0)
	{
		printf("open file error\n");
		exit(1);
	}
	lock_set(fd,F_WRLCK);
	getchar();
	lock_set(fd,F_UNLCK);
	getchar();
	close(fd);
	exit(0);
}

运行两个终端,就可以发现

image-20220604192349819

image-20220604192400052

第二个终端想加写锁,就会被阻塞住。

等第一个终端解锁以后,第二个终端才会加锁成功。

image-20220604192454917

稍微改改上面的程序,编程设置加读取锁,就能发现写锁在的时候,加读取锁被阻塞。

只是很奇怪,为什么只能阻塞C程序打开文件这样的。我用vim还是能正常保存修改。

select

image-20220604193112175

image-20220604193119855

3.poll

image-20220604193401083

#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#include<errno.h>
#include<poll.h>
#define MAX_BUFFER_SIZE 1024
#define IN_FILES 3
#define TIME_DELAY 60000
#define MAX(a,b) ((a>b)?(a):(b))

int main()
{
	struct pollfd fds[IN_FILES];
	char buf[MAX_BUFFER_SIZE];
	int i,res,real_read,maxfd;
	fds[0].fd = 0;
	if((fds[1].fd = open("in1",O_RDONLY|O_NONBLOCK))<0)
	{
		printf("open in1 error!\n");
		return 1;
	}
	if((fds[2].fd = open("in2",O_RDONLY|O_NONBLOCK))<0)
	{
		printf("open in2 error!\n");
		return 1;
	}
	for(i=0;i<IN_FILES;i++)
	{
		fds[i].events = POLLIN;
	}
	while(fds[0].events||fds[1].events||fds[2].events)
	{
		if(poll(fds,IN_FILES,0)<0)
		{
			printf("poll error or time out\n");
			return 1;
		}
		for(i=0;i<IN_FILES;i++)
		{
			if(fds[i].revents)
			{
				memset(buf,0,MAX_BUFFER_SIZE);
			real_read = read(fds[i].fd,buf,MAX_BUFFER_SIZE);
			if(real_read<0)
			{
				if(errno!=EAGAIN)
				{
					return 1;
				}
			}
			else if(!real_read)
			{
				close(fds[i].fd);
				fds[i].events = 0;
			}
			else
			{
				if(i==0)
				{
					if((buf[0]=='q')||(buf[0]=='Q'))
					{
						return 1;
					}
				}
				else
				{
					buf[real_read] = '\0';
					printf("%s",buf);
				}
			}
			}
		}
	}
}

这个主要是

利用管道把2个终端的输出传到了第执行主程序的终端。

首先在第一个终端创建管道。

image-20220604193612622

然后在第二、第三个终端分别重定向输出。

image-20220604193657004

image-20220604193706173

然后第一个终端执行主程序,在其余两个终端进行输入即可。

image-20220604193745021

也就是在其他终端的输入,会输出到终端1的控制台。

4.epoll

参考以下博客就好了

https://blog.csdn.net/weixin_34111790/article/details/89601839

https://blog.csdn.net/wteruiycbqqvwt/article/details/90299610

#include <sys/socket.h>  
#include <sys/epoll.h>  
#include <fcntl.h>  
#include <unistd.h>  
#include <stdio.h>  
#include <errno.h>  
#include <string.h>
int main()
{
    printf("first\n");
    int epfd = epoll_create(1);
    printf("second\n");
    int fd;
    fd = open("hello",O_RDWR|O_CREAT,0644);
    if(fd<0)
    {
        printf("open file error\n");
        exit(1);
    }
    struct epoll_event event1,event2;
    event1.events = EPOLLOUT|EPOLLIN;
    event1.data.fd = fd;
    printf("before Error no.%d:\n", errno);
    int result = epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&event1);
    printf("result = %d\n",result);
    printf("after Error no.%d: %s\n", errno, strerror(errno));
    while(1)
    {
        printf("before wait\n");
        int num = epoll_wait(epfd,&event2,1,-1);
        printf("num = %d\n",num);
        printf("fd = %d\n",event2.data.fd);
        if((event2.events&EPOLLOUT)||(event2.events&EPOLLIN))
        {
            char data[] = "this is my add";
            fwrite(data,1,strlen(data),fd);
            break;
        }
    }
    printf("finish\n");
    close(fd);
    close(epfd);
}

image-20220604204321348

本来想着利用读写锁阻塞文件,等释放后再用epoll_wait来写文件的。

查了查,epoll不支持对普通文件的读写。

好吧,那就这样了,只是稍微熟悉一下用法即可。

posted @ 2022-06-04 20:47  念秋  阅读(45)  评论(0编辑  收藏  举报