Summary for IPC

Summary for IPC

Preface

在进程间完成数据传递需要借助操作系统提供的特殊方法,如文件、管道(pipe,fifo)、信号、共享内存、消息队列、套接字等,其中最常用进程间通信方式有:

管道(使用最简单)
共享映射区
信号(开销最小)
本地套接字(最稳定)

管道

管道的本质是一个伪文件(实为内核缓冲区),其由两个文件描述符引用,一个表示读端,一个表示写端,规定数据从管道的写端流入管道,从读端流出。管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。
管道的局限性:

数据只能沿一个方向流动
数据一旦被读走,便不会继续在管道中存在,不能实现反复读取

匿名管道pipe
    #include <unistd.h>
    int pipe(int pipefd[2]);

    #define _GNU_SOURCE         /* See feature_test_macros(7) */
    #include <fcntl.h>          /* Obtain O_* constant definitions */
    #include <unistd.h>
    int pipe2(int pipefd[2], int flags);

DESCRIPTION
pipe() creates a pipe, a unidirectional data channel that can be used for interprocess communication. The array pipefd is used to return two file descriptors referring to the ends of the pipe. pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe. Data written to the write end of the pipe is buffered by the kernel until it is read from the read end of the pipe. For further
details, see pipe(7). If flags is 0, then pipe2() is the same as pipe().
RETURN VALUE
On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
NOTICE
pipe()只能用于具有相同祖先的进程(最简单的情况:父子进程)之间的通信。

示例:

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>

int main()
{
   pid_t pid;
   int fd[2];
   char buff[30];
   // init pipe
   if(-1 ==  pipe(fd))
   {
      perror("pipe fail");
      exit(-1);
   }
   // create multi-process and prepare for IPC via pipe
   pid = fork();
   if(-1 == pid)
   {
      perror("error occurs when fork");
      exit(1);
   }
   else  if(0 == pid)
   {
      close(fd[0]);
      write(fd[1],"hello pipe",sizeof("hello pipe\n"));
      close(fd[1]);
   }
   else
   {
      sleep(1);
      close(fd[1]);
      while(read(fd[0],buff,1)>0)
         printf("read from pipe: %s\n",buff);
   }
   return 0;
}
    

管道的读写行为:
管道的读写行为

管道缓冲区大小查询:

命令:      ulimit -a
函数:      long fpathconf(int fd, int name);   //name传入_PC_PIPE_BUF
有名/命名管道fifo

fifo不同于pipe的地方:

  • fifo可以看作高级的管道,可以在任意进程之间建立通信连接;
  • fifo是一个实际存在于磁盘中的文件,而pipe是由进程创建的,依赖于进程的存活期。
  • 命名管道和管道的使用方法法基本是相同的。只是使用命名管道时,必须先调用open()将其打开。因为命名管道是一个存在于硬盘上的文件,而管道是存在于内存中的特殊文件。

fifo的打开规则:

  • 如果当前打开操作是为读而打开fifo ,若已经有相应进程为写而打开该fifo时,当前打开操作将成功返回;否则,可能阻塞到有相应进程为写而打开该fifo(当前打开操作设置了阻塞标志);或者,成功返回(当前打开操作没有设置阻塞标志)。
  • 如果当前打开操作是为写而打开fifo时,如果已经有相应进程为读而打开该fifo,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为读而打开该fifo(当前打开操作设置了阻塞标志);或者,返回ENIO错误(当期打开操作没有设置阻塞标志)。
  • 如果open时没有使用O_NONBLOCK参数,我们发现不论读端还是写端先打开,先打开者都会阻塞,一直阻塞到另一端打开。
  • 不能以O_RDWR模式打开FIFO文件进行读写操作,这样做的行为是未定义的

