乱读天书, 不求甚解
周祎骏的个人云笔记
Toggle navigation
乱读天书, 不求甚解
主页
Linux:系统配置
Linux:用户管理
Linux:优化排错
Linux:进程调度
Linux:文件系统
Linux:网络
Linux:系统服务
Linux:安全
Linux:内核
容器:Docker
容器:containerd
容器编排:Kubernetes
IAC:Terraform
大数据:Hadoop
大数据:Zookeeper
大数据:Hbase
消息队列:rsyslog
消息队列:kafka
数据库:MySQL
数据库:MongoDB
搜索引擎:Elasticsearch
时序数据库:OpenTSDB
网站服务:Nginx
编程:Bash
编程:Perl
编程:Python
编程:C
编程:JAVA
编程:Rust
版本控制:gitlab
知识管理:docusaurus
常用小工具
关于我
标签
C 2.08 Linux函数库unistd
2018-06-17 08:23:04
49
0
0
admin
> POSIX 的各种系统调用,文件系统操作(基于文件句柄),进程管理,触发新进程等。。。 #获取系统信息 **int gethostname(char *name, int namelen)**:获取hostname,成功返0,失败返回-1 ``` #include <stdio.h> #include <sys/param.h> #include <unistd.h> main() { char hostname[MAXHOSTNAMELEN]; if (gethostname(hostname,MAXHOSTNAMELEN) == 0 ) { printf("%s\n",hostname); } } ``` *** #IO/文件系统 >相比起getchar;read,write函数不适用缓存。 事实上getchar的实现是调用了read函数读了一堆数据,然后自己维护缓存。 **ssize_t read(int fd,void *buf,size_t count)**: 从文件句柄中一次读取最多count 个字符,写到buf 字符串中,返回读取成功的数量,返回-1表示失败 **ssize_t write(int fd,void *buf,size_t count)**: 往句柄写入buf中一次最多写入count个字符,返回写入的数量,失败返回-1 ``` #include <stdio.h> #include <fcntl.h> #include <unistd.h> main(int argc,char *argv[]) { int fd_read = open(argv[1],O_RDONLY,0); int fd_write = open("./log",O_WRONLY|O_APPEND|O_CREAT,00644); char c[BUFSIZ]; int n; while ((n=read(fd_read,c,BUFSIZ)) > 0) write(fd_write,c,n); close(fd_read); close(fd_write); } ``` *** **long lseek(int fd,long offset,int origin)**:从origin 这个位置开始,改变某个文件的指针offset 个位置,返回新位置,失败返回-1 origin: SEEK_SET/0:开始位置 SEEK_CUR/1: 当前位置 SEEK_END/2:结束位置 *** **int truncate(char *filename,size_t len)** 修改文件长度,往小了改会丢失文件后面的数据。 **int ftruncate(int fd,size_t len)** 修改文件长度,往小了改会丢失文件后面的数据。 *** **int access(const char *filename,int mode)** 检查文件是否有权限 mode: R_OK 可读 W_OK 可写 X_OK 可执行 F_OK 存在 *** **int dup(int oldfd)**:复制一个新的文件句柄,失败返回-1 **int dup2(int oldfd,int newfd)**: 复制一个新的文件句柄且指定句柄数字,错误返回-1 *** **int link(const char *filepath,const char * newfilepath)**创建一个链接 **int unlink(const char *filepath)** 删除文件 有一种玩法是创建临时文件以后立刻unlink,这样进程还拥有文件句柄,可以操作,即使程序奔溃,也不会有临时文件留下来。 **int symlink(const char *realfile, const char *symlinkfile)** 创建软链接 **int readlink(const char *filepath,char *buf, int bufsize)** 读取软链接的类容 *** **int chown(const char *filename, uid_t owner,gid_t group)** 失败返回-1 **int fchown(int fd, uid_t owner,gid_t group)** 失败返回-1 **int lchown(const char *filename, uid_t owner, gid_t group)** 失败返回-1 如果是软链接的话,lchown改的是软连接本身,其它改软链接指向的文件。 **int mkdir(const char *filename, mode_t mode)** 建立目录。失败返回-1 **int rmdir(const char *filename)** 删除目录,失败返回-1 *** **int chdir(const char *pathname)** 更改当前目录,失败返回-1 **int fchdir(int fd)** 更改当前目录,失败返回-1 **char *getcwd(char *pathbuf,size_t size)** 获取绝对路径,size是path的长度,建议用PATH_MAX+1 *** **void sync(void)** 将所有修改过的块排入写队列 **int fsync(int fd)** 把某文件立刻写到磁盘上 *** ##多路转接 **int select(int maxfdp1, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *tvptr)**:等待tvptr时间,返回准备好的文件句柄数量,并把准备好的句柄写进3个fd_set结构 * tvptr为NULL表示永远等下去 ``` struct timeval{ long tv_sec; // seconds long tv_usec; // and microseconds }; ``` * 用以下宏生成fd_set,那些准备好的句柄可以在运行过select 函数后运行FD_ISSET测试 ``` FD_CLR(int fd,fd_set* set);用来清除描述词组set中相关fd的位 FD_ISSET(int fd,fd_set* set);用来测试描述词组set中相关fd的位是否为真 FD_SET(int fd,fd_set* set);用来设置描述词组set中相关fd的位 FD_ZERO(fd_set* set); 用来清除描述词组set的全部位 ``` * maxfdp1 代表句柄的最大值+1,可以用FDSETSIZE,一般建议手动指定,因为事实上一般用不了这么大 ``` int fd1,fd2; fd_set readset; FD_ZERO(&readset); FD_SET(fd1, &readset); FD_SET(fd2, &readset); select(3, &readset, NULL, NULL, NULL); if(FD_ISSET(1, &readset)) { read........ } ``` *** #关于进程 **pid_t getpid(void)**:获得进程pid **pid_t getppid(void)**: 获得进程父进程pid **uid_t getuid(void)**: 获得进程实际用户id **uid_t geteuid(void)**: 获得进程有效用户id **gid_t getgid(void)**: 获得进程实际组id **gid_t getegid(void)**: 获得进程有效组id ``` #include <sys/types.h> #include <unistd.h> #include <stdio.h> main() { printf("%d\n",getuid()); } ``` **pid_t getpgrp(void)**:获得进程的进程组id **int setpgid(pid_t pid, pid_t pgid)**: 将pid的进程组id设置为pgid,pid是0就使用调用进程的pid > 如果要修改子进程的组id: 父进程fork子进程后调用此函数修改子进程的进程组id,同时子进程也调用这个函数修改自己的进程组id,两次必然有一次是冗余的,但是可以有效保证父子进程下一步操作前子进程都进入了新的进程组,可以有效避免竞态条件 **pid_t setsid(void)**:创建一个新的会话,同时创建一个新的进程组,此进程为组长,成功返回进程组id,失败返回-1(如果该进程已经是一个进程组的组长的话会报错) **int setuid(uid_t uid)**:设置真实uid **int setgid(gid_t gid)**:设置真实gid **int seteuid(uid_t uid)**: 设置有效uid **int setegid(gid_t gid)**: 设置有效gid **int setreuid(uid_t ruid, uid_t euid)**: 设置真实uid,有效uid **int setregid(gid_t rgid, gid_t egid)**: 设置真实gid,有效gid ##触发新进程 **pid_t fork(void)**: 子进程返回0,父进程返回子进程id,失败返回-1 >vfork.h中的vfork函数与fork类似,但是不复制子进程的地址空间,同时保证子进程先运行 出现vfork是因为以前的fork没有做到写时拷贝,而大量的fork紧接着exec导致了资源的大量浪费。 很多Linux文档建议现在新的代码不要使用vfork了。 ###用wait.h获取子进程状态 **pid_t wait(int *status)**: 阻塞的等待子进程结束,返回子进程id,退出状态会被写入status(如不关心退出状态可用NULL), 失败返回-1 status某些位表示退出状态,某些位表示信号,有一个位表示是否产生core文件,用以下方式判断: ``` WIFEXITED(status) 如果正常退出就为真,用WEXITSTATUS(status) 获得退出状态 WIFSIGNALED(status) 如果异常终止则为真,用WTERMSIG(status) 获得终止信号,用WCOREDUMP(status) 判断是都产生core 文件 WIFSTOPPED(status) 如果当前子进程为暂停状态则为真,用WSTOPSIG(status) 获得暂停进程的信号(这个一般是waitpid用,因为它可以获取暂停进程的状态) ``` ```c #include <sys/types.h> #include <unistd.h> #include <sys/wait.h> #include <stdlib.h> #include <stdio.h> main() { pid_t pid; int status; if ((pid=fork()) <0 ) { printf("fork failed\n"); exit(1); } else if (pid == 0) //child { sleep(100); exit(3); } if (wait(&status) != pid) { printf("wait failed\n"); exit(1); } if (WIFEXITED(status)) printf("exit %d\n",WEXITSTATUS(status)); else if (WIFSIGNALED(status)) printf("signal %d\n",WTERMSIG(status)); } ``` **pid_t waitpid(pid_t pid, int *status, int options)**: ``` pid的选择: -1 : 等待任一子进程 >0 : 等待指定pid的进程 0 :等待组id与调用进程的组id相同的任一子进程 <-1: 等待组id等于pid绝对值的任一子进程 ``` ``` options的选择: WNOHANG: 无子进程结束就不阻塞,返回值为0 WUNTRACED: 如子进程暂停,且状态之前没有报告过,则返回其状态 ``` *此外wait3() wait4()函数还会报告子进程使用的资源* ###用exec调用其它程序 **int execl(const char *pathname, const char *arg 0, ... ,(char *) 0 )** **int execv(const char *pathname, char *const argv[])** **int execle(const char *pathname, const char *arg 0, ... ,(char *) 0, char *const envp[])** **int execve(const char *pathname , char *const argv [], char *const envp[] )** **int execlp(const char *filename, const char *arg 0, ... , (char *) 0 )** **int execvp(const char *filename, char *const argv[])** 以上exec函数有如下特点 * 名字中有l的代表list,(execl,execle,execlp) 用一系列const char *arg 表示所有参数,用空指针(char *)0 结尾 * 名字中有v的代表vector,(execv,execve,execvp)用一个指针数组表示所有参数,用NULL结尾 * 名字有p的代表path,(execlp,execvp)如果提供的程序不含路径的话,会根据环境变量PATH来寻找程序 * 名字有e的代表env, 提供envp[] 来定制环境变量,每一个元素都是XX=XX的字符串,最后一个元素是NULL *** #关于信号 **unsigned int alarm(unsigned int seconds)**: seconds秒后给自己发送SIGALRM信号 **int pause(void)**:挂起程序直到接收到一个信号 ``` #include <unistd.h> #include <stdio.h> main(int argc,char *argv[]) { int n = atoi(argv[1]); alarm(n); pause(); } ``` *** #管道 ##父子进程管道 **int pipe(int filedes[2])**:在数组中返回两个文件句柄,1为读,2为写。然后fork子进程,想读的进程关闭写句柄,想写的进程关闭读句柄。成功返回0,失败返回-1。 管道的缓冲受PIPE_BUF限制,如果一次写入内容超过这个值,会分多次写入,就有可能与别的写入数据穿插 ``` #include <unistd.h> #include <stdio.h> #include <sys/wait.h> main() { int n,pid,p[2]; pipe(p); char line[99]; pid = fork(); if(pid>0) //parent { close(p[0]); write(p[1],"1234567",7); int status; wait(&status); } else { //child close(p[1]); n=read(p[0],line,99); printf("%s\n",line); } } ``` *** #其它 **void _exit (int status)**: 立刻结束,与exit()的区别是_exit不会先关闭(刷新)所有文件句柄再调用内核关闭进程,所以如果句柄中有缓存的话可能会丢失。 **unsigned int sleep(unsigned int seconds)**:返回0或未睡的秒数,(sleep可能是由alarm实现的,所以可能会和alarm函数有冲突)
上一篇:
C 2.07 Linux函数库fcntl
下一篇:
C 2.081 Linux函数库dirent.h
文档导航