如何架设一个Ubuntu/Debian LAMP服务器

架设一个自己的Lamp服务器- 快捷,简单的DIY安装。需要用到的软件如下,不用介绍了,都再熟悉不过了。

  • Apache 2 - Linux Web server
  • MySQL 5 - MySQL Database Server
  • PHP4/5 - PHP Scripting Language
  • phpMyAdmin - Web-based database admin software.

注释: Linux + Apache + MySQL + PHP/Perl 加在一起就是广为所知的 LAMP 服务器

首先,让我们准备一个debian/ubuntu版本的linux,最少要求256MB的内存。.如果内存低于最小内存可能会导致许多的问题,因为mysql和webmin的正常运行需要很多的内存。如果内存小了,mysql将会给你一个"cannot connet to mysql.sock"的错误。我喜欢基于debian/ubuntu的linux版本那是因为我对apt-get命令的喜好。至少一个知道这个命令的初学者,安装软件包是如此的简单,你完全没有必要担心软件包的依赖性和配置问题。如果你想安装一个自己的服务器,最好还是去买一个独立服务器或者VPS。如果你只是想试验一下服务器和安装,那我推荐你买个VPS。我更喜欢vpslink的价格。即便你是linux或者VPS主机的新手,你也会觉得安装和配置自己的服务器是如此的简单,信不信由你!

如何你通过ssh管理你的服务器,先要下载个PuTTy 。输入服务器的ip用root登陆进去。也许你知道,webmin是一个免费的服务器控制面板,一旦安装好了lamp和mail服务器我们就来安装这个。webmin使配置linux更加容易。安装前,用这个命令升级必要的软件包:

apt-get install update

安装 Apache + PHP

Apache是在大多数基于linux服务器上面跑的一个著名web服务器。很少的命令就可以配置出可以跑php4/php5的apache。如果想安装php4,apt-get就可以了

apt-get install apache2 php4 libapache2-mod-php4

安装php5,运行下面的命令,注意如何不指定版本'4',php5会自动安装。

apt-get install apache2 php5 libapache2-mod-php5

Apache 的配置文件位于: /etc/apache2/apache2.conf 你的web文件夹在: /var/www.

为了检查php是否安装运行正常,创建一个带有phpinfo()函数的test.php文件放在/var/www文件夹。

nano /var/www/test.php

test.php内容:

<?php phpinfo(); ?>

让你的浏览器打开http://ip.address/test.php 或者http://domain/test.php,你将会看到所有的php配置和默认的设置。 你可以编译apache配置文件必要的参数来设置虚拟空间。

安装MySql数据库

如何你跑一个数据库驱动的商务网站,安装mysql总是必要的。记住运行Mysql至少需要256MB的内存,除非你不需要mysql。以下命令会安装mysql 5的服务器和客户端。

apt-get install mysql-server mysql-client php5-mysql

注意:如果你已经安装了php4,你应该像这样稍稍的改一下:

apt-get install mysql-server mysql-client php4-mysql

mysql的配置文件位于:/etc/mysql/my.cnf

创建mysql用户和改root密码

默认情况下mysql创建了一个没有密码的root用户,你需要改一下root的密码。改变root密码:

mysql -u root  
mysql> USE mysql;  
mysql> UPDATE user SET Password=PASSWORD('new-password') WHERE user='root';  
mysql> FLUSH PRIVILEGES;*

你需要创建一个用户在php脚本中连接mysql,而绝不能使用root连接。你可以有选择的使用webmin或者phpMyAdmin来进行用户的创建和权限的指派。webmin和phpmyadmin在基本安装完成后进行。

PhpMyAdmin 安装

phpMyAdmin是一个基于web的数据库管理软件,在apache下很容易安装和配置。没有什么比phpmyadmin管理数据库和表格更容易的了。你所需要做的就是:

apt-get install phpmyadmin

phpmyadmin配置文件在/etc/phpmyadmin目录。
在apache下安装phpmyadmin,你仅需要在/etc/apache2/apache2.conf中加入以下这行:

Include /etc/phpmyadmin/apache.conf

现在重新启动 Apache:

/etc/init.d/apache2 restart

用浏览器打开:http://domain/phpmyadmin。就这样完成了。mysql和phpmyadmin都准备就绪。用root登陆mysql创建用户,然后用php脚本连接。

linux-programming-note-8(POSIX IPC)

<本笔记基于《LINUX编程技术详解》(人民邮电出版社)>

Author:tunpishuang (tunpishuang at gmail dot com)

Chapter 11-------POSIX IPC
使用信号传递的信息有限
使用管道只能传输“无格式的字节流”?-->不理解
AT&T发行的system V 引入3种新的进程间通信(IPC)
分别是:消息队列,共享内存,信号量

linux-programming-note-7(-管道与命名管道(pipe and named pile))

<本笔记基于《LINUX编程技术详解》(人民邮电出版社)>

Author:tunpishuang (tunpishuang at gmail dot com)

Chapter 10-------管道与命名管道(pipe and name pile)
管道在某个时刻只能单项传输数据,成为半双工模式,(全双工是发送的同时可以接收数据)
图:
进程A______写入数据______->管道_____读取数据____->进程B