O_RDONLY、O_WRONLY和O_NONBLOCK标志共有四种合法的组合方式:

  • flags=O_RDONLY:open将会调用阻塞,除非有另外一个进程以写的方式打开同一个FIFO,否则一直等待。
  • flags=O_WRONLY:open将会调用阻塞,除非有另外一个进程以读的方式打开同一个FIFO,否则一直等待。
  • flags=O_RDONLY|O_NONBLOCK:如果此时没有其他进程以写的方式打开FIFO,此时open也会成功返回,此时FIFO被读打开,而不会返回错误。
  • flags=O_WRONLY|O_NONBLOCK:立即返回,如果此时没有其他进程以读的方式打开,open会失败打开,此时FIFO没有被打开,返回-1。
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);

#include <fcntl.h>           /* Definition of AT_* constants */
#include <sys/stat.h>
int mkfifoat(int dirfd, const char *pathname, mode_t mode);

DESCRIPTION
    mkfifo() makes a fifo special file with name pathname. mode specifies the fifo's permissions. 
    It is modified by the process's umask in the usual way: the permissions of the created file are (mode & ~umask).
RETURN VALUE
    On  success mkfifo() and mkfifoat() return 0.  In the case of an error, -1 is returned (in which case, errno is set appropriately).

fifo的读写规则:

从fifo中读取数据
约定:如果一个进程为了从fifo中读取数据而以阻塞的方式打开fifo,则称内核为该进程的读操作设置了阻塞标志

如果有进程为写而打开fifo,且当前fifo内没有数据,那么对于设置了阻塞标志的读操作来说,将一直阻塞。对于没有设置阻塞标志的读操作来说返回-1,当前errno值为EAGAIN,提醒以后再试。
对于设置阻塞标志的读操作来说,造成阻塞的原因有两种:当前fifo内有数据,但有其他进程正在读这些数据;另外就是fifo内没有数据。解阻塞的原因则是fifo中有新的数据写入,不论写入数据量的大小,也不论读操作请求多少数据量。
如果没有进程写打开fifo,则设置了阻塞标志的读操作会阻塞
如果写端关闭,管道中有数据读取管道中的数据,如果管道中没有数据读端将不会继续阻塞,此时返回0。
注意:如果fifo中有数据,则设置了阻塞标志的读操作不会因为fifo中的字节数小于请求读的字节数而阻塞,此时,读操作会返回fifo中现有的数据量。

向fifo中写入数据
约定:如果一个进程为了向fifo中写入数据而阻塞打开fifo,那么称该进程内的写操作设置了阻塞标志。

对于设置了阻塞标志的写操作:
当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果此时管道空闲缓冲区不足以容纳要写入的字节数,则进入睡眠,直到当缓冲区中能够容纳写入的字节数时,才开始进行一次性写操作。
当要写入的数据量大于PIPE_BUF时,Linux将不再保证写入的原子性。fifo缓冲区一有空闲区域,写进程就会试图向管道写入数据,写操作在写完所有请求写的数据后返回。

对于没有设置阻塞标志的写操作:
当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。在写满所有fifo空闲缓冲区后,写操作返回。
当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果当前fifo空闲缓冲区能够容纳请求写入的字节数,写完后成功返回;如果当前fifo空闲缓冲区不能够容纳请求写入的字节数,则返回EAGAIN错误,提醒以后再写。
注意:只有读端存在,写端才有意义。如果读端不在,写端向fifo写数据,内核将向对应的进程发送SIGPIPE信号(默认终止进程)。

函数解析:

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
DESCRIPTION
mkfifo() makes a FIFO special file with name pathname. mode specifies the FIFO's permissions. 
It is modified by the process's umask in the usual way: the permissions of the created file
are (mode & ~umask).

#include <fcntl.h>           /* Definition of AT_* constants */
#include <sys/stat.h>
int mkfifoat(int dirfd, const char *pathname, mode_t mode);
DESCRIPTION
The  mkfifoat()  function operates in exactly the same way as mkfifo(), except for the differences
described here. If the pathname given in pathname is relative, then it is interpreted relative
to the directory referred to by the file descriptor dirfd (rather than relative to the current 
working directory of the calling process, as is done by mkfifo() for a relative pathname).
If pathname is relative and dirfd is the special value AT_FDCWD, then pathname is interpreted 
relative to the current working directory of the calling process (like mkfifo()).
If pathname is absolute, then dirfd is ignored.

