Daemon进程

进程描述符

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两次
  1. 父进程一次fork()后产生一个子进程,
  2. 父进程执行waitpid(子进程pid, NULL, 0)来等待子进程结束
  3. 子进程fork()后产生孙子进程随后立即exit(0), 子进程顺利终止,父进程给子进程收尸了。
  4. 父进程继续执行, 孙子进程继续执行。 由于孙子进程失去了它的父进程,将被转交给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;
  }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用Linux系统提供的fork()函数创建子进程,从而实现守护进程的创建,编写如下的程序: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <time.h> void signal_handler(int signum) { printf("Daemon process is terminated.\n"); exit(0); } int main() { pid_t pid; time_t t; pid = fork(); if (pid < 0) { printf("Failed to create daemon.\n"); exit(1); } else if (pid > 0) { printf("Parent process ID is %d.\n", getpid()); return 0; } printf("Child process ID is %d.\n", getpid()); // 创建新会话,使子进程成为守护进程 if (setsid() < 0) { printf("Failed to create daemon.\n"); exit(1); } // 忽略终端信号,防止进程被终止 signal(SIGINT, SIG_IGN); // 注册信号处理函数,以便在进程被终止时输出信息 signal(SIGTERM, signal_handler); // 输出父进程ID和当前时间 printf("Parent process ID is %d.\n", getppid()); while (1) { time(&t); printf("Current time is %s", ctime(&t)); sleep(1); } return 0; } ``` 程序中首先使用fork()函数创建一个子进程pid,如果pid小于0,则说明创建进程失败;如果pid大于0,则说明当前进程是父进程,直接退出;否则说明当前进程是子进程,执行后续的创建守护进程的操作。 子进程首先调用setsid()函数创建新会话,使自己成为守护进程,并脱离终端。然后忽略终端信号,防止进程被终止。接着注册信号处理函数,以便在进程被终止时输出信息。最后进入一个死循环,输出父进程ID和当前时间,并睡眠1秒钟。 可以使用如下的命令编译和运行程序: ```bash gcc -o daemon daemon.c ./daemon ``` 程序将在后台运行,并输出父进程ID和当前时间。可以使用Ctrl+c组合键中断程序,观察输出信息。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值