管道只能再父子和兄弟进程间创建,之外的进程可以使用命名管道和消息队列。
piplle()
header	<unistd.h>
synopsis	int pipe(int filedes[2]);
return	suc:0	fail:-1	errno:yes
fildes[0]用于读取管道数据,fildes[1]用于往管道写入数据
eg:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char* argv[])
{
	int f_des[2];
	int pid;
	char msg[BUFSIZ];
	if(argc!=2)
	{
		printf("usage:%s message\n",argv[0]);
		return 1;
	}
	if(pipe(f_des)==-1) {perror("cannot create the IPC pipe\n"); return 1;}
	pid=fork();
	if(pid==-1) {perror("cannot create new process\n"); return 1;}
	else if(pid==0)
	{
		close(f_des[1]);
		if(read(f_des[0],msg,BUFSIZ)==-1) 
		{
			perror("child process cannot read the data from pipe\n");
			return 1;
		}else printf("in child process, receive message: %s\n",msg);
		_exit(0);
	}else 	{
			close(f_des[0]);
			if(write(f_des[1],argv[1],strlen(argv[1]))==-1)
			{
				perror("parent process cannot wirte data to pipe\n");
				return 1;
			}else printf("int parent process ,send message %s\n",argv[1]);
			wait(NULL);
			_exit(0);
		}
	return 0;
}
-----------------------------------
使用管道实现进程间的双向通信
#include <stdio.h>
#include <unistd.h>
int main(int argc,char* argv[])
{
	int f_des[2];
	int pid;
	if(argc!=3) {printf("usage: %s cmd1 cmd2\n",argv[0]);	return 1;}
	if(pipe(f_des)==-1) {perror("cannto create the IPC pipe\n"); return 1;}
	pid=fork();
	if(pid==-1)
	{
		perror("fork failed\n");
		return 1;
	}else if(pid==0)
	{
		dup2(f_des[1],STDOUT_FILENO);
		close(f_des[0]);
		close(f_des[1]);
		if(execlp(argv[1],argv[1],NULL)==-1)
		{
			perror("in child process,cannot execute the command\n");
			return 1;
		}
		return 1;
	}else
	{
		dup2(f_des[0],STDIN_FILENO);
		close(f_des[0]);
		close(f_des[0]);
		if(execlp(argv[2],argv[2],NULL)==-1)
		{
			perror("in parent process,cannot execute the command\n");
			return 1;
		}
		return 1;
	}
	return 0;
}
----------------------------------------./pipe3 ls less测试
popen()创建管道,比pipe,fork,dup2,exec的联合使用简单
header		<stdio.h>
synopsis	FILE *poen(const char *command,const char *type);
return		suc:指向FILE的指针	fail:NULL	errno:Yes

pclose()	关闭popen获得执行FILE的指针
stream=popen(command,"r") 子进程STDOUT_FILENO数据写入父进程文件流stream
stream=popen(command,"w") 文件流stream中的数据写入子进程STDIN_FILENO
pclose()
header		<stdio.h>
synopsis	int pclose(FILE *stream);
return		suc:命令退出状态	fail:-1	errno:yes

eg:
#include <stdio.h>
#include <unistd.h>
int main()
{
	FILE* fs=popen("sort","w");
	if(fs=NULL) 
	{
		perror("popen failed\n");
		return 1;
	}
	fprintf(fs,"test1\n");
	fprintf(fs,"test2\n");
	fprintf(fs,"test3\n");
	pclose(fs);
	return 0;
}
---------------------------------------运行后segmentation fault段错误,?
shell管道重定向程序的再实现
stream=popen(argv[1],"r")-->stream-->stream=popen(argv[2],"w")

#include <stdio.h>
#include <unistd.h>
#define BUFFER_SIZE 300
int main(int argc,char* argv[])
{
	FILE* stream_in,stream_out;
	int nLen;
	char buffer[BUFFER_SIZE];
	if(argc!=3)
	{
		printf("usage: %s cmd1 cmd2\n",argv[0]);
		return 1;
	}
	stream_in=popen(argv[1],"r");
	if(stream_in==NULL)
	{
		perror("call popen function fail\n");
		return 1;
	}
	fflush(stream_out);
	nLen=read(fileno(stream_in),buffer,PIPE_BUF);
	while(nLen>0)
	{
		write(fileno(stream_out),buffer,nLen);
		nLen=read(fileno(stream_in),buffer,PIPE_BUF);
	}
	pclose(stream_in);
	pclose(stream_out);
	return 0;
}
-----------------------------没有编译成功,应该是某个头文件未包含,但不知道是哪个,
output:incompatible type for argument 1 of 'fileno' 'pclose' 'fflush'
PIPE_BUF也没有定义,晕死。

命名管道----非亲缘关系的进程间的通信。
命名管道,也被成为FIFO(first in,first out),特殊设备文件,通过open,close打开关闭,
写入的数据会加在末尾,从开始位置读取数据,写入FIFO设备并没有写入文件系统。

