进程间传递文件描述符

进程间传递文件描述符

进程间传递文件描述符
进程间传递描述符1
进程间传递描述符2

#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

enum {
    E_INFO = 0,
    E_NOTICE,
    E_DEBUG,
    E_WARRN,
    E_ERROR,
};

#define LOG_LEVEL 0

#ifdef LOG_LEVEL 
#define LOG_OUT(level, color, tag, str, ...)  (\
                            (tag >= LOG_LEVEL)? \
                             fprintf(stdout, color tag "%s:%d:"\
                                    str "\e[0m", __FILE__, __LINE__, \
                                    ##__VA_ARGS__):0); 
#else
#define LOG_OUT(tag, color, str, ...)  
#endif

#define INFO(a, ...) LOG_OUT(E_INOF, "\e[1;32m", "[INFO]", a, ##__VA_ARGS__)
//yellow
#define NOTICE(a, ...) LOG_OUT(E_NOTICE, "\e[1;43m", "[NOTICE]:", a, ##__VA_ARGS__)
//green 
#define DEBUG(a, ...) LOG_OUT(E_DEBUG, "\e[1;42m", "[DEBUG]:", a, ##__VA_ARGS__)
//blue
#define WARRN(a, ...) LOG_OUT(E_WARRN, "\e[1;34m", "[WARRN]:", a, ##__VA_ARGS__)
//red
#define ERROR(a, ...) LOG_OUT(E_ERROR, "\e[1;41m", "[ERROR]:", a, ##__VA_ARGS__)

#define SIG SIGUSR1

static int
do_sendmsg(int sock, const char *sockpath, int fd)
{
    struct msghdr msg;
    struct cmsghdr *cmsghdr;
    struct iovec iov[1];
    ssize_t nbytes;
    int i, *p;
    char buf[CMSG_SPACE(sizeof(int))];
    发送的内容
    char *send_content = "send file descriptor.\n";
 
    iov[0].iov_base = send_content;
    iov[0].iov_len = strlen(send_content) + 1;
    memset(buf, 0x0b, sizeof(buf));
    发送的附加信息 开头格式化为 struct cmsghdr结构
    下面的参数为发送文件描述符时需要的参数
    cmsghdr = (struct cmsghdr *)buf;
    cmsghdr->cmsg_len = CMSG_LEN(sizeof(int));
    cmsghdr->cmsg_level = SOL_SOCKET;
    cmsghdr->cmsg_type = SCM_RIGHTS;
    msg.msg_name = NULL;
    msg.msg_namelen = 0;
    msg.msg_iov = iov;
    msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
    msg.msg_control = cmsghdr;
    msg.msg_controllen = CMSG_LEN(sizeof(int));
    msg.msg_flags = 0;
    p = (int *)&(CMSG_DATA((struct cmsghdr *)buf));
    *p = fd;
    INFO("sendmsg: %d\n", fd);
 	发送消息 返回的发送字节数为iov内部发送的内容长度
    nbytes = sendmsg(sock, &msg, 0);
    if (nbytes == -1)
        return (1);
    
    INFO("sendmsg length: %ld\n", nbytes);
 
    return (0);
}
 
static int
server_main(pid_t ppid, const char *sockpath)
{
    struct sockaddr_un u_addr;
    struct sockaddr_un *addr = NULL;
    int error, fd, s, sock;
    const char *filepath = "fd_passing.txt";
 
    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock == -1) {
        ERROR("fail to create socket\n");
        exit(-1);
    }

    addr = &u_addr;
    addr->sun_family = AF_UNIX;
    strncpy(addr->sun_path, sockpath, sizeof(addr->sun_path));
    INFO("sock file path %s\n",  addr->sun_path);

    if (bind(sock, (struct sockaddr *)addr, SUN_LEN(addr)) != 0) {
        ERROR("bind fail: %s\n", strerror(error));
        exit(-1);
    }
    if (listen(sock, 0) != 0) {
        ERROR("set listen status fail.\n");
        goto fail;
    }
    if (kill(ppid, SIGUSR1) != 0) {
        ERROR("fail to send SIGUSR1 signal\n");
        goto fail;
    }

    if ((s = accept(sock, NULL, 0)) < 0) {
        ERROR("accept fail: %s\n", strerror(error));
        goto fail;
    }
	打开文件获得文件描述符fd
    if ((fd = open(filepath, O_WRONLY | O_CREAT, 0644)) == -1) {
        ERROR("open file fail: %s\n",  strerror(error));
        goto fail;
    }
 
    if (do_sendmsg(s, sockpath, fd) != 0) {
        ERROR("do_sendmsg fail\n");
        goto fail;
    }
 
    if (close(fd) != 0) {
        ERROR("close fd fail\n");
        goto fail;
    }

    if (close(s) != 0) {
        ERROR("close s fail\n");
        goto fail;
    }
    if (close(sock) != 0) {
        ERROR("close sock\n");
        goto fail;
    }
    if (unlink(sockpath) != 0) {
        ERROR("unlink sockpath fail.\n");
        goto fail;
    }
 
    return (0);
 
fail:
    error = errno;
    unlink(sockpath);
 
    /* NOTREACHED */
    return (1);
}
 
