linux系统编程9:一些函数和库的使用补充

1.dup()函数

#include <unistd.h>

 int dup(int oldfd);

 dup函数的功能:从系统中寻找  最小  可用的文件描述符 作为oldfd的副本。
 新文件描述符通过dup的返回值返回。
 上述相当于有两个不同的文件描述符,指向同一个对应资源或者操作,这里两者可以独立使用,互不影响。使用 
 的计数为1.
 这不同于fork()函数,其是直接复制,两个相同的文件描述符,使用的引用计数为2。
 必须全部关闭才能真正关闭文件描述符
#include <iostream>
#include <unistd.h>
#include <string>

int main(){
   int fd=dup(1);//复制标准输出文件描述符
   std::cout<<fd<<std::endl;
   std::string m="6666";
   std::string g{"747474"};
   write(fd,m.c_str(),m.size());
   std::cout<<std::endl;
   write(1,g.c_str(),g.size());
   std::cout<<std::endl;
   /*fd,1都可以用于标注输出*/
   close(fd);
   return 0;
}

2.dup2()函数

#include <unistd.h>

 int dup2(int oldfd, int newfd);  

 dup2的功能:将newfd作为oldfd的副本。 如果newfd先存在(已经被使用)
 dup2会先close(newfd),然后将newfd作为 oldfd的副本。
 即dup2不同于dup,dup2可以指定重定向文件描述符,而不是系统分配最小的可用的文件描述符。 
#include <iostream>
#include <unistd.h>
#include <string>

int main(){
   int fd=dup2(1,5);//复制标准输出文件描述符(或者是重定向为5)
   std::cout<<fd<<std::endl;
   std::string m="6666";
   std::string g{"747474"};
   write(fd,m.c_str(),m.size());
   std::cout<<std::endl;
   write(1,g.c_str(),g.size());
   std::cout<<std::endl;
   /*fd,5都可以用于标注输出*/
   close(fd);
   return 0;
}

下面是输出结果:

3.无名管道pipe:
 

 管道(pipe)又称无名管道。 无名管道是一种特殊类型的文件,在应用层体现为 两个打开的文件描   述符(读端和写端)。

管道的特点:

1.半双工,数据在同一时刻只能在一个方向上流动。
2.数据只能从管道的一端写入,从另一端读出。
3.写入管道中的数据遵循先入先出的规则。
4.管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数据 的格式, 如  多少字节算一个消息等。
5.管道不是普通的文件,不属于某个文件系统,其只存在于内存中。
6.管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。
7.从管道读数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间以便写更多的数据。

#include <unistd.h>

int pipe(int pipe[2]);

功能:经由参数filedes返回两个文件描述符
参数: filedes为int型数组的首地址,其存放了管道的文件描述符fd[0]、fd[1]。 filedes[0]为读而打开,filedes[1]为写而打开管道,filedes[0]的输出是filedes[1]的输入。

返回值: 成功:返回 0 失败:返回-1

注意:在使用无名管道的时候 必须事先确定,谁发,谁收的问题。一旦确定不可更改。

注意:管道pipe是阻塞的,

1、默认用read函数从管道中读数据是阻塞的(无数据发生阻塞)。

2、调用write函数向管道里写数据,当缓冲区已满时write也会阻塞。

通信过程中,读端口全部关闭后,写进程向管道内写数据时,写进程会(收 到SIGPIPE信号)退出

子进程睡一秒是让CPU调度执行父进程的写入数据操作(只是确定逻辑,可以不睡)

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
 
    int fd[2];
    pipe(fd);
    pid_t pid=fork();
    if(pid==0)
    {
        //子进程负责读取父进程的消息
        close(fd[1]);
        unsigned char buf[128]="";
        sleep(1);
        read(fd[0],buf,sizeof(buf));
        printf("子进程%d读到的消息为:%s\n", getpid(), buf);
        close(fd[0]);//读完 关闭管道
    }
    if(pid>0) //父进程负责写信息
    { 
        close(fd[0]);//关闭读端
        printf("父进程%d写入数据hello pipe\n",getpid());
        write(fd[1],"hello pipe",strlen("hello pipe"));
        printf("父进程:%d完成写入\n", getpid());
        //通信完成 应该关闭写端
        close(fd[0]);
        wait(NULL);
    }
    return 0;
}

注意:若读端口全部关闭后,写进程向管道内写数据时,写进程会(收 到SIGPIPE信号)退出。

双全工管道:socketpair()函数见网络编程部分下面给出例子(数据流动是双方的)

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