在shell中创建命名管道
1:使用mknod /mkfifo创建FIFO文件
mknod用于在文件系统上创建特殊特备文件 
mknod /tmp/fifo_nod p 和mkfifo /tmp/fifo_mkfifo功能一样
例如:
ls / >/tmp/fifo &  //把ls的输出转到/tmp/fifo管道文件
cat < /tmp/fifo    //把/tmp/fifo通过cat输出
ls一直阻塞直到有进程向管道读取数据,
--------------
mkfifo()
	mknod()容易在nfs上出问题,不推荐使用mkmod()
header		<sys/types.h>
		<sys/stat.h>
synopsis	int mkfifo(const char *pathname,mode_t mode);
return	 	suc:0		fail:-1	errno:yes
pathname:指定创建出的FIFO文件名称,
读取FIFO后,没有写入,阻塞
写入FIFO后,没有读取,阻塞
eg:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#define FIFO_CHANNEL "/tmp/fifo.channel"
int main(int argc,char* argv[])
{
	int fd,pid;
	char r_msg[BUFSIZ];
	char* s_msg="send message to other process";
	if(argc!=2)
	{
		printf("usage:%s option (read-2/write-1)\n",argv[0]);
		return 1;
	}
	if(atoi(argv[1])==1)
	{
		if(mkfifo(FIFO_CHANNEL,0777)==-1)
		{
			perror("cannot create FIFO channel\n");
			return 1;
		}
		fd=open(FIFO_CHANNEL,O_WRONLY);
		if(fd==-1)
		{
			perror("cannot open FIFO\n");
			return 1;
		}else printf("send message: %s \n",s_msg);
	}
	if(atoi(argv[1])==2)
	{
		fd=open(FIFO_CHANNEL,O_RDONLY);
		if(fd==-1)
		{
			perror("cannot open the FIFO\n");
			return 1;
		}
		if(read(fd,r_msg,BUFSIZ)==-1)
		{
			perror("process cannot read data from FIFO\n");
			return 1;
		}else printf("receive message from FIFO:%s\n",r_msg);
	}
	return 0;
}
----------------------------------END

linux-programming-note-6(基本进程间通信方法)

<本笔记基于《LINUX编程技术详解》(人民邮电出版社)>

Author:tunpishuang (tunpishuang at gmail dot com)

Chapter 9-------基本进程间通信方法

控制多线程下对某个非共享资源的访问,最简单的方法是使用临时文件作为访问标志。
首先进程判断临时文件是否存在,如果存在表明有进程占用了该资源。
进程A------\
	     \|文件存在?---------资源
进程B_______/

使用文件实现进程互斥程序实例
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int get_access_right(int inq_interval,int retry_times,char* file_name)
{
	int fd,count;
	count=0;
	fd=creat(file_name,0);
	while(fd==-1 && errno==EACCES) 
	{
		printf("pid:%ld process try to get access right...\n",(long)getpid());
		if(++count<retry_times)
			sleep(inq_interval);
		else	return -1;
		fd=creat(file_name,0);
	}
	close(fd);
	if(fd==-1) return 1;
	else return -1;
}
int release_right(char* file_name)
{
	int result;
	result=unlink(file_name);
	if(result==0) return 1;
	else return -1;
}
int main(int argc,char* argv[])
{
	int retry_times,inq_interval;
	char* file_name;
	int len,count=0;
	if(argc!=4) 
		{
		printf("usages: %s retry_times inquire_interval file_name\n",argv[0]);
		return 1;
		}
	inq_interval=atoi(argv[2]);
	if(inq_interval<=0) {printf("illegal retry times\n"); return 1;}
	len=strlen(argv[3]);
	if(len==0) {printf("illegal file name\n");	return 1;}
	if(get_access_right(inq_interval,retry_times,argv[3])==1) 
	{
		while(count<5)
		{
			printf("pid: %ld process is occupying the resource,circle %d\n",(long)getpid(),count);
			count++;
			sleep(inq_interval);
		}
		release_right(file_name);
		return 0;
	}else
	printf("pid: %ld process cannot access the resource .....retry %d times\n",(long)getpid(),retry_times);
	return 0;
}
--------------------------
使用文件作为访问资源的标志的缺陷:
1.限制了进程间信息量的传输
2.对存储器的读写的效率远远的低于内存和CPU
3.如果两个问题同时查询文件,执行结果unexpected(不可预测)
4.如果进程不稳定崩溃了而没有删除标志文件,其他进程永远可以不能访问到资源

Linux文件锁:修改文件属性来控制进程通信
fcntl()
head	<unistd.h>
	<fcntl.h>
define	int fcntl(int fd,int cmd);
	int fcntl(int fd,int cmd,long arg);
	int fcntl(int fd,int cmd,struct flock *lock);
