Line disciplines
     A terminal file is used like any other file in the system in that it can
     be opened, read, and written to using standard system calls.  For each
     existing terminal file, there is a software processing module called a
     line discipline is associated with it.  The line discipline essentially
     glues the low level device driver code with the high level generic inter-
     face routines (such as read(2) and write(2)), and is responsible for
     implementing the semantics associated with the device.  When a terminal
     file is first opened by a program, the default line discipline called the
     termios line discipline is associated with the file.  This is the primary
     line discipline that is used in most cases and provides the semantics
     that users normally associate with a terminal.  When the termios line
     discipline is in effect, the terminal file behaves and is operated
     according to the rules described in termios(4).  Please refer to that man
     page for a full description of the terminal semantics.  The operations
     described here generally represent features common across all line
     disciplines, however some of these calls may not make sense in conjunc-
     tion with a line discipline other than termios, and some may not be sup-
     ported by the underlying hardware (or lack thereof, as in the case of

/* Initialize UART driver */
int init_uart(char *dev, struct uart_t *u, int send_break)
 struct termios ti;
 int  fd, i;

 fd = open(dev, O_RDWR | O_NOCTTY);//打开串口设备,其中标志



//但是不要以控制 tty 的模式,因为我们并不希望在发送 Ctrl-C

 if (fd < 0) {
  perror(“Can’t open serial port”);
  return -1;

 //drop fd’s data;
 tcflush(fd, TCIOFLUSH

 if (tcgetattr(fd, &ti) < 0) {
  perror(“Can’t get port settings”);
  return -1;


cfmakeraw sets the terminal attributes as follows://此函数设置串口终端的以下这些属性,
termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
termios_p->c_cflag &= ~(CSIZE|PARENB) ;
termios_p->c_cflag |=CS8;

 ti.c_cflag |= CLOCAL;//本地连接,无调制解调器控制
 if (u->flags & FLOW_CTL)
  ti.c_cflag |= CRTSCTS

//考 Serial-HOWTO 第七节)
  ti.c_cflag &= ~CRTSCTS;

 if (tcsetattr(fd, TCSANOW, &ti) < 0) {//启动新的串口设置
  perror(“Can’t set port settings”);
  return -1;

 /* Set initial baudrate */
 if (set_speed(fd, &ti, u->init_speed) < 0) {/
/设置串口的传输速率bps, 也可以使

//用 cfsetispeed 和 cfsetospeed 来设置

  perror(“Can’t set initial baud rate”);
  return -1;

 tcflush(fd, TCIOFLUSH);//清空数据线

 if (send_break)
  tcsendbreak(fd, 0);

//int tcsendbreak ( int fd, int duration );Sends a break for
//the given time.在串口线上发送0值,至少维持0.25秒。

//If duration is 0, it transmits zero-valued bits for at least 0.25 seconds, and

//not more than 0.5seconds.

 //where place register u’s init function;
 if (u->init && u->init(fd, u, &ti) < 0)



struct uart_t uart[] = {
 { “any”,      0×0000, 0×0000, HCI_UART_H4,   115200, 115200, FLOW_CTL, NULL },
 { “ericsson”, 0×0000, 0×0000, HCI_UART_H4,   57600,  115200, FLOW_CTL, ericsson },
 { “digi”,     0×0000, 0×0000, HCI_UART_H4,   9600,   115200, FLOW_CTL, digi },
 { “texas”,    0×0000, 0×0000, HCI_UART_H4,   115200, 115200, FLOW_CTL, texas},

 { “bcsp”,     0×0000, 0×0000, HCI_UART_BCSP, 115200, 115200, 0,        bcsp },//bcsp的init函数名为bcsp,定义在本文件中**;

  return -1;

 tcflush(fd, TCIOFLUSH);//清空数据线

 /* Set actual baudrate */
 if (set_speed(fd, &ti, u->speed) < 0) {
  perror(“Can’t set baud rate”);
  return -1;

 /* Set TTY to N_HCI line discipline */
 i = N_HCI;
 if (ioctl(fd, TIOCSETD, &i) < 0) {//

TIOCSETD int *ldisc//改变到 i 行规,即hci行规
Change to the new line discipline pointed to by ldisc. The available line disciplines are listed in 

/* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */

/* line disciplines */
#define N_TTY  0


#define N_HCI  15  /* Bluetooth HCI UART */

  perror(“Can’t set line discipline”);
  return -1;

 if (ioctl(fd, HCIUARTSETPROTO, u->proto) < 0) {

  perror(“Can’t set device”);
  return -1;

 return fd;



 /* 5 seconds should be enough for initialization */
 n = init_uart(dev, u, send_break);
 if (n < 0) {
  perror(“Can’t initialize device”);




while (1) sleep(999999999);







       hciattach < tty > < type | id > [ speed ] [ flow ]


       Hciattach is used to attach a serial UART to the Bluetooth stack as HCI
       transport interface.


       <tty>  This specifies the serial device to attach. A leading  /dev  can
              be omitted. Examples: /dev/ttyS1 ttyS2
       <type | id>
              The  type  or id of the Bluetooth device that is to be attached,
              i.e. vendor or other device specific identifier. Currently  sup-
              ported types are
              type   description

              any    Unspecified   HCI_UART   interface,  no  vendor  specific

                     Ericsson based modules

              digi   Digianswer based cards

              xircom Xircom PCMCIA cards: Credit Card Adapter  and  Real  Port

              csr    CSR  Casira  serial  adapter  or BrainBoxes serial dongle

              bboxes BrainBoxes PCMCIA card (BL620)

              swave  Silicon Wave kits

              bcsp   Serial adapters using CSR chips with BCSP serial protocol

       Supported IDs are (manufacturer id, product id)

              0×0105, 0×080a
                     Xircom  PCMCIA  cards:  Credit Card Adapter and Real Port

              0×0160, 0×0002
                     BrainBoxes PCMCIA card (BL620)

              The speed specifies the UART speed to use. Baudrates higher than
              115.200bps  require vendor specific initializations that are not
              implemented for all types of devices. In general  the  following

       Written by Maxim Krasnyansky <maxk@qualcomm.com>

       man page by Nils Faerber <nils@kernelconcepts.de>

BlueZ                             Jan 22 2002                     HCIATTACH(8






服务属性1 //对于任意一个sdp服务记录,ServiceClassIDList




编写Linux下的Daemon程序 作者: dagger 无崖阁 xyg.ods.org 
版本: 1.0 2003-04-08 初始版本 
一、引言 Daemon程序是一直运行的服务端程序,又称为守护进程。
二、Daemon程序简介 Daemon是长时间运行的进程,通常在系统启动后就运行,
的man page,或相关文档,我们就不在这里讨论了。 
四、一个Daemon程序的例子 编译运行环境为Redhat Linux 8.0。 
#include  #include  #include  #include  #include  
#include  #include  
int daemon_init(void) 
{ pid_t pid; 
if((pid = fork()) < 0) return(-1); 
else if(pid != 0) exit(0); /* parent exit */ 
/* child continues */ 
setsid(); /* become session leader */ 
chdir("/"); /* change working directory */ 
umask(0); /* clear file mode creation mask */ 
close(0); /* close stdin */ 
close(1); /* close stdout */ 
close(2); /* close stderr */ 
return(0); } 
void sig_term(int signo) 
{ if(signo == SIGTERM) 
/* catched signal sent by kill(1) command */ 
 { syslog(LOG_INFO, "program terminated."); 
 closelog(); exit(0); } 
int main(void) 
{ if(daemon_init() == -1) 
{ printf("can't fork self\n"); exit(0); } 
openlog("daemontest", LOG_PID, LOG_USER); 
syslog(LOG_INFO, "program started."); 
signal(SIGTERM, sig_term); /* arrange to catch the signal */ 
while(1) { sleep(1); /* put your main program here */ } 
return(0); } 
使用如下命令编译该程序: gcc -Wall -o daemontest daemontest.c 
使用ps axj命令可以显示系统中已运行的daemon程序的信息,包括进程ID、
session ID、控制终端等内容。 
1098 1101 1101 1074 pts/1 1101 S 0 0:00 -bash 1 1581 777 777 ? -1 S 500 0:13 gedit 1 1650 1650 1650 ? -1 S 500 0:00 ./daemontest 794 1654 1654 794 pts/0 1654 R 500 0:00 
ps axj 从中可以看到daemontest程序运行的进程号为1650。
我们再来看看/var/log/messages文件中的信息: Apr 7 22:00:32 localhost 
daemontest[1650]: program started. 
我们再使用kill 1650命令来杀死这个进程,
Apr 7 22:11:10 localhost daemontest[1650]: program terminated.
 使用ps axj命令检查,发现系统中daemontest进程已经没有了。
Advanced Programming in the UNIX Environment W.Richard Stevens 

void       g_main_loop_run        (GMainLoop    *loop)
 int open_max = sysconf(_SC_OPEN_MAX);
 struct pollfd *ufds = malloc(open_max * sizeof(struct pollfd));

 while (!loop->bail) {//外层主循环,本循环内没有函数改变loop的值,



 memset(&sa, 0, sizeof(sa));
 sa.sa_flags = SA_NOCLDSTOP;
 sa.sa_handler = sig_term;
 //sigaction change default action by system when the first para signal happened;
 sigaction(SIGTERM, &sa, NULL);
 sigaction(SIGINT,  &sa, NULL);


  int nfds, rc, i;
  struct watch *p, *w;

  nfds = 0;
  for (w = watch_head.next; w != NULL; w = w->next) {
   ufds[nfds].fd = w->channel->fd;
   ufds[nfds].events = w->condition;
   ufds[nfds].revents = 0;
  rc = poll(ufds, nfds, -1);

  if (rc < 0 && (errno == EINTR))

//poll’s man page http://jamesthornton.com/linux/man/poll.2.html

//EINTR A signal occurred before any requested event. 

  if (rc < 0) {
  p = &watch_head;
  w = watch_head.next;
  i = 0;
  while (w) {
   if (ufds[i].revents) {
    gboolean keep = w->func(w->channel, ufds[i].revents, w->user_data);
    if (!keep) {
      p->next = w->next;
      memset(w, 0, sizeof(*w));
      w = p->next;
   p = w;
   w = w->next;



Wait queues have several uses in the kernel, particularly for interrupt handling, process synchronization, and timing. Because these topics are discussed in later chapters, we’ll just say here that a process must often wait for some event to occur, such as for a disk operation to terminate, a system resource to be released, or a fixed interval of time to elapse.进程经常需要等待一些事件的发生,例如一个磁盘操作的结束,或者一个系统资源的释放等等。 Wait queues implement conditional waits on events: a process wishing to wait for a specific event places itself in the proper wait queue and relinquishes control.等待队列实现了事件上的条件等待:希望等待特定事件发生的进程将自己放在合适的队列并且放弃对cpu的控制。 Therefore, a wait queue represents a set of sleeping processes, which are woken up by the kernel when some condition becomes true. 因此,等待队列代表一个睡眠进程的集合,当条件为真时,由内核唤醒。

Wait queues are implemented as doubly linked lists whose elements include pointers to process descriptors. Each wait queue is identified by a wait queue head, a data structure of type wait_queue_head_t:

struct _ _wait_queue_head {
    spinlock_t lock;
    struct list_head task_list;
typedef struct _ _wait_queue_head wait_queue_head_t;

Since wait queues are modified by interrupt handlers as well as by major kernel functions, the doubly linked lists must be protected from concurrent accesses, which could induce unpredictable results (see Chapter 5). Synchronization is achieved by the lock spin lock in the wait queue head.

Elements of a wait queue list are of type wait_queue_t:

struct _ _wait_queue {
    unsigned int flags;
    struct task_struct * task;
    struct list_head task_list;
typedef struct _ _wait_queue wait_queue_t;

Each element in the wait queue list represents a sleeping process, which is waiting for some event to occur; its descriptor address is stored in the task field. However, it is not always convenient to wake up all sleeping processes in a wait queue.

For instance, if two or more processes are waiting for exclusive access to some resource to be released, it makes sense to wake up just one process in the wait queue. This process takes the resource, while the other processes continue to sleep. (This avoids a problem known as the “thundering herd,” with which multiple processes are awoken only to race for a resource that can be accessed by one of them, and the result is that remaining processes must once more be put back to sleep.)

Thus, there are two kinds of sleeping processes: exclusive processes (denoted by the value 1 in the flags field of the corresponding wait queue element) are selectively woken up by the kernel, while nonexclusive processes (denoted by the value 0 in flags) are always woken up by the kernel when the event occurs. A process waiting for a resource that can be granted to just one process at a time is a typical exclusive process. Processes waiting for an event like the termination of a disk operation are nonexclusive.

the wake_up_process( ) function is used to make a process runnable. It sets the process state to TASK_RUNNING and invokes add_to_runqueue( ) to insert the process in the runqueue list. It also forces the invocation of the scheduler when the process has a dynamic priority larger than that of the current process or, in SMP systems, that of a process currently executing on some other CPU

这个函数的用途:可以是一个进程可运行。它设置进程的状态为TASH_RUNNING,并且调用add_to_runqueue函数将进程插入runqueue链表。当这个进程的优先级大于现在正在cpu上执行的进程,就会调用scheduler进程调度者。 Doubly linked lists

The process list is a special doubly linked list. However, as you may have noticed, the Linux kernel uses hundreds of doubly linked lists that store the various kernel data structures.

For each list, a set of primitive operations must be implemented: initializing the list, inserting and deleting an element, scanning the list, and so on. It would be both a waste of programmers’ efforts and a waste of memory to replicate the primitive operations for each different list.

Therefore, the Linux kernel defines the list_head data structure, whose fields next and prev represent the forward and back pointers of a generic doubly linked list element, respectively. It is important to note, however, that the pointers in a list_head field store the addresses of other list_head fields rather than the addresses of the whole data structures in which the list_head structure is included (see Figure 3-4).

Figure 3-4. A doubly linked list built with list_head data structures


A new list is created by using the LIST_HEAD(list_name) macro. LIST_HEAD宏产生一个新的链表。It declares a new variable named list_name of type list_head, which is the conventional first element of the new list (much as init_task is the conventional first element of the process list).

Several functions and macros implement the primitives, including those shown in the following list.


Inserts an element pointed by n right after the specified element pointed by p (to insert n at the beginning of the list, set p to the address of the conventional first element)


Inserts an element pointed by n at the end of the list specified by the address h of its conventional first element


Deletes an element pointed by p (there is no need to specify the conventional first element of the list)


Checks if the list specified by the address of its conventional first element is empty


Returns the address of the data structure of type t in which the list_head field that has the name f and the address p is included



Scans the elements of the list specified by the address h of the conventional first element (similar to for_each_task for the process list)