70多个函数,Linux常用系统调用一网打尽


70多个函数,Linux系统调用一网打尽

站在那儿别动,伙计!瞧你那一脸邋遢的络腮胡……那两根吊带,还有那洋洋得意的表情。十足一副高人一等的UNIX电脑用户的样子!

——《UNIX环境高级编程》

引言

本文简单记录了一下5helter在使用Linux系统调用中常用的函数,仅供参考,由于编辑本文时未使用markdown导致文章格式混乱,敬请谅解。本文持续更新中

正文

  1. 判断文件是否存在
    int access(const char* pathname,int FLAG)
    调用时FLAG使用宏:F_OK 目录存在,R_OK文件存在,返回非零数

  2. 创建文件夹
    bool mkdir(const char* pathname,int limit)
    limit是文件夹权限,pathname是绝对路径

  3. 打开文件夹
    DIR *opendir(const char *__name)
    打开失败返回空

  4. 读取目录中的文件
    dirent *readdir(DIR *__dirp)
    dirent类保存有目录信息,注意这个函数是每调用一次返回一个目录中的文件,直到没有文件时返回空

  5. 获取文件信息
    int stat(const char *restrict __file, stat *restrict __buf)
    stat类保存文件信息,返回值int为空时调用文件失败或权限不足等

  6. 重命名文件
    int rename(const char *__old, const char *__new)
    这个函数相当于cp命令

  7. 删除文件或目录
    int remove(const char *__filename)
    成功返回0,失败返回-1

  8. 设置文件时间
    int utime(const char __file, const utimbuf *__file_times)
    失败返回0
    其中utimbuf类型如下:
    struct utimbuf
    {
    time_t actime; /
    Access time. /
    time_t modtime; /
    Modification time. */
    };

  9. 重命名文件
    int rename(const char *__old, const char *__new)
    注意__new的目录如果不存在,将返回失败

  10. 将UTC时间转化为本地时间(年月日结构体)
    tm *localtime_r(const time_t *restrict __timer, tm *restrict __tp)
    通过引用返回值
    注意
    sttm.tm_year=sttm.tm_year+1900; // tm.tm_year成员要加上1900。
    sttm.tm_mon++; // sttm.tm_mon成员是从0开始的,要加1。

  11. 断开socket连接
    int close(int __fd)
    返回值:
    失败返回负,但是我们通常不会关系

  12. 通过主机名获取ip地址
    hostent *gethostbyname(const char __name)
    struct hostent 是用于存储主机名和其相关的网络地址信息的结构体成员如下:
    h_name: 官方的主机名。
    h_aliases: 主机名的别名列表。
    h_addrtype: 地址类型,通常是 AF_INET 表示IPv4。
    h_length: 地址的长度。
    h_addr: 主机地址的列表。对于IPv4,这通常作为(char
    )使用,长度为h_length,网络序

  13. 创建套接字
    int socket(int __domain, int __type, int __protocol)
    参数解释:
    __domain填AF_INET表示IPV4
    __type 填的参数可以分为两部分
    第一部分填SOCK_STREAM或者SOCK_DGRAM分别表示流式SOCKET(tcp协议)和数据包SOCKET
    第二部分可以选择或上SOCK_CLOECEX和SOCK_NONBLOCK
    第三个参数填0(使用默认),也可以填IPPROTO_TCP/IPPROTO_UDP
    返回值:
    函数返回套接字句柄,也是一个io文件,失败时返回-1

  14. 把本地序列转换为网络序
    uint16_t htons(uint16_t __hostshort)
    端口号通过这个转换
    注意INADDR_ANY宏需要用htonl函数转换

  15. 流式socket连接到主机
    int connect(int __fd, const sockaddr *__addr, socklen_t __len)
    如果是tcp(流式socket基本上都是tcp,几乎没有列外)
    __fd socket句柄
    __addr 填充好的sockaddr_in对象
    _t __len 填入sizeof(sockaddr_in)即可

  16. 接收报文函数
    ssize_t recv(int __fd, void *__buf, size_t __n, int __flags)
    参数解释:
    这里只解释一下 __flags,其他参数同编号69,70两个函数
    __flags 一般填0即可
    返回读取到的字符数量,出错返回-1

  17. 发送报文函数
    ssize_t send(int __fd, const void *__buf, size_t __n, int __flags)
    参数情况同16,
    返回值:
    发送的字符数量,出错返回-1

  18. 设置socket选项
    int setsockopt(int __fd, int __level, int __optname, const void *__optval, socklen_t __optlen)
    参数解释:
    __fd socket描述符
    __level 网络层协议可选的选项有SOL_SOCKET(tcp/udp 通用)IPPROTO_TCP IPPROTO_UDP
    __optname 在该层要设置的属性,比如SO_REUSEADDR,SO_REUSEPORT,SO_KEEPALIVE(SOCK级别),TCP_NODELAY(tcp级别)
    __optval 选项设置的值,一般填值为1的整型变量,表示打开;当然填别的值的,根据__optname需要的参数决定
    _t __optlen __optval的字节数,填sizeof(__optval)
    返回值:
    为负如果失败
    说明:
    此函数在在服务器端使用

  19. 将socket绑定到监听端口
    int bind(int __fd, const sockaddr __addr, socklen_t __len)
    __addr是要监听的sockaddr_in,至少设置sin_family(协议) sin_addr.s_addr(地址) sin_port(端口) 三个成员
    注意将sockaddr_in
    强转为sockaddr*;
    __len传入sizeof(sockaddr_in)
    此函数在在服务器端使用,设置失败返回负数

  20. socket监听
    int listen(int __fd, int __n)
    __n最大排队长度,超过可能会被拒绝连接
    成功返回0,失败返回-1

  21. 阻塞等待客户端连接
    int accept(int __fd, sockaddr *restrict __addr, socklen_t *restrict __addr_len)
    restrict __addr 传入一个sockaddr_in,指针,内容将被函数设置
    restrict __addr_len socklen_t类型是一个整数,填入sizeof(sockaddr_in)即可
    返回与客户端连接的Socket描述符

  22. 创建子进程
    int fork()
    返回值:
    主进程返回子进程的进程ID,是一个大于零的数,子进程返回0,出错返回-1
    说明:
    在Linux中,不同进程的socket id/文件描述符 并不是共享的;也就是说,即使socket id/文件描述符 是相同的,在不同的进程中代表的连接也是不同的;
    默认情况下fork()函数不仅会复制socket id/文件描述符 到子进程,也会新建对应连接,从子进程到对应的目标。(底层使用了引用计数器实现,socket id/文件描述符会记录拥有这个连接的进程个数,close函数
    是将个数减一,只有当计数为0时才会被关闭),如果创建Socket时指定了SOCK_CLOEXEC,那么子进程会关闭这个Sock

  23. 回收子进程资源
    pid_t wait(int* status)
    阻塞直到有子进程退出,status被设置为退出子进程状态,使用一些宏可转化为字符串查看
    此函数在signal(SIGHID,SIG_DFL)下生效
    signal(SIGHID,SIG_IGN)下失效,不过情况下系统自动回收资源,父进程繁忙时,将SIGHIDSIGHID绑定到处理函数,如需要;

  24. 设置环境变量
    int setenv(const char *__name, const char *__value, int __replace)
    下面以设置环境字符集为例子,程序字符集与数据库不兼容将导致数据库数据乱码;
    第三个参数填1时,如果存在这个环境变量就用新值替代
    第二个参数是值,字符集可以是 “Simplified Chinese_China.AL32UTF8”
    第一个参数是环境变量名, 更改字符集填 “NLS_LANG”
    这个设置只对当前用户的本次登录生效,重启后失效(写在.bashrc或者.bash_profile中才能永久生效)

  25. select模型
    int select(int nfds, fd_set* restrict readfds,fd_set restrict writefds, fd_set restrict errorfds,struct timeval* restrict timeout);//restrict 关键字表示指针需要指向一个左值地址
    参数解释:
    fd_set* readfds
    传递给select()一个设置好的fd_set对象的地址,用于监视读事件,若不监视填NULL; fd_set解释如下:
    socket集合,大小为1024位,作为位图使用;C语言使用以下四个函数操作位图:

    void FD_ZERO(fd_set *fdset); //初始化位图,将1024个位全部置为0
    void FD_SET(int fd, fd_set fdset); //fd是描述符,把对应socket加入集合中( 其实是把对应位置置一,因此fd<1024)
    void FD_CLR(int fd, fd_set fdset); //把fd从位图中删除
    int FD_ISSET(int fd, fd_set fdset); //判断fd是否在位图中,不在返回0,否则返回大于零的数

    struct timeval
    timeout
    超时时间,在超时时间内,若没有发生任何事件,select()函数返回,不再阻塞进程,如果填NULL或者0表示永远等待
    timeval结构体成员 int tv_sec 秒 int tv_usec 毫秒
    select事件分为读事件和写事件
    读事件包括
    1.监听
    2.读缓存中有数据
    3.收到close()申请
    写事件包括
    1.写缓冲区可写入数据
    int nfds
    传递给select函数位图的实际大小
    fd_set
    writefds
    传递给select()一个设置好的fd_set对象的地址,用于监视读事件,若不监视填NULL;
    fd_set
    errorfds
    传递给select()一个设置好的fd_set对象的地址,用于监视异常事件,若不监视填NULL;
    函数返回值:
    小于0:调用select失败,errno被设置(可能得原因有内存不足,参数错误,描述符异常,收到信号终止)
    等于0;select超时
    大于0;返回已经发生的事件数量,readfds/writefds被修 修改的具体方式是,没有事件的socket fd位被清零
    其他说明:
    select函数判断是否有读写事件发生是通过检测socket的发送或接收缓冲区
    应用经验:
    水平触发:用select检测的socket发生了事件,立即返回
    实际上可以在有事件触发时,先等一段时间再次检测后处理
    由于每次select()函数都要更改位图,因此应该传递给select()一个临时位图每次循环前从复制源位图复制

  26. poll模型
    int poll(struct pollfd* fds, nfds_t nfds, int timeout);
    参数解释:
    pollfd* fds
    传入poll位图;
    poll模型使用数组作为位图,格式是:pollfd <位图名>[]; constexpr number可取1024,2048等,但不要超过10000;
    其中pollfd 结构如下:
    struct pollfd {
    int fd; /* file descriptor /
    short events; /
    requested events /
    short revents; /
    returned events */
    };
    参数解释:
    fd socket描述符,poll认为-1代表没有socket
    events 选择要监听的事件,取值可以是POLLIN POLLOUT POLLDHUP POLLPRI(带外数据) POLLERR POLLHUP(挂起事件)
    revents 由poll()函数设置,revents与事件宏相与可以判断事件是否发生
    nfds_t nfds
    传入一个整数,认为是监视socket的最大下标
    int timeout
    超时事件,单位是毫秒
    返回值:
    小于0:调用poll失败,errno被设置(可能得原因有内存不足,参数错误,描述符异常,收到信号终止)
    等于0;poll超时
    大于0;返回已经发生的事件数量, 位图中每个pollfd 的revents被修改
    开发经验:
    位图的下标和fd的值一般相同,这样符合变成规范

  27. epoll模型
    int epoll_create(int size);
    参数解释:
    int size
    已弃用,填任意大于零的书
    返回值:
    返回一个epoll文件描述符(句柄),是一个整数
    控制epoll句柄的函数:
    int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);
    参数解释:
    int epfd :
    epoll句柄
    int op :
    对epoll进行的操作
    可选的选项有:EPOLL_CTL_ADD EPOLL_CTL_DEL EPOLL_CTL_MOD
    int fd :
    填入要监听的socket
    epoll_event* event :
    epoll_event 是epoll事件类型,定义如下:
    struct epoll_event
    {
    uint32_t events; /* Epoll events /
    epoll_data_t data; /
    User data variable /
    };
    其中epoll_data定义如下:
    typedef union epoll_data
    {
    void ptr;
    int fd;
    uint32_t u32;
    uint64_t u64;
    } epoll_data_t;
    一般epoll_data只需使用fd,并且将其设置为socket描述符即可;这里填的是什么,epoll_wait函数返回的就是什么;
    epoll_data_t data 设置为要监听的事件,可以是EPOLLIN EPOLLOUT EPOLLDHUP EPOLLPRI(带外数据) EPOLLERR EPOLLHUP(挂起事件)
    注意socket断开时EPOLL_CTL_DEL不是必须的,epoll会自动卸下
    使用epoll的函数:
    int epoll_wait(int epfd, struct epoll_event
    events,int maxevents, int timeout);
    参数解释:
    int epfd :
    epoll句柄
    epoll_event
    events:
    传入一个epoll_event数组,内容将被本函数设置
    int maxevents:
    传入events的数组长度
    int timeout:
    超时时间
    返回值:
    小于0:调用epoll失败,errno被设置(可能得原因有内存不足,参数错误,描述符异常,收到信号终止)
    等于0;epoll超时
    大于0;返回已经发生的事件数量,即设置events中元素的个数
    关闭epoll的函数
    void close(int epfd);
    epoll用完后需关闭

  28. 设置文件描述符状态
    文件描述符(文键句柄)有很多状态,这些状态也由整数表示,并且可以使用 | 做添加运算
    int fcntl(int fd, int cmd, … /* arg */);
    用于对文件描述符执行各种操作。它提供了一种在运行时控制文件描述符的方法,包括改变文件状态、设置文件描述符标志、以及执行各种其他操作
    fd:表示文件描述符,
    cmd:表示要执行的操作命令,是一个整数值。例如获取状态 F_GETFL 设置状态F_SETFL
    arg:这是一个可选的参数,它取决于 cmd 参数的具体操作类型。根据不同的命令,arg 可能是一个整数、一个结构体指针,或者其他类型的数据。
    下面演示将socket文件描述符设置为非阻塞状态的例子:
    int setnonblocking(int fd)
    {
    int flags;

    // 获取fd的状态。
    if ((flags=fcntl(fd,F_GETFL,0))==-1)
    flags = 0;

    return fcntl(fd,F_SETFL,flags|O_NONBLOCK);
    }
    非阻塞socket说明:非阻塞socket的connect recv send accept函数都不再阻塞,但是connect函数一定会返回失败这是返回的错误代码是EINPROGRESS;要通过检查socket是否可写检测连接是否成功
    在io复用模型中,connect recv send accept都必须是非阻塞的,任何阻塞操作交给poll或者epoll进行;代码如下:
    if (connect(sockfd, (struct sockaddr *)&servaddr,sizeof(servaddr)) != 0)
    {
    if (errno!=EINPROGRESS)
    {
    printf(“connect(%s:%s) failed.\n”,argv[1],argv[2]); close(sockfd); return -1;
    }
    }

    pollfd fds;
    fds.fd=sockfd;
    fds.events=POLLOUT;
    poll(&fds,1,-1); //负一表示一直等待
    if (fds.revents==POLLOUT)
    printf(“connect ok.\n”);
    else
    printf(“connect failed.\n”);
    同样的问题还有非阻塞的accept,recv函数,使用下面的代码解决
    while (accept(listensock,0,0)==-1)
    {
    if (errno!=EAGAIN) //EAGAIN表示缓冲区中无内容(send函数是被填满)
    perror(“accept:”); return -1;
    else
    break;
    }

    当然直接交给poll或者epoll等待accept和recv没有这么多事

  29. epoll边缘触发模式
    在向epoll句柄中添加epoll_event* event时,如果对齐成员events的赋值 | 上EPOLLET 可设置为边缘触发,边缘触发不再通过检测缓冲区有无内容判断有无事件发生,而是通过等待期间缓冲区的变化判断
    边缘触发模式一定要用循环处理事件,一次性把缓冲区处理干净;

  30. 定时器
    unsigned alarm(unsigned seconds);
    经过unsigned seconds向进程发送一个SIGALRM信号
    返回值:
    返回上一个alarm()调用剩余的秒数,如果没有返回0
    alarm函数实际上是对计时器句柄的封装,创建按定时器句柄并用epoll监视alarm句柄代码如下:
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //把定时器加入epoll。
    int tfd=timerfd_create(CLOCK_MONOTONIC,TFD_NONBLOCK|TFD_CLOEXEC); // 创建timerfd。
    struct itimerspec timeout; // 定时时间的数据结构。
    memset(&timeout,0,sizeof(struct itimerspec));
    timeout.it_value.tv_sec = 10; // 定时时间为10秒。
    timeout.it_value.tv_nsec = 0;
    timerfd_settime(tfd,0,&timeout,0); // 开始计时。alarm(10)
    ev.data.fd=tfd; // 为定时器准备事件。
    ev.events=EPOLLIN;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,tfd,&ev); // 把定时器fd加入epoll。
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////

    在epoll模型中,为了为了更新进程心跳或者清理空闲的客户端socket,一般会监视一个计时器事件,注意计时器句柄也是单次的,使用以下函数操控
    int timerfd_create(int clockid, int flags);

    int timerfd_settime(int fd, int flags,
    const struct itimerspec *new_value,
    struct itimerspec *old_value);

    int timerfd_gettime(int fd, struct itimerspec *curr_value);
    此外epoll还监视信号和信号集,代码如下
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //把信号加入epoll。
    sigset_t sigset; // 创建信号集。
    sigemptyset(&sigset); // 初始化(清空)信号集。
    sigaddset(&sigset, SIGINT); // 把SIGINT信号加入信号集。
    sigaddset(&sigset, SIGTERM); // 把SIGTERM信号加入信号集。
    sigprocmask(SIG_BLOCK, &sigset, 0); // 对当前进程屏蔽信号集(当前程将收不到信号集中的信号)。
    int sigfd=signalfd(-1, &sigset, 0); // 创建信号集的fd。
    ev.data.fd = sigfd; // 为信号集的fd准备事件。
    ev.events = EPOLLIN;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,sigfd,&ev); // 把信号集fd加入epoll。
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////

  31. 线程通信
    线程通信有三种方式
    1.发送信号,epoll可以监视信号
    2.使用tcp通信
    3.使用管道通信,管道通信是一种在本地上简易的通信方式,相比tcp

  32. 管道
    int pipe(int *__pipedes)
    解释:
    此函数用于创建管道,管道是用户进程间通信是一个单向通信通道,数据只能从一个进程的输出端写入,然后从另一个进程的输入端读取
    参数解释:
    传入一个大小为2的整型数组
    __pipedes[0]用于读取数据,__pipedes[1]用于写入数据
    返回值:
    如果执行成功,pipe函数返回0
    如果执行失败,返回-1,并设置errno来指示错误类型

  33. 获取UTC时间
    time_t time(time_t* now)
    __now__被设置

  34. errno==EINTR错误
    这个错误是,阻塞的系统调用(wait,accpt等)过程中,程序收到软中断导致的,防止这个错误的方法包括closeallioandsignal(true)

  35. 获取微秒精度时间
    UTC时间,即time_t是精确到秒的,要获得微秒级时间需要使用下面的函数
    int gettimeofday(struct timeval*tv, struct timezone *tz);
    timezone *tz是需要设置的时区,填nullptr即可
    timeval类型
    struct timeval{
    long int tv_sec; // 秒数
    long int tv_usec; // 微秒数
    }

  36. clock()函数
    获取程序消耗时钟数
    发现clock()是程序从启动到函数调用占用CPU的时间。这个函数返回从“开启这个程序进程”到“程序中调用clock()函数”时之间的CPU时钟计时单元(clock tick)数
    使用方法如下
    int i = 100000000;
    clock_t start,finish; //定义开始,结束变量
    start = clock();//初始化
    while( i– );
    finish = clock();//初始化结束时间
    double duration = (double)(finish - start) / CLOCKS_PER_SEC;//转换浮点型
    printf( “%lf seconds\n”, duration );
    该方法计算出程序所使用的时间

  37. 创建命名管道
    int mkfifo(const char *pathname, mode_t mode);
    匿名管道pipe只能由于父子进程或者线程之间通信
    创建匿名管道可以用于进程间通信
    *pathname 普通的路径名,也就是创建后 FIFO 的名字
    mode ⽂件的权限,
    与打开普通⽂件的 open() 函数中的 mode 参数相同。(0666).

  38. 创建socket
    int socket(int domain, int type, int protocal)
    domain 参数⽤来指定协议族,⽐如 AF_INET,AF_LOCAL(本地进程间通信)
    type 参数SOCK_STREAM和SOCK_DGRAM
    protocal填0

  39. 创建线程
    int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
    *thread穿入线程地址
    attr 传入需要设置的pthread属性,可以填nullptr
    void *(*start_routine) (void ) 传入线程函数指针,这个函数返回值和参数只能是void,参数含义需要自己确定
    arg 传入的线程函数的参数
    返回值:小于零返回失败

  40. 回收非分离线程资源
    int pthread_join(pthread_t thread, void **retval);
    thread 传入线程id
    retval 线程函数的返回值
    返回值:线程函数的返回状态,自然退出返回0,取消返回负一,出错返回其他负数

  41. 线程函数返回
    void pthread_exit(void* retval)
    线程函数不可以用exit函数退出,这个函数等价于 return (void*)retval

  42. 初始化线程属性
    int pthread_attr_init(pthread_attr_t *attr);
    初始化一个线程属性结构体
    返回值为负如果失败
    注意:应先初始化线程属性,再pthread_create创建线程
    成功: 0; 失败: 错误号.

  43. 设置线程属性
    int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
    attr填要设置的属性
    detachstate 填PTHREAD_CREATE_DATACHED可以设置为分离态

    如果设置⼀个线程为分离线程创建,⽽这个线程运⾏⼜⾮常快,它很可能在pthread_create函数返回之前就终⽌
    

    了,它终⽌以后就可能将线程号和系统资源移交给其他的线程使⽤,这样调⽤pthread_create的线程就得到
    了错误的线程号
    要避免这种情况可以采取⼀定的同步措施,最简单的⽅法之⼀是可以在 被创建的线程⾥ 调⽤pthread_cond_timedwait函数,
    让这个线程等待⼀会⼉,留出⾜够的时间让函数pthread_create返回

  44. 设置线程为分离态
    int phtread_detach(pthread_t thread);
    这个函数可以在运行中设置,前面的方法只能在创建线程前设置

  45. 获取本线程id
    pthread_t pthread_self()

  46. 取消线程
    int pthread_cancel(pthread_t thread)

  47. 线程清理
    void pthread_cleanup_push(void (*routine)(void *),void *arg);
    线程退出前调用此函数进行善后,压入一个在线程退出前执行pthread_cleanup_pop就会执行对应的栈顶函数
    void pthread_cleanup_pop(int execute);
    pthread_cleanup_push和pthread_cleanup_pop在线程函数中必须成对出现,否则编译出错
    execute函数
    填0弹出不执行,填大于零的数弹出并执行
    线程退出前,压到栈中的清理函数会被执行,无论其是否执行到pthread_cleanup_pop

  48. 向发送信号
    int pthread_kill(pthread_t thread, int sig)
    参数解释:
    thread:
    线程id
    sig:
    发送的信号
    返回值:
    为负如果失败
    说明:
    使用这个函数直接对线程发信号,会使对应线程中断
    向进程发送的信号并不会中断子线程,主线程和子线程共享信号处理函数,你可在任何线程调用signal函数改变信号处理器
    因此可以说线程缺少对信号的支持,用信号进行线程间通信是困难的
    主线程(进程)向子线程发送信号:

  49. 获取系统线程id
    int syscall(int number, …);
    说明:
    在多线程中,pthread_self()函数获得的线程号是pthread库对线程的编号,而不是Linux系统对线程的编号。这个线程号只在本进程可用在别的进程中可能会被重用
    pthread_create()返回的线程号,使用top命令是查不到的,top显示的是Linux的线程号,syscall(SYS_gettid)返回的线程号,在系统中任意一个时刻是唯一的,
    置得注意到是,在主线程中调用getpid()得到的值与主线程中调用syscall(SYS_gettid)得到的值一致,返回值类型是pid_t
    该函数的一个典型用法是
    获取的方法数是syscall(SYS_gettid),

  50. 销毁线程
    int pthread_attr_destroy(pthread_attr_t *attr);
    attr 线程属性结构体
    成功: 0; 失败: 错误号

  51. 获取线程状态
    int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
    从attr中获取对应属性

  52. 设置栈空间
    int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);

  53. 获取栈空间
    int pthread_attr_getstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);

  54. socket对
    int socketpair(int domain, int type, int protocol, int sv[2]);

  55. eventfd线程间通信
    int eventfd(unsigned int initval, int flags);
    这个是用于线程间通信的新方法,效率高于管道,使用方法完全等同于普通文件描述符

  56. 初始化信号量(posix型,一般用于线程通信)
    sint sem_init(sem_t *sem, int pshared, unsigned int value);
    参数解释:
    sem_t *sem 需要初始化的信号量指针,左值,一般直接创建然后取地址传递即可
    int pshared 传入0代表在线程间使用,一般是全局变量即可,传入1代表线程间使用,必须在共享内存区创建sem_t,具体方法后面说
    unsigned int value 初始值,一般的初始值设置为零可以用于确定某操作必须限于某操作;设置为1相当于互斥锁

  57. 信号量P操作
    sem_post(sem_t *sem)
    这个不解释,参考计算机操作系统课程

  58. 信号量V操作
    sem_wait(sem_t *sem)
    这个不解释,参考计算机操作系统课程

  59. 存储映射IO(PosiX)
    void *mmap(void addr, size_t length, int prot, int flags,int fd, off_t offset);
    参数解释:
    addr 映射到的偏移地址,一般填NULL由系统设置即可
    length 需要映射的长度(一个正数),一般需要小于文件大小,如果大小不对,将收到SIGBUS错误信号
    prot 文件属性,可选PROT_WRITE,PROT_READ,PROT_EXEC,在MAP_SHARED模式下打开的文件描述符必须设置为大于这里的权限,否则会出参数错误,而MAP_PRIVATE则不需要,因为不会写道文件里
    flags MAP_SHARED和MAP_PRIVATE,多进程用前者,后者不会把内存中的值写到文件中,MAP_ANONYMOUS用于创建匿名映射,此时fd设置为-1
    fd 打开的文件描述符,改函数建立映射后可立即关闭fd
    offset 偏移量,必须为4k的整数倍,填零即可
    返回值:
    失败时返回(void
    )-1;
    用法:
    可以进行父子进程通信,需要设置MAP_SHARED
    也可以用于非亲缘关系的进程间通信,映射到同一个文件即可,显然不能创建匿名的
    unix和系统中有两个特殊的文件/dev/zreo和/dev/null前者可以读无限个空洞,后者可以无限写入

  60. 设置文件偏移量
    off_t lseek(int fd, off_t offset, int whence);
    参数解释:
    fd 打开的文件描述符
    offset 偏移量字节为单位
    whence 开始计算偏移的起始位置,可选SEEK_SET,SEEK_END,SEEK_CUR
    返回值:
    失败时返回-1;成功时返回较文件起始位置的偏移总量
    用法:处理用于设置偏移,还常用于创建文件空洞,
    如果总偏移位置大于了文件大小,不足的部分将由空洞补充,在空洞后面写入一个字符可以是\0,此次空洞将被保存到文件中,空洞不能被读到,但是可以用于存储映射IO

  61. 打开文件
    int open(const char *pathname, int flags);
    int open(const char *pathname, int flags, mode_t mode);
    这个函数不解释

  62. 修改文件大小
    int ftruncate(int fd, off_t length)
    源文件长度如果不足,系统将用空洞填充

  63. 释放映射区
    int munmap(void *addr, size_t length);
    参数解释:
    addr起始地址
    length大小
    返回值:
    成功返回0,失败返回-1
    用法:
    释放映射区时内存数据才会真正被写到文件,如果要提前写入调用msync(2)函数,否则写入将被从文件上撤销

  64. 创建或者打开共享内存/在交换分区上的内存映射创建
    int shmget(key_t key, size_t size, int shmflg);
    参数解释:
    key 用于标识共享内存,共享内存编号不可以相同,一般由程序员设置一个十六进制数
    size 创建共享内存的大小
    shmflg 访问权限同open的mode参数
    返回值:
    失败时返回-1;返回共享内存的标识
    用法:
    也叫systemV型内存映射,特点是不能在文件系统中直接看到文件实体,只能通过ipcs -sa 命令查看,这种文件可以防止运行中文件被误删
    使用ipcrm shm [shmid]删除共享内存
    我这里给出一个典型用法:
    int shmid=shmget((key_t)0X5005,1024,0640|IPC_CREATE);//创建shmid得角色类似于fd,key_t的角色类似于文件名

  65. 连接共享内存到程序内存空间
    void *shmat(int shmid, const void shmaddr, int shmflg);
    参数解释:
    shmid 共享内存标识
    shmaddr 可以填空,让系统决定起始位置
    shmflg 标志位填0即可
    返回值:
    返回指向映射的第一个地址,出错返回(void
    )-1

  66. 从进程中分离共享内存
    int shmdt(const void *shmaddr);
    参数解释:
    shmaddr shmat返回的地址
    返回值:
    出错返回负一

  67. 共享内存控制
    int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    参阅linux程序员手册
    我们这里了解一种用法即可
    shmctl(shmid,IPC_RMID,0)
    用于删除共享内存

  68. 信号量集创建或者获取(systemV型,可用于多进程)
    int semget(key_t key, int nsems, int semflg);
    参数解释:
    key 用于标识信号量集,信号量编号不可以相同,一般由程序员设置一个十六进制数
    nsems 信号量的个数
    semflg 同open函数的mode,可以使用IPC_CREAT标记用于创建
    返回值:
    返回semid,失败时返回-1
    PosiX型信号量又叫信号灯一般适用于多线程程序,而systemV型是基于共享内存的,可以在进程间通信
    使用ipcs -s命令查看创建的信号量 ipcrm sem 可以删除信号量

  69. 信号量控制
    int semctl(int semid, int semnum, int cmd, …);
    参数解释:
    semid semget的返回值
    semnum 当前要控制信号量集中的第几个信号量,第一个填0,第二个填1
    cmd 常用的有两个IPC_RMID,SETVAL,GETVAL分别表示删除信号量,设置初始值,获取信号量当前值
    … 一个共用体,结构如下:
    union semun {
    int val; /* Value for SETVAL */
    struct semid_ds buf; / Buffer for IPC_STAT, IPC_SET */
    unsigned short array; / Array for GETALL, SETALL */
    struct seminfo __buf; / Buffer for IPC_INFO
    (Linux-specific) */
    };
    一般使用它的val成员就可以,表示信号量的初始值
    注意这个共用体必须程序员自己定义
    返回值:
    失败返回-1

  70. 信号量操作
    int semop(int semid, struct sembuf *sops, unsigned nsops);
    参数解释:
    semid semget的返回值
    sops 结构体如下:
    struct sembuf
    {
    short sem_num; //信号量在信号量集中的位置
    short sem_post //-1等待,+1发送
    short sem_flg //把此标志SEM_UNDO系统将跟踪这个信号量,如果进程结束没有释放信号量,系统将自动释放
    //如果用于控制进程顺序,这个字段填零
    }
    sops可以是一个sembuf的地址也可以是sembuf的数组首地址,即可以同时对多个信号量集操作
    nsops 要操作的信号量集数,如果sops是一个sembuf的地址,这个值填1
    返回值:
    为负如果失败

  71. 读IO
    ssize_t read(int fd, void *buf, size_t count);
    参数解释:
    fd 文件描述符
    buf 读入到的起始位置
    count 要读入的字节数
    返回值:
    ssize_t是一个有符号整数,返回的是成功读取的字节数,读取失败返回负数

  72. 写IO
    ssize_t write(int fd, const void *buf, size_t count);
    参数解释:
    fd 文件描述符
    buf 写入的起始位置
    count 要写入的字节数
    返回值:
    ssize_t是一个有符号整数,返回的是成功写入的字节数,读取失败返回负数

  73. 从IO读入到非连续空间
    ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
    参数解释:
    fd 文件描述符
    iov iovec数组,iovec类型用于描述内存空间,结构如下
    struct iovec {
    void iov_base; / Starting address /
    size_t iov_len; /
    Number of bytes to transfer */
    };
    成员解释:
    iov_base 内存的起始地址
    iov_len 内存的长度
    iovcnt 参数二中数组长度是多少这里填几
    返回值:
    函数调用成功时返回读,失败时返回 -1 并设置相应的 errno

  74. 从非连续空间写入IO
    ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
    返回值:
    函数调用成功时返回读,失败时返回 -1 并设置相应的 errno


Author: 5helter
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source 5helter !
  TOC