return	suc:返回值依赖cmd参数	fail:-1	errno:yes
cmd:
F_DUPFD(复制文件描述符)		FD_CLOEXEC(文件描述符标志)	F_GETFL F_SETFL(文件状态标志)
F_GETLK / F_SETLK /F_SETLKW(非强制文件锁[advisory locking])
struct flock
{........
short l_type;  //锁类型 F_RDLCK . F_WRLCK. F_UNLCK
short l_whence;//l_start起始点 可以为SEEK_SET SEEK_CUR SEEK_END
off_t l_start; //锁的起始偏移量
off_t l_len;、、被锁定的大小
off_t l_pid;
...............	
}
fcntl调用结果
参数		return
F_DUPFD		新的fd
F_GETFD		fd标志
F_GETFL		文件状态
F_GETOWN	fd所有者
其他参数		0
失败		-1
eg:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc,char* argv[])
{
	int fd;
	int file_stat;
	if(argc!=2) {printf("usage:%s filename\n",argv[0]); return 1;}
	fd=open(argv[1],O_RDONLY);
	if(fd==-1) {perror("cannot open the file\n"); return 1;}
	file_stat=fcntl(fd,F_GETFL);
	if(file_stat<0) {perror("cannot get the file status\n"); return 1;}
	switch(file_stat & O_ACCMODE)
	{
		case O_RDONLY: printf("file:%s read only\n",argv[0]); break;
		case O_WRONLY: printf("file:%s write only \n",argv[0]); break;
		case O_RDWR:  printf("file:%s Read and write\n",argv[0]); break;
		default: printf("unkown access right\n");
	}
	return 0;
}
-----------------------------------
使用fcntl实现进程互斥
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc,char* argv[])
{
	int fd,count=0;
	if(argc!=2)
	{
		printf("usage:%s lock_file_name",argv[0]);
		return 1;
	}
	struct flock lock;
	fd=open(argv[1],O_RDWR);
	if(fd<0) {perror("cannot open the file\n"); return 1;}
	lock.l_type=F_WRLCK;
	lock.l_whence=0;
	lock.l_start=0;
	while(fcntl(fd,F_SETLK,&lock)<0)
	{
		switch(errno)
		{
			case EAGAIN:
			case EACCES:
				if(++count<5)
					sleep(5);
				else
				{
					fcntl(fd,F_GETLK,&lock);
					printf("pid:%ld process find pid:%ld lock the file %s\n",(long)getpid(),(long)lock.l_pid,argv[1]);
					return 1;
				}
				continue;
		}
		perror("function fcntl call fail\n");
		return 1;
	}
	printf("pid:%ld process lock the file\n",(long)getpid());
	printf("pid:%ld process release the file\n",(long)getpid());
	return 0;
}
--------------------------------------------
lockf()是fcntl()在文件锁方面的一个简单调用,添加,检测和解除文件锁。
head	<unistd.h>
define	int lockf(int fd,int cmd,off_t len);
return	suc:0	fail:-1	errno:yes

cmd:
F_LOCK		F_TLOCK		F_ULOCK		F_TEST

eg:
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc,char* argv[])
{
	int fd,lock_result;
	struct flock lock;
	if(argc!=2) {printf("usage:%s lock_file_name\n",argv[0]); return 1;}
	fd=open(argv[1],O_RDWR);
	if(fd<0) {perror("cannot open the file\n"); return 1;}
	lock_result=lockf(fd,F_LOCK,0);
	if(lock_result==-1) {perror("cannot get the lock\n"); return 0;}
	printf("pid:%ld process lock the file\n",(long)getpid());
	printf("pid:%ld process release the file\n",(long)getpid());
	return 0;

}
----------------------------
flock() 实现对文件的加锁解锁
head	<sys/file.h>
define	int flock(int fd,int operation);
return 	suc:0	fail:-1	errno:yes

operation:
LOCK_SH:设置共享锁,多个进程可同时对同一文件拥有共享锁
LOCK_EX:设置互斥锁
LOCK_UN:解除文件锁
锁定是不阻塞:LOCK_NB   eg:flock(fd,LOCk_EX|LOCK_NB)

---------------
信号:
查看Linux支持的信号:kill -l
man 7 signal

Man pages are grouped into sections. To see the full list of Linux man pages for a section, pick one of:

Section 1
    user commands (introduction) 
Section 2
    system calls (introduction) 
Section 3
    library functions (introduction) 
Section 4
    special files (introduction) 
Section 5
    file formats (introduction) 
Section 6
    games (introduction) 
Section 7
    conventions and miscellany (introduction) 
Section 8
    administration and privileged commands (introduction) 
Section L
    math library functions 
Section N
    tcl functions 

产生信号函数kill() raise() alarm()
Name
kill - send signal to a process
Synopsis

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);

Description
The kill() system call can be used to send any signal to any process group or process.

If pid is positive, then signal sig is sent to pid.

If pid equals 0, then sig is sent to every process in the process group of the current process.

If pid equals -1, then sig is sent to every process for which the calling process has permission to send signals, except for process 1 (init), but see below.

If pid is less than -1, then sig is sent to every process in the process group -pid.

If sig is 0, then no signal is sent, but error checking is still performed. 

Return Value
On success (at least one signal was sent), zero is returned. On error, -1 is returned, and errno is set appropriately.
Errors

---------------------
raise()用于给调用进程自身发送信号
head	<signal.h>
synopsis	int raise(int sig);
return	suc:0	fail:!0	errno:-   raise()等同于kill(getpid(),sig)

alarm()	定时产生SIGALRM信号
header	<unistd.h>
synopsis	unsigned int alarm(unsigned int seconds);
return	suc:不存在警告时钟返回0,存在,返回上一个警告时钟等待时间 	fail:-	errno:-

