2008年5月

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

linux-programming-note-4(设备文件)

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

Author:tunpishuang (tunpishuang at gmail dot com)

Chapter 6-------------设备文件(device file)

	|--字符文件,按字节访问,如串口
设备文件|
	|--块文件,一个读写任意字节数据,如文件系统
	|
	|--网络接口

终端设备和一般计算机区别:没有自己的CPU单元和内存单元

Linux系统中所有的终端设备被称为tty(teletypes or tele-typerwriters),是字符设备

	|-串口端口终端(/dev/ttySn)ttyS0到S3对应COM1到COM4
	|
	|-伪终端(/dev/pts/n) telnet实际登陆在伪终端上。
终端设备|
	|-控制终端(/dev/tty) 
	|
	|-控制台终端(/dev/ttyn,/dev/console) 指显示器,称为虚拟终端

终端控制:stty -a
重置终端:reset

ttyname() 获得当前终端名称
head	<unistd.h>
define	char *ttyname(int fd);
return	suc:终端名称	fail:NULL	Set errno:yes

fd:打开终端获得的文件描述符
eg:
#include <stdio.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
char* tty_out_name;
char* tty_in_name;
char* tty_err_name;

if((tty_out_name=ttyname(STDOUT_FILENO))==NULL)
	{perror("cannot get tty name\n"); return 1;}
if((tty_in_name=ttyname(STDIN_FILENO))==NULL)
	{perror("cannot get tty name\n"); return 1;}
if((tty_err_name=ttyname(STDERR_FILENO))==NULL)
	{perror("cannot get tty name\n"); return 1;}
printf("STDIN_FILENO ttyname is %s\n",tty_in_name);
printf("STDOUT_FILENO ttyname is %s\n",tty_out_name);
printf("STDERR_FILENO ttyname is %s \n",tty_err_name);
return 0;
}

------------------------------------
tcgetattr() 获取终端相关信息
tcsetattr() 设置终端参数
head	<termios.h>
	<unistd.h>
define	int tcgetattr(int fd,struct termios *termios_p);
	int tcsetattr(int fd,int optional_actions,const struct termios *termios_p);
return 	suc:o 		fail:-1 	set errno:yes

fd:终端文件描述符 

tcflag_t c_iflag;      /* 输入模式 */
tcflag_t c_oflag;      /* 输出模式 */
tcflag_t c_cflag;      /* 控制模式 */
tcflag_t c_lflag;      /* 本地模式 */
cc_t c_cc[NCCS];       /* 控制字符 */

c_iflag:
IGNBRK		BRKINT		IGNPAR		INPCK		ISTRIP
INLCR		IGNCR		ICRNL		IUCLC		IXON
IXANY		IXOFF		IMAXBEL

c_oflag:
OPOST		OLCUC		ONLCR		OCRNL		ONOCR
ONLRET		OFILL		OFDEL		NLDLY		CRDLY
TABDLY		BSDLY		VTDLY		FFDLY

c_cflag:
CBAUD		CBAUDEX		CSIZE		CSTOPB		CREAD
PARENB		PARODD		HUPCL		CLOCAL		CRTSCTS

c_lflag:
ISIG		ICANON		XCASE		ECHO		ECHOE
ECHONL		ECHOPRT		TOSTOP

c_cc[NCCS]
VINTR		VEOL		VQUIT		VTIME		VEOF
VERASE		VSTOP		VKILL		VSTART		VMIN

optional_actions 用于控制修改起作用的时间
TCSANOW		TCSADRAIN		TCSAFLUSH

termios_p 保存了要修改的参数

-----------------------------
密码输入关闭回显的两种方法

1:使用curses库
#include <stdio.h>
#include <stdlib.h>
#include <curses.h>
#include <unistd.h>
char passwd[20];
void init()
{
	initscr();
	cbreak();
	noecho();
	intrflush(stdscr,FALSE);
	keypad(stdscr,TRUE);
	refresh;
}

int getpasswd(char *passswd,int size)
{
	int c;
	int n=0;
	printf("input password\n");
	do
	{
		c=getchar();
		if(c!='\n')
		{
			echochar('*');
			passwd[n++]=c;
		}
	}while(c!='\n' && n <(size-1));
	passwd[n]='\0';
	return n;
}
int main()
{
	int n;
	init();
	n=getpasswd(passwd,sizeof(passwd));
	printf("\n your passwd is %s\n",passwd);
	printf("press any key to continue...\n");

	refresh();
	getchar();
	endwin();
	return 0;
}
----------------------------
2使用tcgetattr tcsetattr
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <errno.h>
#define  ECHOFLAGS (ECHO | ECHOE |ECHOK |ECHONL)
int set_disp_mode(int fd,int option)
{
	int err;
	struct termios term;
	if(tcgetattr(fd,&term)==-1) 
	{perror("cannot get the attribution of the terminal\n"); return 1;}
	if(option) term.c_lflag|=ECHOFLAGS;
	else term.c_lflag &=~ECHOFLAGS;
	err=tcsetattr(fd,TCSAFLUSH,&term);
	if(err==-1 && err==EINTR) 
	{perror("cannot set the attribution  of the terminal\n"); return 1;}
	return 0;
}

