xargs 可以将 stdin 中以空格或换行符进行分隔的数据,形成以空格分隔的参数(arguments),传递给其他命令。因为以空格作为分隔符,所以有一些文件名或者其他意义的名词内含有空格的时候,xargs 可能会误判。简单来说,xargs 是给其他命令传递参数的一个过滤器,是构建单行命令的重要组件之一。
传送文件描述符是高并发网络服务编程的一种常见实现方式。Nebula 高性能通用网络框架即采用了UNIX域套接字传递文件描述符设计和实现 https://github.com/Bwar/Nebula 在Unix 域套接字概述一节中介绍了什么是 Unix 及相关函数,本文将继续介绍 Unix 域套接字在进程间传递描述符的应用。 在进程间传递打开的描述符时通常会采用如下两种方法: (1)fork 调用返回后,子进程自动共享父进程的所有打开的描述符。 (2)exec 调用执行后,所有描述符通常保持打开状态不变。 第一种方法中,进程先打开一个描述符,再调用 fork,之后父进程关闭这个描述符,子进程则处理该描述符。这样一个打开的描述符就从父进程传递到子进程。不过有时候可能想让子进程打开一个描述符并把他传递给父进程。 使用 Unix 域套接字,可以从一个进程向任一其他进程传递打开的描述符,而无需这两个进程之间存在亲缘关系。这种技术要求首先在这两个进程之间创建一个 Unix 域套接字,然后使用 sendmsg 跨这个套接字发送一个特殊消息。该消息由内核来专门处理,会把打开的描述符从发送进程传递到接收进程。 使用 Unix 域套接字在两个进程之间传递描述符涉及的步骤如下。 (1)创建一个字节流或数据报的 Unix 域套接字。如果目标是让子进程将打开的描述符传递回父进程,则父进程可以预先调用 socketpair 函数创建一个可用于在父子进程之间交换描述符的流管道。如果进程间没有亲缘关系,则服务器进程必须先创建一个 Unix 域字节流套接字(也可以是 Unix 域数据报套接字,不过这没什么好处,而且数据报还存在被丢弃的可能性),然后 bind 一个路径名到该套接字,以允许客户进程 connect 到套接字,发送一个打开某个描述符的请求。 (2)发送进程通过调用返回描述符的任一 Unix 函数(如 open、pipe、mkfifo、socket 和 accept,可以在进程之间传递的描述符不限类型)打开一个描述符。 (3)发送进程创建一个 msghdr 结构,其中含有待传递的描述符。POSIX 规定描述符作为辅助数据(msghdr 结构的 msg_control 成员,见辅助数据)发送。发送进程调用 sendmsg 跨来自步骤 1 的 Unix 域套接字发送该描述符。至此,称这个描述符“在飞行中(in flight)”。即使发送进程在调用 sendmsg 之后但在接收进程调用 recvmsg 之前关闭了该描述符,对于接收进程它仍然保持打开状态。发送一个描述符会使该描述符的引用计数加一。 (4)接收进程调用 recvmsg 在来自步骤 1 的 Unix 域套接字上接收这个描述符。这个描述符在接收进程中的描述符号不同于它在发送进程中的描述符号是正常的,因为传递一个描述符并不是传递一个描述符号,而是涉及在接收进程中创建一个新的描述符,该描述符和发送进程中飞行前的那个描述符指向内核中相同的文件表项。
参考文献:1) 《Unix网络编程》
2) http://book.51cto.com/art/200912/168560.htm
最近学习了使用Unix域套接字在进程间传递文件描述符,仿照参考资料,自己也写了简单的程序来实践这种技术。
其他不多说了,具体理论知识参见参考资料,开始我自己的程序介绍(在OpenSolaris 2009.06平台上测试):
1 程序作用说明:父进程,子进程以及另外一个进程向同一个文件的文件描述符向这个文件中写内容。 具体如下: 1)父进程指定要打开的文件名,打开权限,打开模式; 2)fork一个子进程; 3)子进程调用execl函数来执行程序openfile:该新程序取得指定文件的文件描述符;向指定文件中写入“openfileprog write test”;向父进程返回该文件描述符; 4)父进程收到该文件描述符后,向文件中写“paraent process write ppp”; 5)父进程作为server端建立域socket等待客户端进程连接; 6)客户端进程连接父进程; 7)父进程向该客户端进程返回从子进程得到的文件描述符; 8)客户端进程收到该文件描述符后使用它在文件中写“this is client process ccc”;
其中父子进程传递文件描述符通过建立的一对套接字管道,父进程和客户端进程传递文件描述符通过Unix域套接字。
2 具体代码说明:
1) 首先看openfile程序,这时子进程通过调用execl执行的。调用方法如下: execl(“./openfileprog”, “openfileprog”, permit, mode, argsockfd, (char *)NULL); 其中参数1: openfile程序路径; 参数2: openfile程序名; 参数3: 待打开文件的权限; 参数4: 待打开文件模式; 参数5: 父进程建立的一对套接字管道的其中之一;
作为openfile程序,主要按照execl传的参数,打开指定文件,取得文件描述符;向该文件中写入内容;然后调用func_send_fd函数通过argsockfd把取得的文件描述符传给父进程。该程序代码如下:
int main(int argc, char argv[]) / openfileprog / { int i, fd, ret; ssize_t size; size_t buflen; char data[10]; char buf[] = “openfileprog write test\n”; / 向文件中写入的内容 / / execl(“./openfileprog”, permit, mode, argsockfd, (char *)NULL); */ fd = -1; if((fd = open(“./file”, atoi(argv[1]), atoi(argv[2]))) < 0) { printf(“in openfileprog, open failed\n”); exit(-1); }
size = -1; buflen = sizeof(buf); if((size = write(fd, buf, buflen)) <= 0) { printf(“in openfileprog, write failed\n”); }
/* 把设定的data信息也传给父进程 */ ret = ‘a’; for(i = 0; i < sizeof(data); i++, ret++) { data[i] = ret; } data[sizeof(data) - 1] = ‘\0’;
ret = -1; if(0 > (ret = func_send_fd(atoi(argv[3]), fd, data, 10))) { printf(“in openfileprog, func_send_fd failed\n”); }
close(fd);
return 0; }
func_send_fd函数负责把取得的文件描述符传出去:
int func_send_fd(int send_sock, int send_fd, void *data, int bytes) { struct msghdr msghead; struct iovec passdata[1]; int ret;
/* 填充msghead结构 */ msghead.msg_accrights = (caddr_t)&send_fd; msghead.msg_accrightslen = sizeof(send_fd);
msghead.msg_name = NULL; msghead.msg_namelen = 0; passdata[0].iov_base = data; passdata[0].iov_len = bytes;
msghead.msg_iov = passdata; msghead.msg_iovlen = 1;
/* 发送信息 */ if(0 > (ret = sendmsg(send_sock, &msghead, 0))) { printf(“in func_send, send_fd is %d, sendsock is %d, sendmsg failed,errno is %d\n”, send_fd,send_sock,errno); return -1; }
return ret; }
在上述两个函数之前,加上以下必要头文件和宏:
#include #include #include #include #include #include #include #define SLEEPTIME 3 #define ARGLEN 20
以上作为一个c文件。
2)然后看父进程代码
下面是父进程程序:
int main(int argc, char *argv) { int status,sockfd[2]; char permit[ARGLEN]; char mode[ARGLEN]; char argsockfd[ARGLEN]; int recvfd; char data[20]; int bytes; int ret,i; ssize_t size; int buflen; pid_t pid,chldpid;
/* 以下几行是使用域套接字必要变量 */
int fdsock, fdaccept;
struct sockaddr_un addr_server;
int len;
const char path[] = “/export/home/temp/test/other_prog/fengxianzhong”;
/* 以下是父进程写入文件的内容 */
char buf[] = “paraent process write ppp\n”;
/* 父进程同时向处理向client发送的数据 */ char datasend[] = “send by myopen\n”;
memset(permit, ‘\0’, sizeof(permit)); memset(mode, ‘\0’, sizeof(mode)); memset(argsockfd, ‘\0’, sizeof(argsockfd)); memset(data, ‘\0’, sizeof(data));
printf(“now it is parent process,now will fork a child process\n”); sleep(SLEEPTIME);
/* 设置文件权限和打开模式 */ snprintf(permit, sizeof(permit), “%d”,PERMIT); snprintf(mode, sizeof(mode), “%d”,MODE); // printf(“in myopen %s, %s\n”, permit, mode);
/* 建立和子进程通信的socket套接字管道 */ ret = socketpair(AF_UNIX,SOCK_STREAM,0,sockfd); if(0 > ret) { printf(“socketpair failed,errno is %d \n”,errno); }
/* fork 子进程 / if(0 == (chldpid = fork())) / child process */ { printf(“now it is child process, sendsock is %d\n”,sockfd[1]); close(sockfd[0]); snprintf(argsockfd, sizeof(argsockfd), “%d”, sockfd[1]);
/* 子进程中执行新程序openfile */ execl(“./openfileprog”, “openfileprog”, permit, mode, argsockfd, (char *)NULL); printf(“execl failed, perimit is %s, mode is %s\n”,permit, mode); exit(-1); }
/* paraent process start to write the file opened by child process */
printf(“now it is parent process\n”); close(sockfd[1]); bytes = sizeof(data);
/* 等待子进程结束 / pid = wait(&status); if((status = WEXITSTATUS(status)) == 0) / child process terminate */ { printf(“child %d process terminate,now parent will write file …\n”,pid); }
/* 从子进程取得文件描述符 / recvfd = -1; // printf(“recv sock is %d\n”, sockfd[0]); ret = func_recv_fd(sockfd[0], &recvfd, data, bytes); if(ret < 0) { printf(“paraent recv failed\n”); } / else { printf(“fd %d paraent recv %d bytes data is %s\n”, recvfd,strlen(data),data); } */
/* 向文件写入数据 */ size = -1; buflen = sizeof(buf);
if((size = write(recvfd, buf, buflen)) <= 0) { printf(“in openfileprog, write failed\n”); }
/* 父进程作为server建立域套接字,等待client连接 */ printf(“parent write over! Accept other process ……\n”);
fdsock = socket(AF_UNIX, SOCK_STREAM, 0); if(-1 == fdsock) { printf(“myopen creat socket error!errno is %d\n”, errno); }
unlink(path);
memset(&addr_server, 0, sizeof(addr_server)); addr_server.sun_family = AF_UNIX; strcpy(addr_server.sun_path, path); len = sizeof(struct sockaddr_un);
ret = bind(fdsock, (struct sockaddr*)&addr_server, len); if(-1 == ret) { printf(“in myopen bind error, errorno is %d\n”,errno); close(fdsock); unlink(path); }
ret = listen(fdsock,1); if(-1 == ret) { printf(“in myopen listen error, errorno is %d\n”,errno); close(fdsock); unlink(path); }
fdaccept = accept(fdsock, (struct sockaddr*)&addr_server, &len); if(-1 == ret) { printf(“in myopen accept error, errorno is %d\n”,errno); close(fdsock); unlink(path); }
/* 向已经连接的client传递该文件描述符 */ ret = func_send_fd(fdaccept, recvfd, datasend, sizeof(datasend)); if(0 > ret) { printf(“in myopen, func_send_fd failed\n”); }
printf(“send fd over! Will sleep 10s \n”);
sleep(10);
exit(0);
}
func_recv_fd函数负责从子进程接受文件描述符:
int func_recv_fd(int recv_sock, int *recvfd, void *data, int bytes) { struct msghdr msghead; struct iovec passdata[1]; int ret; int temp; int newfd;
struct cmsghdr *msgptr1;
struct cmsghdr *msgptr = NULL;
memset(&msghead, 0, sizeof(msghead));
/* 同func_send_fd ,填充所需要的结构 */ msghead.msg_accrights = (caddr_t)&newfd; msghead.msg_accrightslen = sizeof(recvfd);
msghead.msg_name = NULL; msghead.msg_namelen = 0; passdata[0].iov_base = data; passdata[0].iov_len = bytes;
msghead.msg_iov = passdata; msghead.msg_iovlen = 1;
/* 接收信息(文件描述符 )*/ if(0 > (ret = recvmsg(recv_sock, &msghead, 0))) { printf(“in func_recv_fd, recvmsg failed\n”); return -1; }
if(msghead.msg_accrightslen == sizeof(recvfd)) { recvfd = newfd; / 文件描述符 */ }
return ret; }
其中父进程向client进程发送文件描述符也使用了func_send_fd函数,该函数在此c文件中重新写了一遍;其实没有必要这样重复。我们可以把它作为一个库来使用;不过这里暂且这样使用。函数代码参考上面所写的。
在这个c文件中还要加入以下头文件和宏定义:
#include #include #include #include #include #include #include #include #include #include #define SLEEPTIME 3 #define ARGLEN 20 #define MODE S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IROTH /* -rwxr–r– / #define PERMIT O_RDWR | O_APPEND | O_CREAT / if the file not exit ,creat it , data written to it append */
3)最后看client进程代码:
int main() { int sockfd, recvfd,ret;
struct sockaddr_un addr_client;
int length,buflen;
char data[10];
ssize_t size;
const char path[] = “/export/home/temp/test/other_prog/fengxianzhong”;
char buf[] = “this is client process ccc\n” ;
sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if(-1 == sockfd) { printf(“client creat socket error!errno is %d\n”, errno); }
addr_client.sun_family = AF_UNIX; strcpy(addr_client.sun_path, path); length = sizeof(addr_client.sun_family) + sizeof(addr_client.sun_path);
https://github.com/emicklei/go-restful-swagger12 https://github.com/emicklei/go-restful-openapi https://github.com/emicklei/go-restful https://github.com/ant0ine/go-json-rest/ go-restful库:https://github.com/emicklei/go-restful
公开密钥基础建设(英语:Public Key Infrastructure,缩写:PKI),又称公开密钥基础架构、公钥基础建设、公钥基础设施、公开密码匙基础建设或公钥基础架构,是一组由硬件、软件、参与者、管理政策与流程组成的基础架构,其目的在于创造、管理、分配、使用、存储以及撤销数字证书。