eg:
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
	int pid,wait_pid,status,i;
	for(i=1;i<=5;i++)
	{
		pid=fork();
		if(pid<0) {perror("cannot create the new process\n"); return 1;}
		else if(pid==0)
		{
			printf("in child process(pid:%ld),the process will terminate in %d seconds\n",(long)getpid(),i);
			alarm(i);
			pause();
		}
	}
	while((wait_pid=wait(&status)) && wait_pid!=-1)
	{
		if(WIFSIGNALED(status)) printf("process id:%d receive SIG:%d exit\n",pid,WTERMSIG(status));
		if(WIFEXITED(status)) printf("process id:%d exit code %d\n",pid,WEXITSTATUS(status));

	}
	return 0;
}
-----------------------------------
进程对信号有3种处理方法:1.系统处理 2.忽略信号 3.捕获信号
signal()
header	<signal.h>
synopsis	typedef void  (*sighandler_t)(int);
	sighandler_t signal(int signum,sighandler_t handler);
return	suc:原来的信号处理函数	fail:SIG_ERR	errno:yes
signum:要捕获的信号值,handler指向要调用的函数指针,可以为SIG_IGN(忽略信号)SIG_DFL(标准信号)也可以是自己的处理函数

eg:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
void sigusr1(int sig)
{
	printf("got the SIGUSR1 signal\n");
	exit(0);
}
int main(void)
{
	if(signal(SIGINT,SIG_IGN)==SIG_ERR) 
	{
		perror("cannot reset the SIGINT signal handler\n");
		return 1;
	}
	if(signal(SIGUSR1,sigusr1)==SIG_ERR)
	{
		perror("cannot reset the SIGUSR1 signal handler\n");
		return 1;
	}
	pause();
	return 0;
}
--------------------./signal后台运行 。对SIGINT忽略,对SIGUSR1信号输出信息,分别用kill -SIGINT  和-SIGUSR1测试结果 用jobs查看结果

sigaction() 检查,修改指定信号的处理动作
在处理小于SIGRTMIN的信号中  某些unix版本中采用函数处理后恢复成系统处理方式,所以每次处理信号后都要重新设置信号处理函数。
这个问题Linux不存在,为了代码的可移植性,采用sigaction()
header	<signal.h>
synopsis	int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact);
return	suc:0	fail:-1	errno:yes

signum为要捕获的信号,除了不能捕获的SIGKILL SIGSTOP 
act:指定动作指针,oldact信号原有处理函数
struct sigaction{
	void (*sa_handler)(int);
	void (*sa_sigaction)(int,siginfo_t *,void *);
	sigset_t	sa_mask;
	int sa_flags;
	void (*sa_restorer)(void);
	}
sa_mask:用于指定进行信号处理时要屏蔽的信号值,
sa_flags用于指定响应信号时的其他行为,取值如下:
SA_NOCLDSTOP	SA_NOCLDWAIT	SA_RESETHAND	SA_RESTART	SA_NODEFER	SA_SIGINFO

sa_handler用于指定信号的处理函数,SIG_DFL	SIG_IGN
siginfo_t{
	int si_signo;
	int si_errno;
	int si_code;
	int si_pid;
	pid_t si_pid;
	uid_t si_uid;
	int si_status;
	clock_t	si_utime;
	clock_t	si_stime;
	sigval_t	si_value;
	int	si_int;
	void * si_ptr;
	void * si_addr;
	int si_band;
	int si_fd;
	}
eg:
#include <stdio.h>
#include <signal.h>
void signal_set(struct sigaction *act)
{
	switch(act->sa_flags)
	{
		case (int)SIG_DFL:
			printf("using default handler\n");
			break;
		case (int)SIG_IGN:
			printf("ignore the signal\n");
			break;
		default: printf("%0x\n",act->sa_handler);
	}
}
int main(int argc,char* argv)
{
	int i;
	struct sigaction act,oldact;
	act.sa_handler=signal_set;
	act.sa_flags=SA_NODEFER | SA_RESETHAND;
	sigaction(SIGUSR1,&act,&oldact);
	for(i=1;i<12;i++)
	{
		printf("signal %d handler is :",i);
		sigaction(i,NULL,&oldact);
		signal_set(&oldact);
	}
	return 0;
}
---------------------------------

综合实例:实现对用户邮件的自动检测
/var/mail/username 判断该文件大小,
守候进程和信号处理模块,lib.base.c
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int daemon(int nochdir,int noclose)
{
	pid_t pid;
	pid=fork();
	if(pid<0) {perror("fork\n"); return -1;}
	if(pid!=0) exit(0);
	pid=setsid();
	if(pid<-1) {perror("setpid\n"); return -1;}
	if(!nochdir) chdir("/");
	if(!noclose) 
	{
		int fd;
		fd=open("/dev/null",O_RDWR,0);
		if(fd!=-1) 
		{
			dup2(fd,STDIN_FILENO);
			dup2(fd,STDOUT_FILENO);
			dup2(fd,STDERR_FILENO);
			if(fd>2) close(fd);
		}
	}
	umask(0027);
	return 0;
}
void * signal_set(int signo,void(*func)(int))
{
	int ret;
	struct sigaction sig,osig;
	sig.sa_handler=func;
	sigemptyset(&sig.sa_mask);
	sig.sa_flags=0;
	#ifdef SA_RESTART
	sig.sa_flags|=SA_RESTART;
	#endif
	ret=sigaction(signo,&sig,&osig);
	if(ret<0) return (SIG_ERR);
	else return (osig.sa_handler);
}
void sigint(int sig)
{
}
void sigtstp(int sig)
{}