int getpasswd(char* passwd,int size)
{
	int c;
	int n=0;
	printf("lease input passwd:\n");
	do
	{
		c=getchar();
		if(c!='\n'|c!='\r') {passwd[n++]=c;}

	}while(c!='\n' && c!='\r' && n<(size-1));
	passwd[n]='\0';
	return n;
}
int main()
{
	char passwd[20];
	set_disp_mode(STDIN_FILENO,0);
	getpasswd(passwd,sizeof(passwd));
	printf("\nyour passwd is %s\n",passwd);
	printf("press any key to continue\n");
	set_disp_mode(STDIN_FILENO,1);
	getchar();
	return 0;
}
----------------------------------

	串口通信

串口再数据通信中,一次只传输一个bit的数据。
串口通信设备也被称为数据通讯设备(DCE,Data Communication Equipment)
或数据终端设备(DTE,Data Terminal Equipment)
串口一般用于ascii的传输,
打开串口fd=open("dev/ttyS0",O_RDWR|O_NOCTTY);

设置串口通信参数
波特率的设置
cfgetispeed() cfgetospeed()
head	<termios.h>
	<unistd.h>
define	speed_t cfgetispeed(const struct termios *termios_p);
	speed_t cfgetospeed(const struct termios *termios_p);
return 	suc:返回termios_p结构中的输入输出端口的波特率 fail:-1 	set errno:yes

cfsetispeed(),cfsetospeed()
head	<termios.h>
	<unistd.h>
define	int cfsetispeed(struct termios *termios_p,speed_t speed);
	int cfsetospeed(struct termios *termios_p,speed_t speed);
return	suc:返回termios_p结构中的输入、输出端口的波特率 fail:-1		set errno:yes

eg:
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
.............
struct termios opt;
tcgetattr(fd,&opt);
cfsetispeed(fd,B9600);
cfsetospeed(fd,B9600);
tcsetattr(fd,TCANOW,&opt);
..........

数据位的设置:
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
........
struct termios opt;
tcgetattr(fd,&opt);
opt.c_cflag&=~CSIZE;
opt.c_cflag|=CS8;
.........
tcsetattr(fd,TCANOW,&opt);
..............

奇偶校验位
设置		代码
无校验		opt.c_cflag&=~PARENB;
奇校验		opt.c_cflag|=(PARODD|PARENB);
偶校验		opt.c_cflag&=~PARENB;
		opt.c_cflag&=~PARODD;
空格		opt.c_cflag&=~PARENB;
		opt.c_cflag&=~CSTOPB;

数据流控制
不使用数据流控制		opt.c_cflag&=~CRTSCTS
硬件				opt.c_cflag|=CRTSCTS
软件				opt.c_cflag|=IXON|IXOFF|IXANY

串口读写实例
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
int open_port(char* port)
{
	int fd;
	if((fd=open(port,O_RDWR|O_NOCTTY|O_NONBLOCK))==-1) {perror("cannot open the expected port"); return 1;}
	return fd;
}
void close_port(int fd) {close(fd);}

int get_baud_rate(unsigned long int baud_rate)
{
	switch(baud_rate)
	{
		case 0	:	return B0;
		case 50	:	return B50;
		case 75 :	return B75;
		case 110:	return B110;
		case 134:	return B134;
		case 150:	return B150;
		case 200:	return B200;
		case 300:	return B300;
		case 600:	return B600;
		case 1200:	return B1200;
		case 1800:	return B1800;
		case 2400:	return B2400;
		case 4800:	return B4800;
		case 9600:	return B9600;
		case 19200:	return B19200;
		case 38400:	return B38400;
		case 57600:	return B57600;
		case 115200:	return B115200;
		case 230400:	return B230400;
		default:	return -1;

	}
}
typedef struct port_info
{
	int baud_rate;
	int port_fd;
	char parity;
	char stop_bit;
	char flow_ctrl;
	char data_bits;
}*pport_info;