int main ()
{
    int sv[2];
    int result = socketpair(PF_UNIX, SOCK_STREAM, 0, sv);//第三个参数只能为0
    if (result < 0){
        exit(1);
    }
    printf("sv[0] is : %d \n", sv[0]);   //这两个套节字句柄并不相同,但作用是一样的
    printf("sv[1] is : %d \n", sv[1]);

    if (fork()){ /* 父进程 */
        int val = 0;
        pid_t pid = getpid(); 

        close(sv[1]);    //父进程关闭sv[1]的读写权限
        while (1){          
            ++val;
            printf("%d send message: %d\n", pid, val);   
            write(sv[0], &val, sizeof(val));            //父进程向管道里写数据

           // read(sv[0], &val, sizeof(val));          //如果字进程不写数据,将会导致此处堵塞
            //printf("%d receive message: %d\n", pid, val);
            sleep(1);
        }
    }else{  /*子进程*/
        int val = 0;
        close(sv[0]); //字进程关闭sv[0]的读写权限
        pid_t pid = getpid(); 
        while(1){
            read(sv[1], &val, sizeof(val));            //字进程从管道中取数据
            printf("%d receive message: %d\n", pid, val);

        //  printf("%d receive message: %d\n", pid, val); 
        //  write(sv[1], &val, sizeof(val));
        }
    }
}

上面需要注意一点的是:父子进程,读写都是一个管道端,比如父进程读写都是sv[0],子进程读写都是sv[1]。但是跨进程交流是不同的管道端,用法和PIPE类似,比如父进程sv[0]写,子进程sv[1]读,或者返回来,子进程sv[1]写,父进程是sv[0]读,而不是一个sv[0],另一个进程sv[0]读。

4.fcntl()函数

#include <fcntl.h>
#include <unistd.h>

int fcntl(int fd,int cmd,....);
此函数可以对打开的文件描述符进行一些操作或者设置。
cmd指定了fcntl函数的行为。

对于cmd,下面主要讲述三者 :

  • 1、复制文件描述符(F_DUPFD);
  • 2、获取/设置文件状态标志(F_GETFL、F_SETFL);


1).cmd为F_DUPFD表示复制文件描述符fd。调用成功会返回新的描述符。新描述符使用大于或等于arg参数的编号最低的可用文件描述符复制文件描述符fd。新描述符与旧的fd共享同一文件表项。但是,新描述符有它自己的一套文件描述符标志. (即类似于dup/dup2()函数的功能)

// text.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main()
{
	int fd = open("./text", O_RDWR | O_CREAT | O_TRUNC, 0775);
	int fcntlFd = fcntl(fd, F_DUPFD, 0); // 指定从 0 开始分配最小的可用描述符作为新描述符
	int dupFd = dup(fd); // 等效于 fcntl(fd, F_DUPFD, 0);
	
	close(fd);
	close(fcntlFd);
	close(dupFd);
	return 0;
}

2).获得/设置文件描述符:

F_GETFL(void) :表示使用 F_GETFL 作为cmd时,不需要传入第三个参数。
功能:获取文件状态标志。
返回值:成功返回文件状态标志(int).
失败返回 -1.
访问方式标志:O_RDONLY 、O_WRONLY、O_RDWR。这3个值是互斥的,因此首先必须用屏蔽O_ACCMODE取得访问方式位,然后将结果与这3个值中的每一个相比较。

F_SETFL(int):表示使用 F_SETFL 作为cmd时,传入第三个参数是int型的。
功能:设置文件状态标志,第三个参数传入新的文件状态标志值。

返回值:成功返回 0
            失败返回 -1.
在Linux上,只能设置这5个文件状态标志:O_APPEND、 O_ASYNC、 O_DIRECT、 O_NOATIME、O_NONBLOCK,其中最常用的是将文件描述符设置成非阻塞(O_NONBLOCK),特别是在网络编程中很常见。下面是设置非阻塞I/O

int  setnonblocking(int fd)
{
    int old_option = fcntl(fd, F_GETFL);//获得文件描述符的状态位
    int new_option = old_option | O_NONBLOCK;//或运算进行设置非阻塞状态位
    fcntl(fd, F_SETFL, new_option);//将状态位重新添加在文件描述符中
    return old_option;
}

注意:文件描述符有多种,可以是文件,可以是套接字等等。

文件描述符的状态位如下:这个open()调用的选择模式的参数也是一样的。

​​​​​​​

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值