void signal_init()
{
	signal_set(SIGINT,sigint);
	signal_set(SIGTSTP,sigtstp);
}
----------------------------
lib.mail.c
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <syslog.h>
#define SLEEP_TIME 5
long get_file_size(const char* filename)
{
	struct stat status;
	long size;
	if(stat(filename,&status)==-1) {perror("cannot get the file status\n"); return -1;}
	size=status.st_size;
	return size;
}
void mail_log(const char* progname,const char* mail_pos)
{
	fprintf(stderr,"%s notice: you have new mail in %s\n",progname,mail_pos);
}
int check_mail(const char *filename,const char* progname)
{
	long old_mail_size,mail_size;
	old_mail_size=get_file_size(filename);
	if(old_mail_size==-1) return 1;
	if(old_mail_size>0) mail_log(progname,filename);
	sleep(SLEEP_TIME);
	while(1)
	{
		mail_size=get_file_size(filename);
		if(mail_size!=old_mail_size) mail_log(progname,filename);
	}
	old_mail_size=mail_size;
	sleep(SLEEP_TIME);
}
-----------------------
mail.c
#include <stdio.h>
#define USER_ACCOUNT "/var/mail/tps"
	extern void signal_init();
	extern int daemon(int nochdir,int noclose);
	int check_mail(const char *filename,const char* progname);
	int main(int argc,char* argv[])
	{
		char *p;
		int opt,daemon_mode=1;
		char* progname;
		signal_init();
		if(daemon_mode) daemon(0,1);
		check_mail(USER_ACCOUNT,argv[0]);
		return 0;
	}
-----------------------------------------------------------------END

linux-programming-note-5(进程与进程环境(proccess)

<本笔记基于《LINUX编程技术详解》(人民邮电出版社)>

Author:tunpishuang (tunpishuang at gmail dot com)

Chapter 7-------------进程与进程环境(proccess)
	|---交互进程,(给出参数后才可运行)
进程分类|
	|---批处理进程(进程序列)
	|
	|---守候进程(执行特定功能或者执行系统相关任务的后台进程)

linux采用虚拟内存管理技术,32位系统支持寻址4GB的线性地址空间。
查看虚拟内存使用情况---vmstat

进程内存结构 分为:代码段,数据段(包括初始化和未初始化),堆栈段
每个进程创建的时候,系统会分配一个进程ID给该进程,如果ID达到系统最大值(<linux/threads.h>中定义的PID_MAX),系统将从新使用最小且当前未使用的PID

查看进程---ps -aux

getpid(),getppid() 获得进程ID和父进程ID
head	<sys/types.h>
	<unistd.h>
define	pid_t getpid(void);
	pid_t getppid(void);
return	suc: pid /ppid	fail:-	set errno:yes

eg:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
	printf("current process ID:%ld\n",(long)getpid());
	printf("curren parent proccess ID:%ld\n",getppid());
	return 0;
}
--------------------------ppid是-bash

用户表识和有效用户标示(UID and EUID)
getuid() / geteuid()
head	<sys/types.h>
	<unistd.h>
define	uid_t getuid(void);
	uid_t geteuid(void);
return	suc:uid/euid	fail:-	errno:-

uid表明进程的创建者,euid表明进程对于资源和文件的访问权限。
因为修改/etc/shadow文件才能更改用户密码,所以可以通过设置euid
让/usr/bin/passwd具有rws权限,这样普通用户就可以访问root的passwd

getgid()/getegid()
head	<sys/types.h>
	<unistd.h>
define	uid_t getgid(void);
	uid_t getegid(void);
return	suc:gid/egid	fail:- 	set errno:-

-------------------------------------------
fork()  创建进程
head	<sys/types.h>
	<unistd.h>
define	pid_t fork(void);
return	suc:放回两次,如果再父进程中,返回子进程号,如果在子进程中,返回0 	fail:-1 	errno:yes

fork创建的进程为当前进程的子进程,特点:一次调用两次返回。
eg:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
	pid_t pid;
	if(pid=fork()<0) {perror("failed to create the new proccess\n"); return 1;}
	else if(pid==0) {printf("in Child proccess\n"); return 0;}
	else printf("in Parent proccess\n"); return 0;
	return 0;
}
------------------------
vfork()和fork()全部一样,除了一点:vfork创建新的进程,父子进程共享虚拟内存空间
fork和vfork的区别:
fork创建的子进程会复制所有的父进程的所有资源,而vfork不会复制,父子进程将共享地址空间,子进程对虚拟内存空间的任何修改实际上是再修改父进程虚拟内存空间内容
在使用vfork创建子进程后,父进程会被阻塞,知道子进程调用了exec或_exit函数推出 ,vfork避免了资源复制的消耗。

