进程描述符
struct task_struct {
...
struct fs_struct *fs; //文件系统信息
struct files_struct *files; // 打开的文件
...
}
文件描述符表
/*
* Open file table structure
*/
#define NR_OPEN_DEFAULT BITS_PER_LONG
struct files_struct {
/*
* read mostly part
*/
atomic_t count; // 共享该表的进程数
struct fdtable __rcu *fdt; //fdtable指针
struct fdtable fdtab;
/*
* written part on a separate cache line in SMP
*/
spinlock_t file_lock ____cacheline_aligned_in_smp;
int next_fd;
unsigned long close_on_exec_init[1];
unsigned long open_fds_init[1];
struct file __rcu * fd_array[NR_OPEN_DEFAULT];
};
struct fdtable {
unsigned int max_fds; //fd数组元素个数
struct file __rcu **fd; //当前fd数组
unsigned long *close_on_exec; //bitmap ,是否close_on_exec
unsigned long *open_fds; //是否打开的bitmap ,每一位代表是否打开
unsigned long *full_fds_bits;
struct rcu_head rcu;
};
#define __FD_SETSIZE 1024
//fd_set 是一个bitmap
typedef struct {
unsigned long fds_bits[__FD_SETSIZE / (8 * sizeof(long))];
} __kernel_fd_set;
计算打开的文件数,open_fds 是一个bitmap ,每一位代表是否打开
static int count_open_files(struct fdtable *fdt)
{
int size = fdt->max_fds;
int i;
for (i = size / BITS_PER_LONG; i > 0; ) {
if (fdt->open_fds[--i])
break;
}
i = (i + 1) * BITS_PER_LONG;
return i;
}
static struct fdtable * alloc_fdtable(unsigned int nr)
{
struct fdtable *fdt;
void *data;
nr /= (1024 / sizeof(struct file *));
nr = roundup_pow_of_two(nr + 1);
nr *= (1024 / sizeof(struct file *));
if (unlikely(nr > sysctl_nr_open))
nr = ((sysctl_nr_open - 1) | (BITS_PER_LONG - 1)) + 1;
//new 一个fdt
fdt = kmalloc(sizeof(struct fdtable), GFP_KERNEL);
fdt->max_fds = nr; //fd数组元素个数
data = alloc_fdmem(nr * sizeof(struct file *));
fdt->fd = data; //fd指针数组 struct file
data = alloc_fdmem(max_t(size_t,
2 * nr / BITS_PER_BYTE + BITBIT_SIZE(nr), L1_CACHE_BYTES));
fdt->open_fds = data;
data += nr / BITS_PER_BYTE;
fdt->close_on_exec = data;
data += nr / BITS_PER_BYTE;
fdt->full_fds_bits = data;
return fdt;
out_arr:
kvfree(fdt->fd);
out_fdt:
kfree(fdt);
out:
return NULL;
}
struct file {
/*
* fu_list becomes invalid after file_free is called and queued via
* fu_rcuhead for RCU freeing
*/
union {
struct list_head fu_list;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path; //路径
#define f_dentry f_path.dentry
#define f_vfsmnt f_path.mnt
const struct file_operations *f_op; //件操作表
/*
* Protects f_ep_links, f_flags, f_pos vs i_size in lseek SEEK_CUR.
* Must not be taken from IRQ context.
*/
spinlock_t f_lock;
#ifdef CONFIG_SMP
int f_sb_list_cpu;
#endif
atomic_long_t f_count; //进程数
unsigned int f_flags;
fmode_t f_mode; //打开模式
loff_t f_pos; //当前位置
struct fown_struct f_owner;
const struct cred *f_cred; //权限
struct file_ra_state f_ra;
u64 f_version;
#ifdef CONFIG_SECURITY
void *f_security;
#endif
/* needed for tty driver, and maybe others */
void *private_data;
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
struct list_head f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
#ifdef CONFIG_DEBUG_WRITECOUNT
unsigned long f_mnt_write_state;
#endif
};
fork 新的进程需要拷贝files到新的task_struct
static int copy_files(unsigned long clone_flags, struct task_struct *tsk)
{
struct files_struct *oldf, *newf;
int error = 0;
oldf = current->files;
if (!oldf)
goto out;
//设置CLONE_FILES,则只增加文件引用计数, 例如pthread
if (clone_flags & CLONE_FILES) {
atomic_inc(&oldf->count);
goto out;
}
//创建新的files_struct,并拷贝内容
newf = dup_fd(oldf, &error);
if (!newf)
goto out;
tsk->files = newf; //新创建的files_struct赋值给新task_struct
error = 0;
out:
return error;
}
struct files_struct *dup_fd(struct files_struct *oldf, int *errorp)
{
struct files_struct *newf;
struct file **old_fds, **new_fds;
int open_files, i;
struct fdtable *old_fdt, *new_fdt;
newf = kmem_cache_alloc(files_cachep, GFP_KERNEL);
...
atomic_set(&newf->count, 1);
...
//初始化新的fdtable
new_fdt = &newf->fdtab;
new_fdt->max_fds = NR_OPEN_DEFAULT; //默认大小为32
new_fdt->close_on_exec = newf->close_on_exec_init;
new_fdt->open_fds = newf->open_fds_init;
new_fdt->full_fds_bits = newf->full_fds_bits_init;
new_fdt->fd = &newf->fd_array[0];
...
//获取oldf->fdt
old_fdt = files_fdtable(oldf);
//获取old_fdt所打开的文件个数
open_files = count_open_files(old_fdt);
//open_files 超过max_fds(32)
while (unlikely(open_files > new_fdt->max_fds)) {
...
//分配更多的fdtable
new_fdt = alloc_fdtable(open_files - 1);
...
}
...
}
特殊的文件描述符
从shell中运行一个进程,默认会有3个文件描述符
fd = 0 , 1, 2
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
dup
复制一个现有的文件描述符oldfd
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
重定向std输入输出
for(i=0;i<NR_OPEN;i++){
close(i);
}
open("/dev/null",O_RDWR); //0
open("/dev/null",O_RDWR); //1
open("/dev/null",O_RDWR); //2
//android/system/extra/tests/cpueater
/* redirect to /dev/null */
close(0);
open("/dev/null", 0);
close(1);
if(open("/dev/null", O_WRONLY) < 0) {
perror("/dev/null");
exit(1);
}
fflush(stdout);
close(2);
dup(1); /* join stdout and stderr */
for (fd = 3; fd < 256; fd++) {
close(fd);
}
Android中的Log
init进程会把0,1,2三个fd指向到/dev/null,而其他进程都是由init fork出来。
int main(int argc, char** argv) {
...
InitKernelLogging(argv);
}
void InitKernelLogging(char* argv[]) {
// Make stdin/stdout/stderr all point to /dev/null.
int fd = open("/sys/fs/selinux/null", O_RDWR);
if (fd == -1) {
int saved_errno = errno;
android::base::InitLogging(argv, &android::base::KernelLogger);
errno = saved_errno;
PLOG(FATAL) << "Couldn't open /sys/fs/selinux/null";
}
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
if (fd > 2) close(fd);
android::base::InitLogging(argv, &android::base::KernelLogger);
}
umask
mode_t umask(mode_t cmask);
open时的mode 需要和cmask进行mode & (~cmask) 。
如果cmask = 0 ,就是不屏蔽任何位。
进程组
进程组是一个或多个进程的集合,接受来自同一终端的各种信号。
- 领头进程
//getpgrp: 获得进程组 id, 即领头进程的 pid
pid_t getpgrp(void);
setpgrp(void); //相当于setpgid(0,0 将当前进程所属的组识别码设为当前进程pid
//如果参数pid 为0,则会用来设置当前进程的组识别码
//如果参数pgid为0,则由pid指定的进程ID将用作进程组ID
int setpgid(pid_t pid,pid_t pgid);
//创建一个新的对话并设置进程组ID为当前进程的id, 调用它的进程不能是进程组leader
//例如在一个child进程中调用此函数。
//这时原来的进程终端仍然是可打开的,仍然可以读写,但只是一个普通的打开文件而不是控制终端了
pid_t setsid(void);
//获取当前进程pid号
pid_t getpid(void);
//获取当前进程的父进程pid号
pid_t getppid(void);
//参数 str 所指的字符串会先打印出,后面再加上错误原因字符串。此错误原因依照全局变量errno的值来决定要输出的字符串
void perror ( const char * str );
Daemon进程
- SIGCHLD & wait
当子进程退出的时候,内核会向父进程SIGCHLD信号
父进程查询子进程的退出状态可以用wait/waitpid函数
#define WEXITSTATUS(s) (((s) & 0xff00) >> 8)
#define WCOREDUMP(s) ((s) & 0x80)
#define WTERMSIG(s) ((s) & 0x7f)
#define WSTOPSIG(s) WEXITSTATUS(s)
#define WIFEXITED(s) (WTERMSIG(s) == 0)
#define WIFSTOPPED(s) (WTERMSIG(s) == 0x7f)
#define WIFSIGNALED(s) (WTERMSIG((s)+1) >= 2)
fork一次后,存在父进程和子进程
- 父进程需要调用waitpid()等函数来接收子进程退出状态
- 如果父进程先结束,子进程则自动托管到Init进程(pid = 1)
fork两次
- 父进程一次fork()后产生一个子进程,
- 父进程执行waitpid(子进程pid, NULL, 0)来等待子进程结束
- 子进程fork()后产生孙子进程随后立即exit(0), 子进程顺利终止,父进程给子进程收尸了。
- 父进程继续执行, 孙子进程继续执行。 由于孙子进程失去了它的父进程,将被转交给Init进程托管。
//关闭input
close(0);
// input redirect to null
open("/dev/null", 0);
//关闭output
close(1);
// output redirect to file
if(open(filename, O_WRONLY|mode|O_CREAT, 0666) < 0) {
perror(filename);
exit(1);
}
//第一次fork
switch(pid = fork()) {
case -1:
perror(argv[0]);
exit(1);
break;
case 0:
//子进程
fflush(stdout);
close(2); dup(1); /* join stdout and stderr */
for (fd = 3; fd < 256; fd++) {
close(fd);
}
chdir("/");
//umask
umask(0);
setpgrp();
//create new session , detach control terminal
setsid();
switch(pid = fork()) {
case -1:
break;
case 0:
//孙子进程
//替换进程映像
execvp(argv[0], argv);
//如果出错,输出execvp的错误
perror(argv[0]);
break;
default:
_exit(0);
}
_exit(1);
break;
default:
exit(0);
break;
}