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!\n");
- 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\n");
- }
- else{
- printf("fcntl=%d\n",fcntl(fd,F_SETFL,FNDELAY));
- }
- }
- else
- {
- if(fcntl(fd,F_SETFL,0) < 0){ //阻塞,即使前面在open串口設備時設置的是非阻塞的,這里設為阻塞后,以此為准
- printf("fcntl failed\n");
- }
- else{
- printf("fcntl=%d\n",fcntl(fd,F_SETFL,0));
- }
- }
- if(isatty(STDIN_FILENO) == 0){
- printf("standard input is not a terminal device\n");
- }
- else{
- printf("isatty sucess!\n");
- }
- printf("fd-open=%d\n",fd);
- return fd;
- }
所以,linux的串口的阻塞性通過fcntl()函數進行設置即可。
- 阻塞:fcntl(fd,F_SETFL,0)
- 非阻塞:fcntl(fd,F_SETFL,FNDELAY