LinuxC函数
前置知识
文件描述符
在进程的PCB中,有专门存储文件描述符的位置。
在PCB中,有一个*files 的指针,这个指针指向一个名叫 files_struct 的指针数组,这里这个指针数组里存储的就是指向不同文件的指针,既然是一个数组,那就可以通过下标进行数组元素的访问,所以,这里数组的下标的值,就是文件描述符的值。
我们如果在进程中打开文件的时候,系统就会在这个进程的files_struct中添加进对应的文件的指针,然后返回这个文件指针对应的文件描述符,也就是这个文件的下标。我们在进行文件读写操作的时候,如果我们调用的是系统提供的读写操作接口的话,就势必需要使用文件描述符进行操作
文件描述符在分配的时候,并不是按照你打开的文件的顺序进行分配的,而是,从目前能分配的最小的文件描述符进行分配,比如说:你如果关闭了这个进程的标准输入和标准输出,这两个文件的文件描述符就会被释放,然后,再次打开一个新的文件的时候,就会给这个文件分配一个0的文件描述符,占有标准输入的文件描述符的位置。
1.pipe2(int pipefd[2], int flags)
传入数组两个元素被赋予管道两端的文件描述符,通过描述符进行读写操作。
pipe用于与fork出的子进程通信,父子进程各选一个端口close掉,就变成了单向pipe,并且是同步的
fd[0]:读管道,fd[1]:写管道。
2.dup2(int oldfd, int newfd, int flags)
作用:使得新fd与旧fd操作相同的文件,例如dup2(-1,1),会使得对标准输出流的输入进入/dev/null
fd 0,1,2分别对应标准输入,标准输出,错误流
3.setsid()
- 前置知识
session
举个例子,使用xshell登录服务器就是创建了一个session
通过这个shell session启动一个进程,这个进程作为父进程启动了其他子进程,那么就相当于启动了一个进程组
我们同时在后台启动多个进程组,就会得到如上图所示的情况。
setsid
setsid 会创建一个新的 session,它的目的是让进程在后台执行命令,实现方式就是让命令进程运行在一个新的与终端脱离的 session 中。看下面的示例
root@dggphisprb19134:/usr1/zhd/learn/learn-c/afltest# setsid sleep 1000
root@dggphisprb19134:/usr1/zhd/learn/learn-c/afltest# ps -o pid,ppid,pgid,sid,tty,comm
PID PPID PGID SID TT COMMAND
7784 26122 7784 26122 pts/4 ps
26122 26055 26122 26122 pts/4 bash
root@dggphisprb19134:/usr1/zhd/learn/learn-c/afltest# ps aux | grep sleep
root 7567 0.0 0.0 16140 864 ? S 14:36 0:00 sleep 180
root 7578 0.0 0.0 16140 812 ? S 14:36 0:00 sleep 180
root 7589 0.0 0.0 16140 980 ? S 14:37 0:00 sleep 180
root 7594 0.0 0.0 16140 824 ? S 14:37 0:00 sleep 180
root 7595 0.0 0.0 16140 828 ? S 14:37 0:00 sleep 180
root 7779 0.0 0.0 16140 864 ? Ss 14:38 0:00 sleep 1000
root 7790 0.0 0.0 18840 1004 pts/4 S+ 14:38 0:00 grep --color=auto sleep
这时候发现sleep进程被系统1号进程systemd收养了
systemd-+-acpid
|.... 中间其他进程省略
|
|-sleep
|-smbd---2*[smbd]
|-snapd---7*[{snapd}]
|-spesservice---9*[{spesservice}]
close(int fd)
close就是让当前fd不再绑定具体文件(失效)
官方解释如下 :
int close(int fd);
DESCRIPTION
close() closes a file descriptor, so that it no longer refers to any file and may be reused. Any record locks (see fcntl(2)) held on the file it was associated with, and
owned by the process, are removed (regardless of the file descriptor that was used to obtain the lock).
If fd is the last file descriptor referring to the underlying open file description (see open(2)), the resources associated with the open file description are freed; if
the descriptor was the last reference to a file which has been removed using unlink(2), the file is deleted.
execv(const char* file, char * const argv[])
execv的作用:将新程序代码加载(拷贝)到子进程的内存空间,替换掉原有的与父进程一模一样的代码和数据,让子进程空间运行全新的程序
在Linux中使用exec函数族主要有以下两种情况:
a. 当进程认为自己不能再为系统和用户做出任何贡献时,就可以调用任何exec 函数族让自己重生。
b. 如果一个进程想执行另一个程序,那么它就可以调用fork函数新建一个进程,然后调用任何一个exec函数使子进程重生。
样例如下:
代码
// exectest.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
printf("parent process start!\n");
int fk_id = fork();
if (!fk_id) {
execv("./hello",NULL);
printf("i am fork process\n");
}
printf("i am parent process\n") ;
}
#include <stdio.h>
int main() {
printf("\nhello world!\n");
exit(0);
}
- 编译
root@dggphisprb19134:/usr1/zhd/learn/learn-c/afltest# gcc exectest.c -o ext
root@dggphisprb19134:/usr1/zhd/learn/learn-c/afltest# gcc hello.c -o hello
- 执行
root@dggphisprb19134:/usr1/zhd/learn/learn-c/afltest# ./ext
parent process start!
i am parent process
root@dggphisprb19134:/usr1/zhd/learn/learn-c/afltest#
hello world!
可以看出来,fork进程的 “i am fork process”打印语句未执行,因为fork进程已被替换为hello进程.
setitimer()
函数原型
setitimer(int which, const struct itimerval * new_value, struct itimerval *old_value)
itimerval结构体,相当于一个配置器
struct itimerval {
struct timeval it_interval; /* Interval for periodic timer */
struct timeval it_value; /* Time until next expiration */
};
//子结构,有秒和毫秒两档
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
搭配setitimer()函数使用,通过信号机制做出反应
static int count = 0;
void set_timer()
{
struct itimerval itv;
itv.it_value.tv_sec = 3; //timer start after 3 seconds later
itv.it_value.tv_usec = 0;
itv.it_interval.tv_sec = 1;
itv.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL,&itv,NULL);
}
void signal_handler(int m)
{
count ++;
printf("%d\n",count);
}
int main()
{
signal(SIGALRM,signal_handler);
set_timer();
while(count < 10);
exit(0);
return 0;
}
这段代码实现的功能:3秒钟后启动定时器。然后每隔1秒钟向终端打印count的递增值,当count到10时程序退出
getitimer
函数原型
int getitimer(int which, struct itimerval *curr_value);
功能:传入itimerval指针作为返回值入参,获得当前timer的拷贝.
dup2
函数原型
int dup2(int oldfd, int newfd)
作用:将newfd描述符重定向到oldfd,此后对newfd的操作,都体现在oldfd上