RETURN VALUE
On success mkfifo() and mkfifoat() return 0. In the case of an error, -1 is returned (in which
case, errno is set appropriately).

程序示例:

// fifo_server.c


#include<stdio.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<string.h>
#define _PATH_NAME "./tmp"
    
 int main()
 {
      int ret;
      unlink(_PATH_NAME);
      ret=mkfifo(_PATH_NAME,0777);
      if(ret==-1)
      {
           printf("make fifo error\n");
           return 1;
      }
     int fd=open(_PATH_NAME,O_WRONLY);
     if(fd < 0)
     {
        perror("open fifo failed!");
        return -1;
     }
     else
     {
        printf("open fifo successful\n");
        char str[20] = "love but not own";
        int i = 1;
        if(write(fd,str,sizeof(str)) != -1)
        {
            printf("write successful\n");
            sleep(1);
        }
        else
            perror("Write failed");
     }
     return 0;
 }                   
//fifo_client.c


#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<string.h>
#include<error.h>
#define _PATH_NAME "./tmp"

int main()
{
    int fd,ret;
    char *buff;
    fd=open(_PATH_NAME,O_RDONLY|O_NONBLOCK);
    if(fd<0)
    {
        printf("open file error");
        return -1;
    }
    
   int size;
   char str[30];
   size = read(fd,str,sizeof(str));
   if(size > 0)
   {
       printf("%s\n",str);
   }
   else
   {
       printf("Err:%d\n",size);
       sleep(1);
   }
     
   close(fd);
   return 0;
 }

Addition&Advance:

#include <sys/stat.h>
int mknod(const char *path, mode_t mode, dev_t dev);
int mknodat(int fd, const char *path, mode_t mode, dev_t dev);

DESCRIPTION
The mknod() function shall create a new file named by the  pathname  to which the argument 
path points. The  file type for path is OR'ed into the mode argument, and the application 
shall select one of the following symbolic constants:
            ┌───────────┬──────────────────────────────────┐
            │   Name    │           Description            │
            ├───────────┼──────────────────────────────────┤
            │S_IFIFO    │ FIFO-special                     │
            │S_IFCHR    │ Character-special (non-portable) │
            │S_IFDIR    │ Directory (non-portable)         │
            │S_IFBLK    │ Block-special (non-portable)     │
            │S_IFREG    │ Regular (non-portable)           │
            └───────────┴──────────────────────────────────┘
The only portable use of mknod() is to create a FIFO-special  file. If mode is not S_IFIFO 
or dev is not 0, the behavior of mknod() is unspecified.
其中, dev为设备值,取决于文件创建的种类,仅在创建设备文件时才会用到。
mknod()函数的前两个参数与mkfifo()的含义相同。

umask(0);
if(mknod("/tmp/fifo",S_IFIFO | 0666) == -1)
{
    perror("mkfifo error");
    exit(1);
}

共享映射区mmap

函数解析:

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
int munmap(void *addr, size_t length);

DESCRIPTION
mmap()  creates a new mapping in the virtual address space of the calling process. 
The starting address for the new mapping is specified in addr. The length argument 
specifies the length of the mapping. 
If addr is NULL, then the kernel chooses the address at which to create the mapping; 
this is the most portable method of creating a new mapping. If addr is not NULL, then 
the kernel takes it as a hint about where to place the mapping; on Linux, the mapping 
will be created at a nearby page boundary. The address of the new mapping is returned 
as the result of the call.
The contents of a file mapping, are initialized using length bytes starting at offset,
offset in the file (or other object) referred to by the file descriptor fd. offset must 
be a multiple of the page size as returned by sysconf(_SC_PAGE_SIZE).
The prot argument describes the desired memory protection of the mapping (and must not 
conflict with the open mode of the file). It is either PROT_NONE or the bitwise OR of 
one or more of the following flags:
        PROT_EXEC  Pages may be executed.
        PROT_READ  Pages may be read.
        PROT_WRITE Pages may be written.
        PROT_NONE  Pages may not be accessed.