int set_port(pport_info p_info)
{
	struct termios old_opt,new_opt;
	int baud_rate,parity;
	memset(&old_opt,0,sizeof(old_opt));
	memset(&new_opt,0,sizeof(new_opt));
	cfmakeraw(&new_opt);
	tcgetattr(p_info->port_fd,&old_opt);
	baud_rate=get_baud_rate(p_info->baud_rate);
	cfsetispeed(&new_opt,baud_rate);
	cfsetospeed(&new_opt,baud_rate);
	new_opt.c_cflag |=CLOCAL;
	new_opt.c_cflag |=CREAD;

	switch(p_info->flow_ctrl)
	{
		case '0': new_opt.c_cflag &=~CRTSCTS; break;
		case '1': new_opt.c_cflag |=CRTSCTS; break;
		case '2': new_opt.c_cflag |=IXON | IXOFF | IXANY; break;

	}
	new_opt.c_cflag &=~CSIZE;
	switch(p_info->data_bits)
	{
		case '5': new_opt.c_cflag |=CS5; break;
		case '6': new_opt.c_cflag |=CS6; break;
		case '7': new_opt.c_cflag |=CS7; break;
		case '8': new_opt.c_cflag |=CS8; break;
		default: new_opt.c_cflag |=CS8;
	}
	switch(p_info->parity)
	{
		case '0': new_opt.c_cflag &=~PARENB; break;
		case '1': new_opt.c_cflag |=PARENB; 
			  new_opt.c_cflag &=~PARODD; break;
		case '2': new_opt.c_cflag |=PARENB;
			  new_opt.c_cflag |=PARODD; break;
	}
	if(p_info->stop_bit=='2') new_opt.c_cflag |=CSTOPB;
	else new_opt.c_cflag &=~CSTOPB;
	new_opt.c_oflag &=~OPOST;
	new_opt.c_cc[VMIN]=1;
	new_opt.c_cc[VTIME]=1;
	tcflush(p_info->port_fd,TCIFLUSH);
	int result;
	result=tcsetattr(p_info->port_fd,TCSANOW,&new_opt);
	if(result==-1) {perror("cannot set the serial port parameters\n"); return -1;}
	tcgetattr(p_info->port_fd,&old_opt);
	return result;

}

int send_data(int fd,char *data,int data_len)
{
	int len;
	len=0;
	len=write(fd,data,data_len);
	if(len==data_len) return len;
	else {tcflush(fd,TCOFLUSH); return -1;}

}

int recv_data(int fd,char *data,int data_len) 
{
	int len ,fs_sel;
	fd_set fs_read;
	struct timeval time;
	FD_ZERO(&fs_read);
	FD_SET(fd,&fs_read);
	time.tv_sec=10;
	time.tv_usec=0;
	fs_sel=select(fd+1,&fs_read,NULL,NULL,&time);
	if(fs_sel) 
	{
		len=read(fd,data,data_len);
		return len;
	} else return -1;

int main(int argc,char* argv[])
{
	int port_fd;
	int len;
	char recv_buf[9];
	int i;
	if(argc!=3)
	{	
	printf("usage: %s /dev/ttySn 0(send data)/1(receive data)\n",argv[0]); return -1;	
	}
	port_fd=open_port(argv[1]);
	if(port_fd==-1)
	{
		printf("program exit \n");
		return -1;
	}
	struct port_info info;
	info.baud_rate=9600;
	info.data_bits=8;
	info.flow_ctrl=2;
	info.port_fd=port_fd;
	info.stop_bit=1;
	info.parity=0;
	if(set_port(&info)==-1)
	{
		printf("program exit \n");
		return -1;
	}
	if(strcmp(argv[2],"0")==0)
	{
		for(i=0;i<10;i++)
		{
			len=send_data(port_fd,"test data",9);
			if(len>0) printf("%d send data successfully\n",i);
			else printf("send data failed\n");
			sleep(2);
		}
		close_port(port_fd);
	}
	else
	{
	while(1) 
	{
		len=recv_data(port_fd,recv_buf,9);
		if(len>0) 
		{
			for(i=0;i<len;i++)
			printf("receive data is %s \n",recv_buf);
		}
		else printf("cannot receive data \n"); sleep(2);
	}
	} return 0;
}
}
--------------------
音频设备文件编程基础

	linux下的音频设备文件

/dev/console 	与扬声器有关
/dev/dsp 	与声卡设备上的数字信号处理器(DSP) 声卡设备通过DSP实现A/D转换,
		由于声卡驱动的原因,应用程序只能以只读只写的方式打开,某些例外。
/dev/audio	使用编码方式是mu-law。
/dev/mixer	声卡混音器软件接口,混音器的作用是将多个声音信号组合或叠加,
/dev/sequencer 设备文件用于提供对声卡中的波表合成器的支持。

ioctl() 对设备的特性进行控制,
head	<sys/ioctl.h>
define	int ioctl(int d,int request,...);
return 	suc:0	fail:-1		set errno:yes
d:文件描述符	request:对设备的控制命令

