解决方式:1.互斥锁 2.读写锁
进程的同步:记录锁
多个进程对同一个文件操作
int fcntl(int fd, int cmd, ... /* arg */ );
通过文件描述符根据cmd选项对文件进行操作
cmd:
F_DUPFD : 复制一个现有的文件描述符
F_SETLKW : 设置记录锁
struct flock {
...
short l_type; /* Type of lock:
F_RDLCK, 读锁
F_WRLCK, 写锁
F_UNLCK 解锁
*/
short l_whence; /* How to interpret l_start:
SEEK_SET,
SEEK_CUR,
SEEK_END */
off_t l_start; /* Starting offset for lock :一般给0,由系统自动判断*/
off_t l_len; /* Number of bytes to :一般给0,由系统自动判断*/
...
};
====================================================================================
阻塞: 一旦数据1没有到达,被阻塞了,即使数据2和数据3到达也不会被处理
if(数据1到达) ===>一直阻塞在这里
{
处理数据1;
}
else if(数据2到达)
{
处理数据2;
}
else if(数据3到达)
{
处理数据3;
}
非阻塞:数据1没有到达,被阻塞了,在规定的时间内轮询,如若超过规定的时间,则退出,让
其他数据执行,所以数据2和数据3到达会被处理,不会阻塞[时间片]
if(数据1到达) ===>一直阻塞在这里
{
处理数据1;
}
else if(数据2到达)
{
处理数据2;
}
else if(数据3到达)
{
处理数据3;
}
注意:
1.一般涉及到终端或者网络需要解决阻塞问题
2.普通文件不需要解决阻塞问题【系统已经处理了】
_________________________________________________________________________________________
1.在同一个进程中操作一个描述符,不涉及阻塞问题
2.在同一个进程中操作两个或者以上的文件描述符,需要解决阻塞问题
怎么解决?
1).在同一个进程中,都以非阻塞的方式打开
在规定的时间内不停的轮询==》浪费cpu资源
-----------------------------------------------------------------------------------------------
F_GETFL (void)
Read the file status flags; arg is ignored.
F_SETFL (long)
Set the file status flags to the value specified by arg. File access mode (O_RDONLY,
O_WRONLY, O_RDWR) and file creation flags
(i.e., O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC) in arg are ignored. On Linux this
command can only change the O_APPEND, O_ASYNC,
O_DIRECT, O_NOATIME, and O_NONBLOCK flags.
-----------------------------------------------------------------------------------------------
2).在一个进程中,开辟多个线程【一读终端,一个读鼠标】
:需要解决线程间的同步问题--》使得程序变得复杂
3).fork一个新的进程,一个进程处理一个文件描述符
:使得程序更加复杂
4.一种比较好的方式,设置异步方式
:不是所有的系统都支持异步.......
______________________________________________________
5.比较好的,常用的方式:I/O多路复用
a.select机制【轮询】
:把所有关心的文件描述符都加入到一个集合中,让内核为我们判断,当描述符准备好了[不阻塞]
返回准备好的文件描述符给我们[程序员],之后我们操作的文件描述符就不会发送阻塞
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
nfds : 最大的文件描述符+1
fd_set: 类型
typedef struct {
unsigned long fds_bits [__FDSET_LONGS];
} __kernel_fd_set;
readfds : 所关心的读的集合
writefds: 所关心的写的集合 [一般不关心]
execptfds:所关心的异常的集合 [一般不关心]
timeout : 你愿意等待多少时间
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
NULL : 永远等待
0 : 不等待
!= 0 : 等待指定的时间
返回值:
成功: > 0 返回准好的个数
超时: = 0
失败: -1
______________________________________________________
void FD_CLR(int fd, fd_set *set);
把文件描述符fd从集合set中删除
____________________________________________________
int FD_ISSET(int fd, fd_set *set);
判断文件描述符fd是否在集合set中
void FD_SET(int fd, fd_set *set);
把文件描述符fd加入到集合set中
void FD_ZERO(fd_set *set);
清空集合set
select缺点:
1.每次都需要把描述符重新加入到集合中
2.每次都要重新设置等待时间
[==========================================================================]
b.poll机制
内部实现和select机制一样,但是比select更为高效,poll机制没有采用低效的三位[读集合,写集合,异常集合]
而是把所有关心的描述符都加入到一个结构体数组struct pollfd中,由内核判断,返回准备就绪的描述符
____________________________________________________________
select缺点:
1.每次都需要把描述符重新加入到集合中
2.每次都要重新设置等待时间
poll : 文件描述符只需要加入一次
_____________________________________________________________
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd {
int fd;//文件描述符
short events;//告诉内核我[程序员]要做什么事情
short revents;//内核返回给我,它到底做了什么事情
};
nfds : 加入到数组中的 文件描述符的个数
timeout: 表示愿意等待多少时间
-1 : 永远等待
0 : 不等待
>0 : 等待给定的时间
返回值:
> 0 : 表示至少有一个文件描述符准备就绪
= 0 : 超时
< 0 : 异常返回
#define POLLIN 0x001 /* There is data to read. */
b.poll机制