/*test the diff between fork() and vfork()*/
/*change vfork into fork to see the diff*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int g_var=0;
int main(void)
{
	pid_t pid;
	int var=1;
	printf("process id:%ld\n",(long)getpid());
	printf("before execute the fork system call,g_var=%d var=%d\n",g_var,var);
	if(pid=vfork()<0) {perror("cannot create new proccess\n"); return 1;}
	else if(pid==0) 
	{
		g_var++;
		var++;
		printf("process id:%ld,g_var=%d var=%d\n",(long)getpid(),g_var,var);
		_exit(0);

	}
	printf("process id:%ld, g_var=%d var=%d\n",(long)getpid(),g_var,var);
	return 0;
}
----------------------------------
exec函数族 用于对创建新进程中的数据段、代码段和堆栈段进行替换。
进程调用exec后,代码段会被替换成新的代码段,同时获得新程序的数据段和堆栈段,
pid保持不变,
head 	<unistd.h>
	extern char **environ;
define	int execl(const char *path,const char *arg,...);
	int execlp(const char *file,const char *arg,...);
	int execle(const char *path,const char *arg,...,char * const envp[]);
	int execv(const char *path,char *const argv[]);
	int execve(const char *filename,char *const argv[],char *const envp[]);
	int execvp(const char *file,char *const argv[]);
return	suc:-		fail:-1		set errno:yes

l表示(list),参数可变
p表示自动搜索PATH路径
区别在于参数以及再查找可执行程序时是否使用系统路径。
eg:execlp函数使用可变参数,实现ls
#include <stdio.h>
#include <unistd.h>
int main(int argc,char* argv[])
{
	if(argc<2) {printf("usage: %s path\n",argv[0]);	return 1;}
	execlp("/bin/ls","ls",argv[1],(char*)NULL);
	return 0;

}
----------------(char*)NULL参数结束的标志
execvp支持参数列表,具有更大的灵活性

#include <stdio.h>
#include <unistd.h>
int main(int argc,char* argv[])
{
	if(argc<2) {printf("usage: %s arg list...\n",argv[0]);	return 1;}
	execvp(argv[1],&argv[1]);
	return 0;
}

-------------------------

exit() / _exit()  / return 的区别
exit()
head	<stdlib.h>
define	void exit(int status)
return	suc:-	fail:-	set errno:-
将返回(status &0377)给父进程,status为推出进程时的状态,父进程将获得该状态值,EXIT_SUCCESS和EXIT_FAILUR为正常结束和机场终止的两个宏。

_exit()
head	<unistd.h>
define	void _exit(int status);
return	suc:-	fail:-	errno:-

区别:
1:exit在ansi c中说明,_exit再Posix标准说明
2:_exit终止调用进程,但不关闭文件,不清除缓存,也不调用atexit注册的回调函数。

------------------
wait() 让父进程等待子进程结束
head	<sys/wait.h>
	<sys/types.h>
define	pid_t wait(int *status);
return	suc:结束的子进程pid和结束状态,fail:-1	errno:yes

wait系统调用将挂起当前进程,直到子进程结束。

kill -l查看linux系统中定义的64种信号类型。

int kill(pid_t pid,int sig);

#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
int main(int argc,char* argv[])
{
	pid_t pid;
	int exit_code;
	pid=getpid();
	srand((unsigned)pid);
	exit_code=(int)(rand()%256);
	sleep(10);
	if(atoi(*(argv+1))%2) 
	{	printf("the child process id:%d receive signal SIGKILL\n",pid); 
		kill(pid,9);
	}
	else
	{
		printf("the child process id:%d normally exit,exit_code is %0x\n",pid,exit_code);
		exit(exit_code);
	}	

}
--------------kill.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc,char* argv[])
{
	pid_t pid,wait_pid;
	int status;
	int i;
	if(argc<4) 
		{
		printf("usage: %s para1 para2 para3 \n",argv[0]);
		return 1;
		}
	for(i=1;i<4;i++)
		if((pid=fork())==0) execl("./kill","kill",argv[i],NULL);
		else printf("create the child process id %d \n",pid);
	while((wait_pid=wait(&status))&& wait_pid!=1)
		printf("process id:%d exit,exit_code is %0x\n",wait_pid,status);
	return 0;
}
----------------kill2.c

检测退出状态的宏
WIFEXITED(status)	WEXITSTATUS(status)	WIFSIGHALED(status)	WTERMSIG(status)
WCOREDUMP(status)	WIFSTOPPED(status)	WSTOPSIG(status)	WIFCONTINUED(status)

waipid() 根据进程号等待指定的进程
head	<sys/wait.h>
	<sys/types.h>
define	pid_t waitpid(pid_t pid,int *status,int options);
return	suc:返回状态和子进程的pid	fail:-1		errno:yes

option:
WNOHANG		WUNTRACED	WCONTINUED
pid:
<-1:等待子进程,子进程所属进程组id与进程号的绝对值相等。
-1:等待所有子进程
0:等待子进程,进程组id与调用进程的组Id相等。
>0:等待指定的进程。

eg:
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
	pid_t pid,wait_pid;
	int status;
	pid=fork();
	if(pid==-1)
		{
		perror("cannot create new process\n");
		exit(1);
		}
	else if(pid==0)
		{
		printf("child process id: %ld\n",(long)getpid());
		pause();
		_exit(0);
		}
	else
		{
			do
			{
				wait_pid=waitpid(pid,&status,WUNTRACED | WCONTINUED);
				if(wait_pid==-1) {perror("cannot using waitpid function\n"); exit(1);}
				if(WIFEXITED(status)) { printf("child process exites,status=%d\n",WEXITSTATUS(status));}
				if(WIFSIGNALED(status)) {printf("child process is killed by signal %d\n",WTERMSIG(status));}
				if(WIFSTOPPED(status)) {printf("child process is stopped by signal %d\n",WSTOPSIG(status));}
				if(WIFCONTINUED(status)) printf("child process resume running ...\n");

			}while(!WIFEXITED(status) && !WIFSIGNALED(status));
		exit(0);
		}
}
------------------用&后台运行,然后kil -sig process

僵死进程(zombie process)
子进程退出后父进程没有用wait来收集子进程的状态而产生,占用进程表。
eg:
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
	pid_t pid;
	pid=fork();
	if(pid<0) {perror("cannot create new process \n");	exit(1);}
	if(pid>0) sleep(60);
	else exit(0);
	return 0;
}
-----------------------------------END

Chapter 8-------------守候进程(daemon)

日志系统是记录daemon信息的手段

实现daemon的步骤
1:让init成为新产生进程的父进程

2:调用setsid函数
作用:使得新创建的进程脱离控制终端,同时创建新的进程组,并成为该进程组的首进程
setsid()
head	<unistd.h>
define	pid_t setsid(void);
return	suc:调用进程的会话id	fail:-1	set errno:yes

3:更改当前工作目录
chdir() daemon一般会将其工作目录更改到根目录下。

4:关闭文件描述符,并重定向到stdin/stdout/stderr
daemon不应该输出任何信息,定向到空设备/dev/null
.......
int fd;
fd=open("/dev/null",O_RDWR,0);
if(fd!=1)
{
	dup2(fd,STDIN_FILENO);
	dup2(fd,STDOUT_FILENO);
	dup2(fd,STDERR_FILENO);
	if(fd>2)
	  close(fd);
}
...............
5:设置daemon的文件权限创建掩码
daemon会创建临时文件,给其分配合理的权限。

daemon的具体实现:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int daemon(int nochdir,int noclose)
{
	pid_t pid;
	pid=fork();
	if(pid<0) {perror("fork\n"); 	return -1;}
	if(pid!=0) exit(0);
	pid=setsid();
	if(pid<-1) {perror("setsid\n");	return -1;}
	if(!nochdir) 	chdir("/");
	if(!noclose) 
	{
		int fd;
		fd=open("/dev/null",O_RDWR,0);
		if(fd!=-1) 
		{
			dup2(fd,STDIN_FILENO);
			dup2(fd,STDOUT_FILENO);
			dup2(fd,STDERR_FILENO);
			if(fd>2) close(fd);
		}
	}
	umask(0027);
	return 0;
}
int main(void)
{
	daemon(0,0);
	sleep(1000);
	return 0;
}
-------------------------------------------------------
daemon的日志实现
syslogd通过接受其他daemon的信息,并将这些信息记录在指定位置来解决日志记录问题。
configuration file :/etc/syslog.conf
man 5 syslog.conf
facility	value 			level	value
kern	0			emerg	0
user 	1			alert	1
mail	2			crit	2
daemon 	3			err	3
auth	4			warning	4
syslog	5			notice	5
lpr	6			info	6
news	7			debug	7
uucp	8
cron 	9
authpriv 	10
ftp 	11
系统保留	12-15
local0-local7	16-23

openlog() 打开系统日志连接 /   syslog()   /closelog() 
head	<syslog.h>
define	void openlog(const char *ident,int option,int facility);
	void syslog(int priority,const char *format,...);
	void closelog(void);
return	suc:-	fail:-	errno:-
ident:信息来源,option:控制标志,priority:消息的级别,format为消息格式输出与printf类似。
option:
LOG_CONS	LOG_NDELAY	LOG_NOWAIT	LOG_ODELAY
LOG_PERROR	LOG_PID	
---------------------------------
openlog函数参数facility值		syslog.conf对应的facility取值
LOG_KERN			kern
LOG_USER			user
LOG_MAIL			mail
LOG_DAEMON			daemon
LOG_AUTH			auth
LOG_SYSLOG			syslog
LOG_LPR				lpr
LOG_NEWS			news
LOG_UUCP			uucp
LOG_CRON			cron
LOG_AUTHPRIV			authpriv
LOG_FTP				ftp
LOG_LOCAL0~LOG_LOCAL7		local0~local7
-----------------------------------------
priority:
LOG_EMERG	emerg
LOG_ALERT	alert
LOG_CRIT	crit
LOG_ERR		err
LOG_WARNING	warning
LOG_NOTICE 	notice
LOG_INFO	info
LOG_DEBUG 	debug
-----------------------------------------------------
添加日志的流程
openlog()-->syslog()-->closelog()
#include <stdio.h>
#include <syslog.h>
int main(int argc,char* argv[])
{
	openlog(argv[0],LOG_CONS|LOG_PID,LOG_USER);
	int count=0;
	while(count<5) 
	{
		syslog(LOG_INFO,"%d , log info test...\n",count);
		count++;
	}
	closelog();
	return 0;
}
--------------------------------------END