The flags argument determines whether updates to the mapping are visible to other proc-
esses mapping the same region, and whether updates are carried through to the underlyi-
ng file. This behavior is determined by including exactly one of the following values 
in flags:
        MAP_SHARED      //共享映射区
        MAP_PRIVATE     //各自独占映射区
        MAP_ANON
        MAP_ANONYMOUS
fd argument is the file descriptor which points to the mapping in the virtual address space
offset argument must be the integral multiple of 4K.
RETURN VALUE
On success, mmap() returns a pointer to the mapped area. On error, the value MAP_FAILED 
(that is, (void *) -1) is returned, and errno is set to indicate the cause of the error. 
On success, munmap() returns 0. On failure, it returns -1, and errno is set to indicate 
the cause of the error (probably to EINVAL).

Summary for mmap:

  1. 创建映射区的过程中,隐含了一次对映射文件的一次读操作
  2. 当flag为MAP_SHARED时,,要求:营社区的权限应小于等于文件的打开权限(出于对映射区的保护),而MAP_PRIVATE则无所谓,因为mmap中的权限是对内存的限制。
  3. 映射区的释放与文件关闭无关,只要建立映射成功,文件可以立即关闭。
  4. 当映射文件大小为0时,不能创建映射区,因此,用于映射的文件必须要有实际大小,mmap常常出现总线错误,通常是由于共享文件存储空间大小引起的,因此,open()函数创建文件后,务必使用lseek()/fseek()/truncate() /ftruncate()函数来改变文件大小。
  5. munmap()传入的地址一定是mmap()的返回地址,要谨慎对该地址的运算操作
  6. 文件偏移量必须是4K的整数倍
  7. mmap()创建映射区出错率较高,一定要检查返回值,确保映射建立成功后再进行其他操作

使用mmap在有血缘关系的进程间通信

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{

    pid_t pid;
    int fd;
    int *a;
    fd = open("temp",O_CREAT|O_RDWR,0644);
    if(fd<0)
    {
        perror("open err");
        exit(1);
    }
    unlink("temp");
    ftruncate(fd,4);
    a =(int*) mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(a == MAP_FAILED)
    {
        perror("Map failed");
        exit(1);
    }
    close(fd);

    pid=fork();
    if(pid<0)
    {
        perror("fork error");
    }
    else if(pid == 0)
    {
        printf("This is the children process\n");
        *a = 1000;
        printf("a=%d\n",*a);
        sleep(1);
        printf("after 1S, changed to %d\n",*a);
    }	
    else
    {
        sleep(1);		
        *a = 2000;
        sleep(1);	
    }
    if(munmap(a,4) != 0)
    {
        perror("munmap failed");
    }
    return 0;
}

使用mmap在非血缘关系的进程间通信

/********************mmap_rd.c******************/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<string.h>

struct Life
{
    int age;
    char * value;
};

