本文谢绝转载,原文来自http://990487026.blog.51cto.com



《大纲》
Linux系统编程
	Linux 进程描述符结构体;
	进程资源上限
	进程环境变量
	进程获取/修改环境变量
	创建子进程fork()
	最大进程数测试
	程序的设置用户ID/组ID/黏住位
	exec簇函数,执行程序覆盖堆栈
	fork 与execl函数在一起
	exec() 与主程序同一个PCB
	僵尸进程
	wait()回收僵尸进程
	证明:父子进程同组pid
	waitpid() 非阻塞等待子线程发生变化
	孤儿进程演示【父进程已经结束,子进程还在运行】
	


Linux 进程描述符结构体;

root@http://990487026.blog.51cto.com~# cat -n /usr/src/linux-headers-3.13.0-92-generic/include/linux/sched.h | grep "struct task_struct {"
  1053	struct task_struct {
root@http://990487026.blog.51cto.com~# sed -n '1053,$p' /usr/src/linux-headers-3.13.0-92-generic/include/linux/sched.h

struct task_struct {
	volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
	void *stack;
	atomic_t usage;
	unsigned int flags;	/* per process flags, defined below */
	unsigned int ptrace;

#ifdef CONFIG_SMP
	struct llist_node wake_entry;
	int on_cpu;
	struct task_struct *last_wakee;
	unsigned long wakee_flips;
	unsigned long wakee_flip_decay_ts;

	int wake_cpu;
#endif
	int on_rq;

	int prio, static_prio, normal_prio;
	unsigned int rt_priority;
	const struct sched_class *sched_class;
	struct sched_entity se;
	struct sched_rt_entity rt;
#ifdef CONFIG_CGROUP_SCHED
	struct task_group *sched_task_group;
#endif

#ifdef CONFIG_PREEMPT_NOTIFIERS
	/* list of struct preempt_notifier: */
	struct hlist_head preempt_notifiers;
#endif

#ifdef CONFIG_BLK_DEV_IO_TRACE
	unsigned int btrace_seq;
#endif

	unsigned int policy;
	int nr_cpus_allowed;
	cpumask_t cpus_allowed;

#ifdef CONFIG_PREEMPT_RCU
	int rcu_read_lock_nesting;
	char rcu_read_unlock_special;
	struct list_head rcu_node_entry;
#endif /* #ifdef CONFIG_PREEMPT_RCU */
#ifdef CONFIG_TREE_PREEMPT_RCU
	struct rcu_node *rcu_blocked_node;
#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */
#ifdef CONFIG_RCU_BOOST
	struct rt_mutex *rcu_boost_mutex;
#endif /* #ifdef CONFIG_RCU_BOOST */

#if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT)
	struct sched_info sched_info;
#endif

	struct list_head tasks;
#ifdef CONFIG_SMP
	struct plist_node pushable_tasks;
#endif

	struct mm_struct *mm, *active_mm;
#ifdef CONFIG_COMPAT_BRK
	unsigned brk_randomized:1;
#endif
#if defined(SPLIT_RSS_COUNTING)
	struct task_rss_stat	rss_stat;
#endif
/* task state */
	int exit_state;
	int exit_code, exit_signal;
	int pdeath_signal;  /*  The signal sent when the parent dies  */
	unsigned int jobctl;	/* JOBCTL_*, siglock protected */

	/* Used for emulating ABI behavior of previous Linux versions */
	unsigned int personality;

	unsigned did_exec:1;
	unsigned in_execve:1;	/* Tell the LSMs that the process is doing an
				 * execve */
	unsigned in_iowait:1;

	/* Revert to default priority/policy when forking */
	unsigned sched_reset_on_fork:1;
	unsigned sched_contributes_to_load:1;

	unsigned long atomic_flags; /* Flags needing atomic access. */

	pid_t pid;
	pid_t tgid;

#ifdef CONFIG_CC_STACKPROTECTOR
	/* Canary value for the -fstack-protector gcc feature */
	unsigned long stack_canary;
#endif
	/*
	 * pointers to (original) parent process, youngest child, younger sibling,
	 * older sibling, respectively.  (p->father can be replaced with
	 * p->real_parent->pid)
	 */
	struct task_struct __rcu *real_parent; /* real parent process */
	struct task_struct __rcu *parent; /* recipient of SIGCHLD, wait4() reports */
	/*
	 * children/sibling forms the list of my natural children
	 */
	struct list_head children;	/* list of my children */
	struct list_head sibling;	/* linkage in my parent's children list */
	struct task_struct *group_leader;	/* threadgroup leader */

	/*
	 * ptraced is the list of tasks this task is using ptrace on.
	 * This includes both natural children and PTRACE_ATTACH targets.
	 * p->ptrace_entry is p's link on the p->parent->ptraced list.
	 */
	struct list_head ptraced;
	struct list_head ptrace_entry;

	/* PID/PID hash table linkage. */
	struct pid_link pids[PIDTYPE_MAX];
	struct list_head thread_group;
	struct list_head thread_node;

	struct completion *vfork_done;		/* for vfork() */
	int __user *set_child_tid;		/* CLONE_CHILD_SETTID */
	int __user *clear_child_tid;		/* CLONE_CHILD_CLEARTID */

	cputime_t utime, stime, utimescaled, stimescaled;
	cputime_t gtime;
#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
	struct cputime prev_cputime;
#endif
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
	seqlock_t vtime_seqlock;
	unsigned long long vtime_snap;
	enum {
		VTIME_SLEEPING = 0,
		VTIME_USER,
		VTIME_SYS,
	} vtime_snap_whence;
#endif
	unsigned long nvcsw, nivcsw; /* context switch counts */
	struct timespec start_time; 		/* monotonic time */
	struct timespec real_start_time;	/* boot based time */
/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
	unsigned long min_flt, maj_flt;

	struct task_cputime cputime_expires;
	struct list_head cpu_timers[3];

/* process credentials */
	const struct cred __rcu *real_cred; /* objective and real subjective task
					 * credentials (COW) */
	const struct cred __rcu *cred;	/* effective (overridable) subjective task
					 * credentials (COW) */
	char comm[TASK_COMM_LEN]; /* executable name excluding path
				     - access with [gs]et_task_comm (which lock
				       it with task_lock())
				     - initialized normally by setup_new_exec */
/* file system info */
	int link_count, total_link_count;
#ifdef CONFIG_SYSVIPC
/* ipc stuff */
	struct sysv_sem sysvsem;
#endif
#ifdef CONFIG_DETECT_HUNG_TASK
/* hung task detection */
	unsigned long last_switch_count;
#endif
/* CPU-specific state of this task */
	struct thread_struct thread;
/* filesystem information */
	struct fs_struct *fs;
/* open file information */
	struct files_struct *files;
/* namespaces */
	struct nsproxy *nsproxy;
/* signal handlers */
	struct signal_struct *signal;
	struct sighand_struct *sighand;

	sigset_t blocked, real_blocked;
	sigset_t saved_sigmask;	/* restored if set_restore_sigmask() was used */
	struct sigpending pending;

	unsigned long sas_ss_sp;
	size_t sas_ss_size;
	int (*notifier)(void *priv);
	void *notifier_data;
	sigset_t *notifier_mask;
	struct callback_head *task_works;

	struct audit_context *audit_context;
#ifdef CONFIG_AUDITSYSCALL
	kuid_t loginuid;
	unsigned int sessionid;
#endif
	struct seccomp seccomp;

/* Thread group tracking */
   	u32 parent_exec_id;
   	u32 self_exec_id;
/* Protection of (de-)allocation: mm, files, fs, tty, keyrings, mems_allowed,
 * mempolicy */
	spinlock_t alloc_lock;

	/* Protection of the PI data structures: */
	raw_spinlock_t pi_lock;

#ifdef CONFIG_RT_MUTEXES
	/* PI waiters blocked on a rt_mutex held by this task */
	struct plist_head pi_waiters;
	/* Deadlock detection and priority inheritance handling */
	struct rt_mutex_waiter *pi_blocked_on;
#endif

#ifdef CONFIG_DEBUG_MUTEXES
	/* mutex deadlock detection */
	struct mutex_waiter *blocked_on;
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
	unsigned int irq_events;
	unsigned long hardirq_enable_ip;
	unsigned long hardirq_disable_ip;
	unsigned int hardirq_enable_event;
	unsigned int hardirq_disable_event;
	int hardirqs_enabled;
	int hardirq_context;
	unsigned long softirq_disable_ip;
	unsigned long softirq_enable_ip;
	unsigned int softirq_disable_event;
	unsigned int softirq_enable_event;
	int softirqs_enabled;
	int softirq_context;
#endif
#ifdef CONFIG_LOCKDEP
# define MAX_LOCK_DEPTH 48UL
	u64 curr_chain_key;
	int lockdep_depth;
	unsigned int lockdep_recursion;
	struct held_lock held_locks[MAX_LOCK_DEPTH];
	gfp_t lockdep_reclaim_gfp;
#endif

/* journalling filesystem info */
	void *journal_info;

/* stacked block device info */
	struct bio_list *bio_list;

#ifdef CONFIG_BLOCK
/* stack plugging */
	struct blk_plug *plug;
#endif

/* VM state */
	struct reclaim_state *reclaim_state;

	struct backing_dev_info *backing_dev_info;

	struct io_context *io_context;

	unsigned long ptrace_message;
	siginfo_t *last_siginfo; /* For ptrace use.  */
	struct task_io_accounting ioac;
#if defined(CONFIG_TASK_XACCT)
	u64 acct_rss_mem1;	/* accumulated rss usage */
	u64 acct_vm_mem1;	/* accumulated virtual memory usage */
	cputime_t acct_timexpd;	/* stime + utime since last update */
#endif
#ifdef CONFIG_CPUSETS
	nodemask_t mems_allowed;	/* Protected by alloc_lock */
	seqcount_t mems_allowed_seq;	/* Seqence no to catch updates */
	int cpuset_mem_spread_rotor;
	int cpuset_slab_spread_rotor;
#endif
#ifdef CONFIG_CGROUPS
	/* Control Group info protected by css_set_lock */
	struct css_set __rcu *cgroups;
	/* cg_list protected by css_set_lock and tsk->alloc_lock */
	struct list_head cg_list;
#endif
#ifdef CONFIG_FUTEX
	struct robust_list_head __user *robust_list;
#ifdef CONFIG_COMPAT
	struct compat_robust_list_head __user *compat_robust_list;
#endif
	struct list_head pi_state_list;
	struct futex_pi_state *pi_state_cache;
#endif
#ifdef CONFIG_PERF_EVENTS
	struct perf_event_context *perf_event_ctxp[perf_nr_task_contexts];
	struct mutex perf_event_mutex;
	struct list_head perf_event_list;
#endif
#ifdef CONFIG_NUMA
	struct mempolicy *mempolicy;	/* Protected by alloc_lock */
	short il_next;
	short pref_node_fork;
#endif
#ifdef CONFIG_NUMA_BALANCING
	int numa_scan_seq;
	unsigned int numa_scan_period;
	unsigned int numa_scan_period_max;
	int numa_preferred_nid;
	int numa_migrate_deferred;
	unsigned long numa_migrate_retry;
	u64 node_stamp;			/* migration stamp  */
	struct callback_head numa_work;

	struct list_head numa_entry;
	struct numa_group *numa_group;

	/*
	 * Exponential decaying average of faults on a per-node basis.
	 * Scheduling placement decisions are made based on the these counts.
	 * The values remain static for the duration of a PTE scan
	 */
	unsigned long *numa_faults;
	unsigned long total_numa_faults;

	/*
	 * numa_faults_buffer records faults per node during the current
	 * scan window. When the scan completes, the counts in numa_faults
	 * decay and these values are copied.
	 */
	unsigned long *numa_faults_buffer;

	/*
	 * numa_faults_locality tracks if faults recorded during the last
	 * scan window were remote/local. The task scan period is adapted
	 * based on the locality of the faults with different weights
	 * depending on whether they were shared or private faults
	 */
	unsigned long numa_faults_locality[2];

	unsigned long numa_pages_migrated;
#endif /* CONFIG_NUMA_BALANCING */

	struct rcu_head rcu;

	/*
	 * cache last used pipe for splice
	 */
	struct pipe_inode_info *splice_pipe;

	struct page_frag task_frag;

#ifdef	CONFIG_TASK_DELAY_ACCT
	struct task_delay_info *delays;
#endif
#ifdef CONFIG_FAULT_INJECTION
	int make_it_fail;
#endif
	/*
	 * when (nr_dirtied >= nr_dirtied_pause), it's time to call
	 * balance_dirty_pages() for some dirty throttling pause
	 */
	int nr_dirtied;
	int nr_dirtied_pause;
	unsigned long dirty_paused_when; /* start of a write-and-pause period */

#ifdef CONFIG_LATENCYTOP
	int latency_record_count;
	struct latency_record latency_record[LT_SAVECOUNT];
#endif
	/*
	 * time slack values; these are used to round up poll() and
	 * select() etc timeout values. These are in nanoseconds.
	 */
	unsigned long timer_slack_ns;
	unsigned long default_timer_slack_ns;

#ifdef CONFIG_FUNCTION_GRAPH_TRACER
	/* Index of current stored address in ret_stack */
	int curr_ret_stack;
	/* Stack of return addresses for return function tracing */
	struct ftrace_ret_stack	*ret_stack;
	/* time stamp for last schedule */
	unsigned long long ftrace_timestamp;
	/*
	 * Number of functions that haven't been traced
	 * because of depth overrun.
	 */
	atomic_t trace_overrun;
	/* Pause for the tracing */
	atomic_t tracing_graph_pause;
#endif
#ifdef CONFIG_TRACING
	/* state flags for use by tracers */
	unsigned long trace;
	/* bitmask and counter of trace recursion */
	unsigned long trace_recursion;
#endif /* CONFIG_TRACING */
#ifdef CONFIG_MEMCG /* memcg uses this to do batch job */
	struct memcg_batch_info {
		int do_batch;	/* incremented when batch uncharge started */
		struct mem_cgroup *memcg; /* target memcg of uncharge */
		unsigned long nr_pages;	/* uncharged usage */
		unsigned long memsw_nr_pages; /* uncharged mem+swap usage */
	} memcg_batch;
	unsigned int memcg_kmem_skip_account;
	struct memcg_oom_info {
		struct mem_cgroup *memcg;
		gfp_t gfp_mask;
		int order;
		unsigned int may_oom:1;
	} memcg_oom;
#endif
#ifdef CONFIG_UPROBES
	struct uprobe_task *utask;
#endif
#if defined(CONFIG_BCACHE) || defined(CONFIG_BCACHE_MODULE)
	unsigned int	sequential_io;
	unsigned int	sequential_io_avg;
#endif
};









进程资源上限值

chunli@ubuntu16:~$ cat /proc/self/limits 
Limit                     Soft Limit           Hard Limit           Units     
Max cpu time              unlimited            unlimited            seconds   
Max file size             unlimited            unlimited            bytes     
Max data size             unlimited            unlimited            bytes     
Max stack size            8388608              unlimited            bytes     
Max core file size        0                    unlimited            bytes     
Max resident set          unlimited            unlimited            bytes     
Max processes             3749                 3749                 processes 
Max open files            1024                 65536                files     
Max locked memory         65536                65536                bytes     
Max address space         unlimited            unlimited            bytes     
Max file locks            unlimited            unlimited            locks     
Max pending signals       3749                 3749                 signals   
Max msgqueue size         819200               819200               bytes     
Max nice priority         0                    0                    
Max realtime priority     0                    0                    
Max realtime timeout      unlimited            unlimited            us        
chunli@ubuntu16:~$



进程获取环境变量

chunli@ubuntu16:~/linux_c$ cat main.c 
#include 
int main(int num,char **args,char **env)
{
	int i = 0;
	for(i = 0;env[i] != NULL;i++)	
	{
		printf("%s\n",env[i]);
	}
	return 0;
}

chunli@ubuntu16:~/linux_c$ gcc main.c  && ./a.out 
XDG_SESSION_ID=8
TERM=xterm
SHELL=/bin/bash
SSH_CLIENT=10.11.12.1 2826 22
SSH_TTY=/dev/pts/9
USER=chunli
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:
MAIL=/var/mail/chunli
PATH=/home/chunli/bin:/home/chunli/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
QT_QPA_PLATFORMTHEME=appmenu-qt5
PWD=/home/chunli/linux_c
LANG=zh_CN.UTF-8
SHLVL=1
HOME=/home/chunli
LANGUAGE=zh_CN:zh
LOGNAME=chunli
SSH_CONNECTION=10.11.12.1 2826 10.11.12.21 22
LESSOPEN=| /usr/bin/lesspipe %s
XDG_RUNTIME_DIR=/run/user/1000
LESSCLOSE=/usr/bin/lesspipe %s %s
_=./a.out
OLDPWD=/home/chunli
chunli@ubuntu16:~/linux_c$



getenv(),setenv()获取修改进程的环境变量

chunli@ubuntu16:~/linux_c$ cat main.c 
#include 
#include 
int main(int num,char **args,char **env)
{
	printf("PATH=%s\n", getenv("PATH"));
	setenv("PATH","Hello World!",1);
	printf("PATH=%s\n", getenv("PATH"));
	return 0;
}

chunli@ubuntu16:~/linux_c$ gcc main.c  && ./a.out 
PATH=/home/chunli/bin:/home/chunli/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
PATH=Hello World!
chunli@ubuntu16:~/linux_c$



fork()进程创建

chunli@ubuntu:~/linux_c$ cat main.c 
#include 
#include 
#include 
#include 
int main(int num,char **args,char **env)
{
	int i = 0;	//变量,读时共享,写时复制.把子进程需要的变量才复制到子进程一份
	printf("主函数\n");
	pid_t  pid;
	pid = fork();	//子父进程诞生,从下面的语句开始运行,不再运行本条及上面的语句
			//子进程,仅仅是pid不一样,开始执行语句的位置也不一样
	if(pid > 0)
	{
		while(1)
		{
			printf("父进程 %d \n",i++);	//共享变量,读共享,写操作才会复制一份
			printf("父进程的pid =%d \n",getpid());
			printf("父进程的ppid =%d \n",getppid());
			sleep(5);
		}
	}
	else if(pid == 0)
	{
		while(1)
		{
			printf("子进程 %d \n",i++);
			printf("子进程的pid =%d \n",getpid());
			printf("子进程的ppid =%d \n",getppid());
			sleep(2);
		}
	}
	else
	{
		printf("进程创建失败\n");
	}
	return 0;
}

chunli@ubuntu:~/linux_c$ 

chunli@ubuntu:~/linux_c$ gcc main.c  && ./a.out 
主函数
父进程 0 
父进程的pid =6189 
父进程的ppid =5811 
子进程 0 
子进程的pid =6190 
子进程的ppid =6189 
子进程 1 
子进程的pid =6190 
子进程的ppid =6189 
子进程 2 
子进程的pid =6190 
子进程的ppid =6189 
父进程 1 
父进程的pid =6189 
父进程的ppid =5811 
子进程 3 
子进程的pid =6190 
子进程的ppid =6189 
^C


测试最多能开启线程数

chunli@ubuntu:~/linux_c$ cat main.c 
#include 
#include 
#include 
#include 
int main(int num,char **args,char **env)
{
	int time = 50;	//让子进程存活n 秒
	int n = 0;
	pid_t pid;
	while(1)
	{
		pid = fork();
		if(pid == 0)
		{
			break;	//如果是子进程,就跳出来
		}
		if(pid < 0)
		{
			printf("\nfork 出错\n");
			exit(1);
		}
		//printf("%d\t",n++);	//如果启用这一句,会执行两次输出
		printf("%d\n",n++);
	}
	while(time--)
	{
		sleep(1);	//子进程干的事儿
		//printf(".");
	}
	
	return 0;
}

chunli@ubuntu:~/linux_c$ gcc main.c  && ./a.out
...............................
3735
3736
3737

fork 出错




设置用户ID,执行文件时以文件所有者运行

比如passwd命令,普通用户执行这个命令,修改的密码放在/etc/shadow文件中,

只有root权限才可以修改文件的内容

chunli@ubuntu:~$ which passwd 
/usr/bin/passwd
chunli@ubuntu:~$ ls -l /usr/bin/passwd 
-rwsr-xr-x 1 root root 54256 3月  29 17:25 /usr/bin/passwd
chunli@ubuntu:~$ 
chunli@ubuntu:~$ ls -l /etc/shadow
-rw-r----- 1 root shadow 1273 8月   2 19:19 /etc/shadow


文件所属chunli,chuli来运行,没问题

chunli@ubuntu:~/linux_c$ cat main.c 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

int main(int num,char **args,char **env)
{
	int fd = 0;
	fd = open("./abc",O_CREAT,0644);//如果没有这个文件就创建
	if(fd == -1)
	{
		perror("文件打开/创建\n");
	}
	printf("运行程序的实际用户id是%d\n",getuid());
	printf("运行程序的有效用户id是%d\n",geteuid());

	return 0;
}

chunli@ubuntu:~/linux_c$ 


gcc main.c  && ./a.out 
运行程序的实际用户id是1000
运行程序的有效用户id是1000
chunli@ubuntu:~/linux_c$ ll
总用量 16K
-rw-r--r-- 1 chunli chunli    0 8月   5 11:31 abc
-rwxrwxr-x 1 chunli chunli 8.7K 8月   5 11:31 a.out*
-rw-rw-r-- 1 chunli chunli  456 8月   5 11:30 main.c


把上面的可执行程序放在根目录,无法创建文件

chunli@ubuntu:~/linux_c$ cp ./a.out  /
cp: 无法创建普通文件'/a.out': 权限不够
chunli@ubuntu:~/linux_c$ sudo cp ./a.out  /
[sudo] chunli 的密码: 
chunli@ubuntu:~/linux_c$ ll /a.out 
-rwxr-xr-x 1 root root 8.7K 8月   5 11:32 /a.out*
chunli@ubuntu:~/linux_c$ 
chunli@ubuntu:/$ cd /
chunli@ubuntu:/$ ./a.out 
文件打开/创建: Permission denied
运行程序的实际用户id是1000
运行程序的有效用户id是1000



设置用户ID,程序运行时以root身份运行,可以创建文化

Linux系统开发 4 进程资源 环境 fork()子进程 wait() waitpid()僵尸 孤儿进程_第1张图片



chunli@ubuntu:/$ sudo chmod  4611 a.out 
chunli@ubuntu:/$ ll a.out 
-rwS--x--x 1 root root 8.7K 8月   5 11:34 a.out*
chunli@ubuntu:/$ ./a.out 
运行程序的实际用户id是1000
运行程序的有效用户id是0
chunli@ubuntu:/$ ll abc 
-rw-r--r-- 1 root chunli 0 8月   5 11:37 abc




设置用户id和组ID
chunli@ubuntu:/$ sudo chmod  6611 a.out 
chunli@ubuntu:/$ ll a.out
-rwS--s--x 1 root root 8.7K 8月   5 11:34 a.out*
chunli@ubuntu:/$ 

还有一个粘住位,常驻内存程序
chunli@ubuntu:/$ sudo chmod  7777 a.out 
chunli@ubuntu:/$ ll a.out
-rwsrwsrwt 1 root root 8.7K 8月   5 11:34 a.out*
chunli@ubuntu:/$



exec簇函数

#include 

extern char **environ;

int execl(const char *path, const char *arg, ...
	       /* (char  *) NULL */);
int execlp(const char *file, const char *arg, ...
	       /* (char  *) NULL */);
int execle(const char *path, const char *arg, ...
	       /*, (char *) NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
	       char *const envp[]);



execl执行一个程序,完全替换掉当前程序的堆栈

exec下面的语句没有机会执行了

chunli@ubuntu:~/linux_c$ ll
总用量 16K
-rwxrwxr-x 1 chunli chunli 8.5K 8月   5 11:56 a.out*
-rw-rw-r-- 1 chunli chunli  277 8月   5 11:56 main.c
chunli@ubuntu:~/linux_c$ cat main.c 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

int main(int num,char **args,char **env)
{
	printf("Hello \n");
	execl("/bin/ls","ls","-l",NULL);//下面的代码没有机会执行,程序从ls返回
	printf("World \n");
	return 0;
}

chunli@ubuntu:~/linux_c$ gcc main.c  && ./a.out 
Hello 
总用量 16
-rwxrwxr-x 1 chunli chunli 8656 8月   5 11:56 a.out
-rw-rw-r-- 1 chunli chunli  277 8月   5 11:56 main.c
chunli@ubuntu:~/linux_c$



证明:execl函数下面的代码没有机会执行

chunli@ubuntu:~/linux_c$ cat test.c 
int main()
{
	return 9;	//测试程序,返回值是9
}
chunli@ubuntu:~/linux_c$ gcc test.c  -o test

chunli@ubuntu:~/linux_c$ cat main.c 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

int main(int num,char **args,char **env)
{
	printf("Hello \n");
	execl("/home/chunli/linux_c/test","./test",NULL);
	printf("World \n");
	return 0;
}

chunli@ubuntu:~/linux_c$ gcc main.c  && ./a.out 
Hello 
chunli@ubuntu:~/linux_c$ echo $?
9




fork() execl()在一起演示

子进程加载其他程序,替换当前的程序代码段

chunli@ubuntu:~/linux_c$ cat main.c 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

int main(int num,char **args,char **env)
{
	pid_t pid  = fork();
	if(pid == 0 )
	{
		execl("/usr/bin/firefox","firefox","www.baidu.com",NULL);//参数1,2,3
	}
	if(pid > 0)
	{
		printf("I'm parent \n");
		sleep(1);
	}
	if(pid < 0)
	{	
		perror("fork");
		exit(2);
	}
	return 0;
}

chunli@ubuntu:~/linux_c$ gcc main.c  && ./a.out 
I'm parent 
Error: GDK_BACKEND does not match available displays
chunli@ubuntu:~/linux_c$


execlp在PATH里面找可执行程序

chunli@ubuntu:~/linux_c$ cat main.c 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

int main(int num,char **args,char **env)
{
	pid_t pid  = fork();
	if(pid == 0 )
	{
		execlp("ls","ls","-lh",NULL);//参数1,2,3
	}
	if(pid > 0)
	{
		printf("I'm parent \n");
		sleep(1);
	}
	if(pid < 0)
	{	
		perror("fork");
		exit(2);
	}
	return 0;
}

chunli@ubuntu:~/linux_c$ gcc main.c  && ./a.out 
I'm parent 
总用量 32K
-rwxrwxr-x 1 chunli chunli 8.7K 8月   5 13:47 a.out
-rw-rw-r-- 1 chunli chunli  396 8月   5 13:47 main.c
-rwxrwxr-x 1 chunli chunli 8.4K 8月   5 12:04 test
-rw-rw-r-- 1 chunli chunli   26 8月   5 12:04 test.c
chunli@ubuntu:~/linux_c$


execvp()函数,指针数组的形式

chunli@ubuntu:~/linux_c$ cat main.c 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

int main(int num,char **args,char **env)
{
	char *str[]={"ls","-l",NULL};
	pid_t pid  = fork();
	if(pid == 0 )
	{
		execvp("ls",str);//参数1,2,3
	}
	if(pid > 0)
	{
		printf("I'm parent \n");
		sleep(1);
	}
	if(pid < 0)
	{	
		perror("fork");
		exit(2);
	}
	return 0;
}

chunli@ubuntu:~/linux_c$ gcc main.c  && ./a.out 
I'm parent 
总用量 32
-rwxrwxr-x 1 chunli chunli 8920 8月   5 13:55 a.out
-rw-rw-r-- 1 chunli chunli  415 8月   5 13:55 main.c
-rwxrwxr-x 1 chunli chunli 8552 8月   5 12:04 test
-rw-rw-r-- 1 chunli chunli   26 8月   5 12:04 test.c
chunli@ubuntu:~/linux_c$


execle()不仅把代码堆栈替换,还把命令行参数环境变量也替换

exec() 与主程序同一个PCB【看图】

Linux系统开发 4 进程资源 环境 fork()子进程 wait() waitpid()僵尸 孤儿进程_第2张图片

chunli@ubuntu:~/linux_c$ cat test.c 
#include 
#include 
int main()
{
	int ch = 0;
	while((ch = getchar()) != EOF)
	{
		putchar(toupper(ch));
	}
	return 0;
}
chunli@ubuntu:~/linux_c$ cat main.c 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

int main(int argc,char **args,char **env)
{
        int fd = 0;
        if(argc < 2)
        {
                fputs("需要1个参数\n",stdout);
                exit(1);
        }
        fd = open(args[1],O_RDONLY);    //fd = 3,指向了文件结构体
        if(fd < 0)
        {
                perror("open");
                exit(1);
        }
        dup2(fd,STDIN_FILENO);  //关闭了标准输入,fd就是0
        close(fd);              //关闭文件描述符与文件结构体的关系
        execl("./upper","upper",NULL);  //execl的程序与主程序公用PCB,PCB里面有文件描述符表
                                        //upper里面的getchar操作从文件描述符为0的文件读内容
        perror("exec ./upper");
        exit(2);
        return 0;
}

chunli@ubuntu:~/linux_c$ 

chunli@ubuntu:~/linux_c$ gcc test.c -o upper
chunli@ubuntu:~/linux_c$ gcc main.c -o main
chunli@ubuntu:~/linux_c$ ./main 
需要1个参数
chunli@ubuntu:~/linux_c$ gcc main.c && ./main main.c 
#INCLUDE 
#INCLUDE 

#INCLUDE 
#INCLUDE 
#INCLUDE 
#INCLUDE 
#INCLUDE 

INT MAIN(INT ARGC,CHAR **ARGS,CHAR **ENV)
{
	INT FD = 0;
	IF(ARGC < 2)
	{
		FPUTS("需要1个参数\N",STDOUT);
		EXIT(1);
	}
	FD = OPEN(ARGS[1],O_RDONLY);	//FD = 3,指向了文件结构体
	IF(FD < 0)
	{
		PERROR("OPEN");
		EXIT(1);
	}
	DUP2(FD,STDIN_FILENO);	//关闭了标准输入,FD就是0
	CLOSE(FD);		//关闭文件描述符与文件结构体的关系
	EXECL("./UPPER","UPPER",NULL);	//EXECL的程序与主程序公用PCB,PCB里面有文件描述符表
					//UPPER里面的GETCHAR操作从文件描述符为0的文件读内容
	PERROR("EXEC ./UPPER");
	EXIT(2);
	RETURN 0;
}

chunli@ubuntu:~/linux_c$



僵尸进程 演示

在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他, 

那么他将变成一个僵尸进程。

子进程运行结束,内核PCB并没有被释放,等待回收

chunli@ubuntu:~/linux_c$ cat main.c 
#include 
#include 
#include 
#include 
int main(int num,char **args,char **env)
{
	pid_t  pid;
	pid = fork();
	if(pid > 0)
	{
		while(1)
			sleep(5);
	}
	return 0;
}

chunli@ubuntu:~/linux_c$ gcc main.c  && ./a.out



chunli     5908  0.0  0.0   4224   656 pts/1    S+   15:06   0:00 ./a.out
chunli     5909  0.0  0.0      0     0 pts/1    Z+   15:06   0:00 [a.out] 
chunli     5913  0.0  0.3  44432  3176 pts/21   R+   15:06   0:00 ps aux


wait()回收僵尸进程

chunli@ubuntu:~/linux_c$ cat main.c 
#include 
#include 
#include 
#include 
#include 
#include 
int main(int num,char **args,char **env)
{
	pid_t  pid;
	pid_t  pid_c;
	pid = fork();
	if(pid > 0)
	{
		while(1)
		{
			printf("I'm parent %d\n",getpid());
			pid_c = wait(NULL);	//阻塞函数,等待子进程死
			printf("wait for child %d\n",pid_c);
			sleep(5);
		}
	}
	else if(pid == 0)
	{
		printf("I'm child %d\n",getpid());
		sleep(2);
	}
	return 0;
}

chunli@ubuntu:~/linux_c$ gcc main.c  && ./a.out
I'm parent 6265
I'm child 6266
wait for child 6266
I'm parent 6265
wait for child -1
I'm parent 6265
wait for child -1
^C
chunli@ubuntu:~/linux_c$ 


chunli     6104  0.0  0.0   4356   744 pts/1    S+   16:03   0:00 ./a.out
chunli     6108  0.0  0.3  44432  3224 pts/21   R+   16:03   0:00 ps aux



证明:父子进程同组pid

chunli@ubuntu:~/linux_c$ cat main.c 
#include 
#include 
#include 
#include 
#include 
#include 
int main(int num,char **args,char **env)
{       
        int i = 4;
        pid_t  pid;
        pid_t  pid_c;
        while(i--)
        {       
                pid = fork();
                if(pid == 0)
                {
                        break;
                }
        }       
        if(pid > 0)
        {       
                printf("I'm parent %d\n",getpid());
                wait(NULL);
                sleep(30);
        }
        if(pid == 0)
        {
                printf("I'm child %d\n",getpid());
                sleep(3);
        }
        return 0;
}

编译运行:
chunli@ubuntu:~/linux_c$ gcc main.c  && ./a.out
I'm parent 6416
I'm child 6420
I'm child 6419
I'm child 6417
I'm child 6418

chunli@ubuntu:~/linux_c$ ps ajx 
  PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
  5242   6433   6433   5242 pts/1      6433 S+    1000   0:00 ./a.out
  6433   6434   6433   5242 pts/1      6433 S+    1000   0:00 ./a.out
  6433   6435   6433   5242 pts/1      6433 S+    1000   0:00 ./a.out
  6433   6436   6433   5242 pts/1      6433 S+    1000   0:00 ./a.out
  6433   6437   6433   5242 pts/1      6433 S+    1000   0:00 ./a.out
  5673   6438   6438   5673 pts/21     6438 R+    1000   0:00 ps ajx

  
可以看出,PGID都是一样
chunli@ubuntu:~/linux_c$ man 2 wait
< -1   meaning wait for any child process whose process group ID is equal to the absolute value of pid.
从这句话意思
chunli@ubuntu:~/linux_c$ kill -9 6433
chunli@ubuntu:~/linux_c$ ps ajx  | grep a.out
chunli@ubuntu:~/linux_c$ 
全部杀干净了

waitpid() 非阻塞等待子线程发生变化

chunli@ubuntu:~/linux_c$ cat main.c 
#include 
#include 
#include 
#include 
#include 
#include 
int main(int num,char **args,char **env)
{
	int i = 3;
	pid_t  pid;
	pid_t  pid_c;
	while(i--)
	{
		pid = fork();
		if(pid == 0)
		{
			break;
		}
	}
	if(pid > 0)
	{
		i = 0;
		while(1)
		{
			sleep(1);
			printf("I'm parent %d\n",getpid());
			pid_c = waitpid(0,NULL,WNOHANG);	//非阻塞,子进程状态发生改变也会有返回值
								//如果子进程没有发生变化返回0
								//如果子进程发生改变返回子进程的pid
			if(pid_c == -1)
			{
				continue;
			}
			else if(pid_c == 0)
			{
				printf("子进程的状态没有发生变化 %d \n",pid_c);	//如果子进程没有发生变化返回0
			}
			else if(pid_c > 0)
			{
				printf("子进程的状态发生变化 %d \n",pid_c);	//如果子进程没有发生变化返回0
			}
			//sleep(1);	//邪门呀,sleep放在这里一直刷屏
		}
	}
	if(pid == 0)
	{
		printf("I'm child %d\n",getpid());
		sleep(6);
	}
	return 0;
}
//	pid_t waitpid(pid_t pid, int *status, int options);
//	wait for process to change state
//	< -1   meaning wait for any child process whose process group ID is equal to the absolute value of pid.
//	-1     meaning wait for any child process.
//	0      meaning wait for any child process whose process group ID is equal to that of the calling process.
//	> 0    meaning wait for the child whose process ID is equal to the value of pid.
//	The value of options is an OR of zero or more of the following constants:

//	RETURN VALUE
//	waitpid(): on success, returns the process ID of the child whose state has changed; 
//	if WNOHANG was specified and one or more child(ren) specified by  pid  exist,
//	but have not yet changed state, then 0 is returned.  On error, -1 is returned.



chunli@ubuntu:~/linux_c$ gcc main.c  && ./a.out
I'm child 7004
I'm child 7006
I'm child 7005
I'm parent 7003
子进程的状态没有发生变化 0 
I'm parent 7003
子进程的状态没有发生变化 0 
I'm parent 7003
子进程的状态没有发生变化 0 
I'm parent 7003
子进程的状态没有发生变化 0 
I'm parent 7003
子进程的状态没有发生变化 0 
I'm parent 7003
子进程的状态发生变化 7004 
I'm parent 7003
子进程的状态发生变化 7005 
I'm parent 7003
子进程的状态发生变化 7006 
I'm parent 7003
I'm parent 7003
I'm parent 7003
I'm parent 7003
^C
chunli@ubuntu:~/linux_c$


孤儿进程演示【父进程已经结束,子进程还在运行】

chunli@ubuntu:~/linux_c$ cat main.c 
#include 
#include 
#include 
#include 
#include 
#include 
int main(int num,char **args,char **env)
{
	int i = 3;
	pid_t  pid;
	pid_t  pid_c;
	while(i--)
	{
		pid = fork();
		if(pid == 0)
		{
			break;
		}
	}
	if(pid > 0)
	{
		printf("我是父进程 %d\n",getpid());
		exit(1);
	}
	else if(pid == 0)
	{
		i = 100;
		while(i--)
		{
			sleep(1);
			printf("我是子进程 %d 我的父进程是%d\n",getpid(),getppid());
		}
	}
	else
	{
		perror("fork ");
		exit(1);
	}
	return 0;
}
chunli@ubuntu:~/linux_c$ 

编译运行:
chunli@ubuntu:~/linux_c$ gcc main.c -omain && ./main 
我是父进程 4757
chunli@ubuntu:~/linux_c$ 我是子进程 4760 我的父进程是1
我是子进程 4759 我的父进程是1
我是子进程 4758 我的父进程是1
我是子进程 4760 我的父进程是1
我是子进程 4759 我的父进程是1
我是子进程 4758 我的父进程是1
我是子进程 4760 我的父进程是1
我是子进程 4759 我的父进程是1

chunli@ubuntu:~$ ps ajx
  PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
 4803   4804   4804   4804 pts/9      4804 Ss+   1000   0:00 -bash
     1   4826   4825   4804 pts/9      4804 S     1000   0:00 ./a.out
     1   4827   4825   4804 pts/9      4804 S     1000   0:00 ./a.out
     1   4828   4825   4804 pts/9      4804 S     1000   0:00 ./a.out
  4699   4829   4829   4699 pts/10     4829 R+    1000   0:00 ps ajx

只有结束组pid这么干了  
chunli@ubuntu:~$ kill -9 -4825