static int
do_recvmsg(int sock)
{
    struct msghdr msg;
    struct cmsghdr *cmsghdr;
    struct iovec iov[1];
    FILE *fp;
    ssize_t nbytes;
    int i, *p;
    char buf[CMSG_SPACE(sizeof(int))];
    接收数据的buffer
    char recv_buf[128] = {0};
 
    iov[0].iov_base = recv_buf;
    iov[0].iov_len = sizeof(recv_buf);
    memset(buf, 0x0d, sizeof(buf));
    cmsghdr = (struct cmsghdr *)buf;
    cmsghdr->cmsg_len = CMSG_LEN(sizeof(int));
    cmsghdr->cmsg_level = SOL_SOCKET;
    cmsghdr->cmsg_type = SCM_RIGHTS;
    msg.msg_name = NULL;
    msg.msg_namelen = 0;
    msg.msg_iov = iov;
    msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
    msg.msg_control = cmsghdr;
    msg.msg_controllen = CMSG_LEN(sizeof(int));
    msg.msg_flags = 0;
 	接收到的数据长度为iov收到的内容长度
    nbytes = recvmsg(sock, &msg, 0);
    if (nbytes == -1)
        return (1);

    INFO("recvmsg length: %ld\n", nbytes);
 
    p = (int *)&(CMSG_DATA((struct cmsghdr *)buf));
    INFO("recvmsg fd: %d\n", *p);
    fp = fdopen(*p, "w");
    fprintf(fp, (char *)iov[0].iov_base);
    fclose(fp);
 
    return (0);
}
 
static int
client_main(pid_t pid, const char *sockpath)
{
    struct sockaddr_storage sockaddr;
    struct sockaddr_un *addr;
    sigset_t set;
    int sig, sock, status;
    const char *signame;
    // clean the signal set
    if (sigemptyset(&set) != 0) {
        ERROR("sigemptyset fail.\n");
        exit(-1);
    }

    if (sigaddset(&set, SIGUSR1) != 0 ) {
        ERROR("sigaddset fail.\n");
        exit(-1);
    }

    if (sigwait(&set, &sig) != 0) {
        ERROR("sig wait fail.\n");
    }

    if (sig != SIGUSR1) {
        INFO("recv signal is not SIGUSR1\n");
        return 2;
    } 
    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock == -1) {
        ERROR("fail to create socket\n");
        exit(-1);
    }
    addr = (struct sockaddr_un *)&sockaddr;
    addr->sun_family = AF_UNIX;
    strncpy(addr->sun_path, sockpath, sizeof(addr->sun_path));
    if (connect(sock, (struct sockaddr *)addr, SUN_LEN(addr)) != 0) {
        ERROR("connect fail.\n");
        exit(-1);
    }
 
    if (do_recvmsg(sock) == -1)
        return (3);
 
    if (close(sock) == -1) {
        ERROR("fail to close sock.\n");
        exit(-1);
    }
    等待所有子进程结束
    if (waitpid(pid, &status, 0) < 0) {
        ERROR("waitpid fail.\n");
        exit(-1);
    }
    if (!WIFEXITED(status))
        return (4);
    if (WEXITSTATUS(status) != 0)
        return (32 + WEXITSTATUS(status));
 
    return (0);
}
 
int
main(int argc, const char *argv[])
{
    sigset_t set;
    pid_t pid, ppid;
    char sockpath[MAXPATHLEN];
 
    屏蔽掉所有的信号
    if (sigfillset(&set) != 0) {
        ERROR("sigfillset fail.\n");
        exit(-1);
    }
    if (sigprocmask(SIG_BLOCK, &set, NULL) != 0) {
        ERROR("sigprocmask fail.\n");
        exit(-1);
    }

 
    ppid = getpid();
    snprintf(sockpath, sizeof(sockpath), "%d.sock", ppid);
 
    pid = fork();
    switch (pid) {
    case -1:
        ERROR("fork fail.\n");
        exit(-1);
    case 0:
    子进程跑server
        return (server_main(ppid, sockpath));
    default:
        break;
    }
 	父进程跑client
    return (client_main(pid, sockpath));
}

运行结果

在这里插入图片描述
本来进程间是不能传递描述符的,但是内核使用unix domain机制专门支持了这个功能。所谓的传递描述符其实是使得接收进程重新创建了一个file描述结构指向相同的文件node。此时实际接收到的fd index是不同的。
从上运行结果中可以看到,发送和接收到的长度都指的是iov 列表中的数据内容,不包含附加信息。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值