让扬声器发声
形式为:ioctl(fd,KIOCSOUND,(int)tone);
fd:打开扬声器获得的文件描述符,KIOCSOUND对扬声器音频进行控制,tone表示要输出的扬声器音频值。

usleep() 控制扬声器发生时间的长短
head	<unistd.h>
define	void usleep(unsigned long usec);
return	suc:0		fail:-2		set errno:yes
usec:毫秒

eg:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <linux/kd.h>
#define SPEAKER_DEVICE "/dev/console"
int main(int argc,char* argv[])
{
	int fd;
	int i;
	int freq;
	if(argc!=2) {printf("usage:%s frequence \n"),argv[0]; return 1;}
	freq=atoi(argv[1]);
	if(freq<=0 || freq>10000) {printf("the freq must be int range from 0 to 10000\n"); return 1;}
	fd=open(SPEAKER_DEVICE,O_WRONLY);
	if(fd==-1) {perror("cannot open the speaker device\n"); return 1;}

	for(i=0;i<10;i++)
	{
		int set_freq=1190000/freq;
		ioctl(fd,KIOCSOUND,set_freq);
		usleep(200);
		ioctl(fd,KIOCSOUND,0);
		usleep(100);
	}
	return 0;
}

-------------------------
linux下声卡编程
播放指定音频文件
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <linux/soundcard.h>
#define Audio_Device "/dev/dsp"
#define Sample_Size 16
#define Sample_Rate 8000
int play_sound(char *filename)
{
	struct stat stat_buf;
	unsigned char *buf=NULL;
	int handler,fd;
	int result;
	int arg,status;
	if((fd=open(filename,O_RDONLY))<0) return -1;
	if(fstat(fd,&stat_buf)) {close(fd); return -1;}
	if(!stat_buf.st_size) {close(fd); return -1;}
	buf=malloc(stat_buf.st_size);
	if(!buf) {close(fd); return -1;}
	if(read(fd,buf,stat_buf.st_size)<0) 
	{
		free(buf);
		close(fd);
		return -1;
	}
	handler=open(Audio_Device,O_WRONLY);
	if(handler==-1)  {perror("open Audio_Device fail\n"); return -1;}
	arg=Sample_Rate;
	status=ioctl(handler,SOUND_PCM_WRITE_RATE,&arg);
	if(status==-1) {perror("error from SOUND_PCM_WRITE_RATE ioctl\n"); return -1;}
	arg=Sample_Size;
	status=ioctl(handler,SOUND_PCM_WRITE_BITS,&arg);
	if(status==-1) {perror("error from SOUND_PCM_WRITE_BITS ioctl\n"); return -1;}
	result=write(handler,buf,stat_buf.st_size);
	if(result==-1) {perror("fail to play sound\n"); return -1;}
	free(buf);
	close(fd);
	close(handler);
	return result;
}
int main(int argc,char *argv[])
{
	if(argc!=2)
	{printf("usage:%s soundfilename\n",argv[1]); return -1;}
	play_sound(argv[1]);
	return 0;
}

-------------------------
录制音频文件
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <linux/soundcard.h>
#define	LENGTH 3
#define RATE 8000
#define SIZE 16
#define CHANNELS 2
unsigned char buf[LENGTH*RATE*SIZE*CHANNELS/8];
int main(void)
{
	int fd;
	int arg;
	int status;
	fd=open("/dev/dsp",O_RDWR);
	if(fd<0) {perror("cannot open /dev/dsp device\n"); return 1;}
	arg=SIZE;
	status=ioctl(fd,SOUND_PCM_WRITE_BITS,&arg);
	if(status==-1) {perror("cannot set SOUND_PCM_WRITE_BITS\n"); return 1;}

	arg=CHANNELS;
	status=ioctl(fd,SOUND_PCM_WRITE_CHANNELS,&arg);
	if(status==-1) {perror("cannot set SOUND_PCM_WRITE_CHANNELS\n"); return 1;}

	arg=RATE;
	status=ioctl(fd,SOUND_PCM_WRITE_RATE,&arg);
	if(status==-1) {perror("cannot set SOUND_PCM_WRITE_RATE\n"); return 1;}

	/*start to record section*/
	while(1)
	{
		printf("Recording...:\n");
		status=read(fd,buf,sizeof(buf));
		if(status!=sizeof(buf)) {perror("read wrong number of bytes\n");}

		printf("Play...:\n");
		status=write(fd,buf,sizeof(buf));
		if(status!=sizeof(buf)) perror("wrote wrong number of bytes\n");
		status=ioctl(fd,SOUND_PCM_SYNC,0);
		if(status==-1) perror("cannot set SOUND_PCM_SYNC");
	}
	return 0;
}
---------------------------------------------end