int main(void)
{
    pid_t pid;
    int fd;
    struct Life *sh;
    fd = open("temp",O_RDWR,0644);
    if(fd<0)
    {
        perror("open err");
        exit(1);
    }
    sh = (struct Life *) mmap(NULL,100,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(sh == MAP_FAILED)
    {
        perror("Map failed");
        exit(1);
    }
    close(fd);  

    sleep(1);
    printf("%d,%s\n",sh->age,sh->value);

    if(munmap(sh,4) != 0)
    {
        perror("munmap failed");
    }
    return 0;
}

/********************mmap_wr.c******************/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<sys/mman.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>

struct Life
{
    int age;
    char *value;
};

int main(void)
{
    pid_t pid;
    int fd;
    struct Life *rs;
    struct Life fac = {27,"life is more bitter than sweet\n"};

    sleep(1);
    fd = open("temp",O_CREAT|O_RDWR,0644);
    if(fd<0)
    {
        perror("open err");
        exit(1);
    }
    ftruncate(fd,400);

    rs =(struct Life *) mmap(NULL,100,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(rs == MAP_FAILED)
    {
        perror("Map failed");
        exit(1);
    }
    close(fd);

    memcpy(rs,&fac,sizeof(fac));
        
    if(munmap(rs,4) != 0)
    {
        perror("munmap failed");
    }
    return 0;
}

使用mmap进行匿名映射

Ubuntu下进行匿名映射有两种途径,一种是使用Ubuntu在flag中额外提供的宏名ANON或ANONYMOUS,另一种是使用所有UNIX及类UNIX下通用的设备文件(/dev/zero)

基于ANON

示例:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<string.h>

int main(void)
{

    pid_t pid;
    char *Ru;
    Ru =(char*) mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,0,0);
    if(Ru == MAP_FAILED)
    {
        perror("Map failed");
        exit(1);
    }

    pid=fork();
    if(pid<0)
    {
        perror("fork error");
    }
    else if(pid == 0)
    {
        sleep(1);
        printf("%s\n",Ru);
    }	
    else
    {		
        strcpy(Ru,"Different classes are created when you fall in love");
        sleep(2);	
    }
    if(munmap(Ru,4) != 0)
    {
        perror("munmap failed");
    }
    return 0;
}
可以看出,借助于ANON宏的匿名映射要简洁许多。
基于设备文件

示例:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{

    pid_t pid;
    int fd;
    int *a;
    fd = open("/dev/zero",O_CREAT|O_RDWR,0644);
    if(fd<0)
    {
        perror("open err");
        exit(1);
    }
    //不需要在进行文件扩展,/dev/zero可以写入任意多内容
    a =(int*) mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(a == MAP_FAILED)
    {
        perror("Map failed");
        exit(1);
    }
    close(fd);

    pid=fork();
    if(pid<0)
    {
        perror("fork error");
    }
    else if(pid == 0)
    {
        printf("This is the children process\n");
        *a = 1000;
        printf("a=%d\n",*a);
        sleep(1);
        printf("after 1S, changed to %d\n",*a);
    }	
    else
    {
        sleep(1);		
        *a = 2000;
        sleep(1);	
    }
    if(munmap(a,4) != 0)
    {
        perror("munmap failed");
    }
    return 0;
}

###strace命令
用法:strace ./可执行文件
对于使用mmap()进行IPC时易出现的段错误,使用strace命令更易于查找问题所在。

信号

发送信号传参

sigqueue函数对应kill函数,但可以在向指定进程发送信号的同时携带参数
函数解析:

#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);

DESCRIPTION
sigqueue() sends the signal specified in sig to the process whose PID is given in pid.  
The permissions required to send a signal are the same as for kill(2). As with kill(2), 
the null signal(0) can be used to check if a process with a given PID exists.

The value argument is used to specify an accompanying item of data(either an integer or 
a pointer value) to be sent with the signal, and has the following type:
    union sigval {
        int   sival_int;
        void *sival_ptr;
    };
If the receiving process has installed a handler for this signal using the SA_SIGINFO flag 
to sigaction(2), then it can obtain this data via the si_value field of the siginfo_t 
structure passed as the second argument to the handler. Furthermore, the si_code field of 
that structure will be set to SI_QUEUE.

RETURN VALUE
On success, sigqueue() returns 0, indicating that the signal  was  successfully queued to 
the receiving process. Otherwise, -1 is returned and errno is set to indicate the error.

Notice
向指定进程发送指定信号的同时,借助sigval可以携带数据。但如果是传地址,需要注意,不同进程间虚拟地
址空间各自独立,没有任何意义,但可以给本进程发。

信号捕捉函数传参

函数解析

#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

DESCRIPTION
The sigaction() system call is used to change the action taken by a process on receipt of 
a specific signal.(See signal(7) for  an overview of signals.) signum specifies the signal 
and can be any valid signal except SIGKILLand SIGSTOP.

If act is non-NULL, the new action for signal signum is installed from act. If oldact is 
non-NULL, the previous action is saved in oldact.
The sigaction structure is defined as something like:
    struct sigaction {
        void     (*sa_handler)(int);
        void     (*sa_sigaction)(int, siginfo_t *, void *);
        sigset_t   sa_mask;
        int        sa_flags;
        void     (*sa_restorer)(void);  //已弃用
    };
On some architectures a union is involved: do not assign to both sa_handler and sa_sigaction.

sa_handler specifies the action to be associated with signum and may be SIG_DFL for the 
default action, SIG_IGN to ignore this signal, or a pointer to a signal handling function. 
This function receives the signal number as its only argument.

If SA_SIGINFO is specified in sa_flags, then sa_sigaction\(instead of sa_handler) specifies 
the signal-handling function for signum. This function receives the signal number as its 
first argument, a pointer to a siginfo_t as its second argument and a pointer to a ucontext_t 
(cast to void *) as its third argument. (Commonly, the handler function doesn't make any use 
of the third argument.  See getcontext(3) for further information about ucontext_t.)

sa_mask specifies a mask of signals which should be blocked (i.e., added to the signal mask 
of the thread in which the signal handler is invoked) during execution of the signal handler.  
In addition, the signal which triggered the handler will be blocked, unless the SA_NODEFER 
flag is used.

sa_flags specifies a set of flags which modify the behavior of the signal. It is formed by 
the bitwise OR of zero or more of the following:
    SA_NODEFER
        Do not prevent the signal from being received from within its own signal handler. 
        This flag is meaningful only when establishing a signal handler. SA_NOMASK is an 
        obsolete,nonstandard synonym for this flag.
    SA_RESTART
        Provide behavior compatible with BSD signal semantics by making certain system calls 
        restartable across signals. This flag is meaningful only when establishing a signal 
        handler. See signal(7) for a discussion of system call restarting.
    SA_SIGINFO (since Linux 2.2)
        The signal handler takes three arguments, not one. In this case, sa_sigaction should 
        be set instead of sa_handler. This flag is meaningful only when establishing a signal 
        handler.
        The siginfo_t argument to sa_sigaction is a struct with the following fields:
            siginfo_t {
                int      si_signo;     /* Signal number */
                int      si_errno;     /* An errno value */
                int      si_code;      /* Signal code */
                int      si_trapno;    /* Trap number that caused
                                            hardware-generated signal
                                            (unused on most architectures) */
                pid_t    si_pid;       /* Sending process ID */
                uid_t    si_uid;       /* Real user ID of sending process */
                int      si_status;    /* Exit value or signal */
                clock_t  si_utime;     /* User time consumed */
                clock_t  si_stime;     /* System time consumed */
                sigval_t si_value;     /* Signal value */
                int      si_int;       /* POSIX.1b signal */
                void    *si_ptr;       /* POSIX.1b signal */
                int      si_overrun;   /* Timer overrun count;
                                            POSIX.1b timers */
                int      si_timerid;   /* Timer ID; POSIX.1b timers */
                void    *si_addr;      /* Memory location which caused fault */
                long     si_band;      /* Band event (was int in
                                            glibc 2.3.2 and earlier) */
                int      si_fd;        /* File descriptor */
                short    si_addr_lsb;  /* Least significant bit of address
                                            (since Linux 2.6.32) */
                void    *si_call_addr; /* Address of system call instruction
                                            (since Linux 3.5) */
                int      si_syscall;   /* Number of attempted system call
                                         (since Linux 3.5) */
               unsigned int si_arch;  /* Architecture of attempted system call
                                         (since Linux 3.5) */
           }

本地套接字

进程锁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值