fcntl系统调用可以用来对已打开的文件描述符进行各种控制操作以改变已打开文件的的各种属性
- #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);
#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);
fcntl函数功能依据cmd的值的不同而不同。参数对应功能如下:
(1)F_DUPFD
与dup函数功能一样,复制由fd指向的文件描述符,调用成功后返回新的文件描述符,与旧的文件描述符共同指向同一个文件。
(2)F_GETFD
读取文件描述符close-on-exec标志
(3)F_SETFD
将文件描述符close-on-exec标志设置为第三个参数arg的最后一位
(4)F_GETFL
获取文件打开方式的标志,标志值含义与open调用一致
(5)F_SETF
设置文件打开方式为arg指定方式
文件记录锁是fcntl函数的主要功能。
记录锁:实现只锁文件的某个部分,并且可以灵活的选择是阻塞方式还是立刻返回方式
当fcntl用于管理文件记录锁的操作时,第三个参数指向一个struct flock *lock的结构体
- struct flock
- {
- short_l_type; /*锁的类型*/
- short_l_whence; /*偏移量的起始位置:SEEK_SET,SEEK_CUR,SEEK_END*/
- off_t_l_start; /*加锁的起始偏移*/
- off_t_l_len; /*上锁字节*/
- pid_t_l_pid; /*锁的属主进程ID */
- };
struct flock { short_l_type; /*锁的类型*/ short_l_whence; /*偏移量的起始位置:SEEK_SET,SEEK_CUR,SEEK_END*/ off_t_l_start; /*加锁的起始偏移*/ off_t_l_len; /*上锁字节*/ pid_t_l_pid; /*锁的属主进程ID */ };
short_l_type用来指定设置共享锁(F_RDLCK,读锁)还是互斥锁(F_WDLCK,写锁).
当short_l_type的值为F_UNLCK时,传入函数中将解锁。
每个进程可以在该字节区域上设置不同的读锁。
但给定的字节上只能设置一把写锁,并且写锁存在就不能再设其他任何锁,且该写锁只能被一个进程单独使用。
这是多个进程的情况。
单个进程时,文件的一个区域上只能有一把锁,若该区域已经存在一个锁,再在该区域设置锁时,新锁会覆盖掉旧的锁,无论是写锁还时读锁。
l_whence,l_start,l_len三个变量来确定给文件上锁的区域。
l_whence确定文件内部的位置指针从哪开始,l_star确定从l_whence开始的位置的偏移量,两个变量一起确定了文件内的位置指针先所指的位置,即开始上锁的位置,然后l_len的字节数就确定了上锁的区域。
特殊的,当l_len的值为0时,则表示锁的区域从起点开始直至最大的可能位置,就是从l_whence和l_start两个变量确定的开始位置开始上锁,将开始以后的所有区域都上锁。
为了锁整个文件,我们会把l_whence,l_start,l_len都设为0。
(6)F_SETLK
此时fcntl函数用来设置或释放锁。当short_l_type为F_RDLCK为读锁,F_WDLCK为写锁,F_UNLCK为解锁。
如果锁被其他进程占用,则返回-1;
这种情况设的锁遇到锁被其他进程占用时,会立刻停止进程。
(7)F_SETLKW
此时也是给文件上锁,不同于F_SETLK的是,该上锁是阻塞方式。当希望设置的锁因为其他锁而被阻止设置时,该命令会等待相冲突的锁被释放。
(8)F_GETLK
第3个参数lock指向一个希望设置的锁的属性结构,如果锁能被设置,该命令并不真的设置锁,而是只修改lock的l_type为F_UNLCK,然后返回该结构体。如果存在一个或多个锁与希望设置的锁相互冲突,则fcntl返回其中的一个锁的flock结构。
====================================================================================================================================================
linux下串口的阻塞和非阻塞操作
有两个可以进行控制串口阻塞性(同时控制read和write):一个是在打开串口的时候,open函数是否带O_NDELAY;第二个是可以在打开串口之后通过fcntl()函数进行控制。
阻塞的定义:
对于read,block指当串口输入缓冲区没有数据的时候,read函数将会阻塞在这里,移植到串口输入缓冲区中有数据可读取,read读到了需要的字节数之后,返回值为读到的字节数;
对于write,block指当串口输出缓冲区满,或剩下的空间小于将要写入的字节数,则write将阻塞,一直到串口输出缓冲区中剩下的空间大于等于将要写入的字节数,执行写入操作,返回写入的字节数。
非阻塞的定义:
对于read,no block指当串口输入缓冲区没有数据的时候,read函数立即返回,返回值为0。
对于write,no block指当串口输出缓冲区满,或剩下的空间小于将要写入的字节数,则write将进行写操作,写入当前串口输出缓冲区剩下空间允许的字节数,然后返回写入的字节数。
- static int set_opt(int fd, int nSpeed, int nBits, char nEvent, int nStop)
- {
- struct termios newtio;
- struct termios oldtio;
- if(tcgetattr(fd,&oldtio) != 0)
- {
- perror("SetupSerial 1");
- return -1;
- }
- bzero(&newtio,sizeof(newtio));
- newtio.c_cflag |= CLOCAL |CREAD;
- newtio.c_cflag &= ~CSIZE;
- /***********数据位选择****************/
- switch(nBits)
- {
- case 7:
- newtio.c_cflag |= CS7;
- break;
- case 8:
- newtio.c_cflag |= CS8;
- break;
- }
- /***********校验位选择****************/
- switch(nEvent)
- {
- case 'O':
- newtio.c_cflag |= PARENB;
- newtio.c_cflag |= PARODD;
- newtio.c_iflag |= (INPCK | ISTRIP);
- break;
- case 'E':
- newtio.c_iflag |= (INPCK |ISTRIP);
- newtio.c_cflag |= PARENB;
- newtio.c_cflag &= ~PARODD;
- break;
- case 'N':
- newtio.c_cflag &= ~PARENB;
- break;
- }
- /***********波特率选择****************/
- switch(nSpeed)
- {
- case 2400:
- cfsetispeed(&newtio,B2400);
- cfsetospeed(&newtio,B2400);
- break;
- case 4800:
- cfsetispeed(&newtio,B4800);
- cfsetospeed(&newtio,B4800);
- break;
- case 9600:
- cfsetispeed(&newtio,B9600);
- cfsetospeed(&newtio,B9600);
- break;
- case 57600:
- cfsetispeed(&newtio,B57600);
- cfsetospeed(&newtio,B57600);
- break;
- case 115200:
- cfsetispeed(&newtio,B115200);
- cfsetospeed(&newtio,B115200);
- break;
- case 460800:
- cfsetispeed(&newtio,B460800);
- cfsetospeed(&newtio,B460800);
- break;
- default:
- cfsetispeed(&newtio,B9600);
- cfsetospeed(&newtio,B9600);
- break;
- }
- /***********停止位选择****************/
- if(nStop == 1){
- newtio.c_cflag &= ~CSTOPB;
- }
- else if(nStop ==2){
- newtio.c_cflag |= CSTOPB;
- }
- newtio.c_cc[VTIME] = 1;
- newtio.c_cc[VMIN] = FRAME_MAXSIZE; //阻塞条件下有效
- tcflush(fd,TCIFLUSH);
- if((tcsetattr(fd,TCSANOW,&newtio)) != 0)
- {
- perror("com set error");
- return -1;
- }
- printf("set done! ");
- return 0;
- }
- static int open_port(int fd,int comport)
- {
- /***********打开串口1****************/
- if(comport == 1)
- {
- fd = open("/dev/ttyAT1",O_RDWR|O_NOCTTY|O_NDELAY);
- if(fd == -1){
- perror("Can't Open Serial Port");
- return -1;
- }
- }
- /***********打开串口2****************/
- else if(comport == 2)
- {
- fd = open("/dev/ttyAT2",O_RDWR|O_NOCTTY|O_NDELAY);
- if(fd == -1){
- perror("Can't Open Serial Port");
- return -1;
- }
- }
- /***********打开串口3****************/
- else if(comport == 3)
- {
- fd = open("/dev/ttyAT3",O_RDWR|O_NOCTTY|O_NDELAY);
- if(fd == -1){
- perror("Can't Open Serial Port");
- return -1;
- }
- }
- if(comport == 1)
- {
- if(fcntl(fd,F_SETFL,FNDELAY) < 0)//非阻塞,覆盖前面open的属性
- {
- printf("fcntl failed ");
- }
- else{
- printf("fcntl=%d ",fcntl(fd,F_SETFL,FNDELAY));
- }
- }
- else
- {
- if(fcntl(fd,F_SETFL,0) < 0){ //阻塞,即使前面在open串口设备时设置的是非阻塞的,这里设为阻塞后,以此为准
- printf("fcntl failed ");
- }
- else{
- printf("fcntl=%d ",fcntl(fd,F_SETFL,0));
- }
- }
- if(isatty(STDIN_FILENO) == 0){
- printf("standard input is not a terminal device ");
- }
- else{
- printf("isatty sucess! ");
- }
- printf("fd-open=%d ",fd);
- return fd;
- }
所以,linux的串口的阻塞性通过fcntl()函数进行设置即可。
- 阻塞:fcntl(fd,F_SETFL,0)
- 非阻塞:fcntl(fd,F_SETFL,FNDELAY